Python-Skript liefert doppelte Daten
McNerl
- sonstiges
Hallo,
Ich habe in Python folgendes Skript geschrieben:
---
#!/usr/bin/python -O
import time
today = time.mktime( time.strptime( '2007-05-20', '%Y-%m-%d' ) )
string = ''
for i in range( 0, 1001 ) :
date = time.strftime( '%Y-%m-%d', time.localtime ( today ) )
string += 'INSERT INTO cndDate (date) VALUES ( '%s' );\n'%(date)
today += 86400
theFile = open( 'date.sql' , 'w' )
theFile.write( string )
theFile.close()
---
Wie Ihr evtl. sehen könnt liefert mir dies eine SQL Datei mit 1000 Daten.
Warum aber war der 2007-10-28, 2008-10-26, und 2009-10-25 doppelt vorhanden?! Ich springe doch immer einen Tag weiter ( 86400 Sekunden ).
Hat einer eine Erklärung?!
..danke
Hallo,
Wie Ihr evtl. sehen könnt liefert mir dies eine SQL Datei mit 1000 Daten.
Warum aber war der 2007-10-28, 2008-10-26, und 2009-10-25 doppelt vorhanden?! Ich springe doch immer einen Tag weiter ( 86400 Sekunden ).Hat einer eine Erklärung?!
Das ist wieder mal eine typische Sommer/Winterzeit+Zeitzonen-Geschichte [1]. Der Hintergrund ist folgender:
Wir befinden uns in einer Zeitzone, bei der im Sommer eine andere Zeit gilt, als im Winter, die Differenz beträgt genau 1 Stunde. Unsere sogenannte Standardzeit (das ist die Winterzeit) besitzt einen Offset von genau 1 Stunde von UTC (Universalzeit), d.h. wir sind UTC+01:00 im Winter. Im Sommer gibt es eine *zusätzliche* Korrektur um eine Stunde, d.h. im Sommer sind wir UTC+02:00. Wenn man im Winter also eine Uhrzeit in UTC umrechnen will, muss man eine Stunde abziehen, im Sommer zwei, umgekehrt muss man von UTC nach Lokalzeit im Winter eine Stunde addieren und im Sommer zwei.
Die Umstellung von Sommer- auf Winterzeit erfolgt nun genau am letzten Sonntag im Oktober, also genau den Daten, die Du gepostet hast (2007-10-28, 2008-10-26, 2009-10-25). Was passiert dort? Bei der Umstellung von Sommer- auf Winterzeit wird die Stunde 02:00 wiederholt, d.h. die Uhrzeit springt von 02:59:59 auf 02:00:00 - laut Gesetz ist die erste Stunde als "A" und die zweite Stunde als "B" zu bezeichnen, d.h. wir werden am Sonntag, den 28. Oktober 2007 folgenden Zeitablauf haben:
...
01:59:59
02:00:00A
02:00:01A
...
02:59:59A
02:00:00B
02:00:01B
...
02:59:59B
03:00:00
03:00:01
...
UTC selbst kennt aber keine Sommer- und Winterzeit, die Universalzeit ist *immer* kontinuierlich. Macht man aus der obigen Liste nun eine Tabelle, in der auch Universalzeit (inklusive Offset) eingetragen werden, erhält man folgendes:
Lokalzeit (CET/CEST) | Universalzeit (UTC) | UTC-Offset
---------------------+---------------------+-----------
... | ... | ...
2007-10-28 01:59:59 | 2007-10-27 23:59:59 | +02:00
2007-10-28 02:00:00A | 2007-10-28 00:00:00 | +02:00
2007-10-28 02:00:01A | 2007-10-28 00:00:01 | +02:00
... | ... | ...
2007-10-28 02:59:59A | 2007-10-28 00:59:59 | +02:00
2007-10-28 02:00:00B | 2007-10-28 01:00:00 | +01:00
2007-10-28 02:00:01B | 2007-10-28 01:00:01 | +01:00
... | ... | ...
2007-10-28 02:59:59B | 2007-10-28 01:59:59 | +01:00
2007-10-28 03:00:00 | 2007-10-28 02:00:00 | +01:00
2007-10-28 03:00:01 | 2007-10-28 02:00:01 | +01:00
... | ... | ...
Sprich: Zwischen 2007-10-28 00:00:00 CEST und 2007-10-29 00:00:00 CET liegen keine 86400, sondern ganze 90000 Sekunden, d.h. eine Stunde mehr.
Deine Funktion geht nun vom Datum 2007-05-20 aus - das ist bereits Sommerzeit. Wenn Du strptime() nutzt, dann wird die Uhrzeit 00:00:00 in Lokalzeit angenommen.
Nun solltest Du wissen, dass Timestamps genauso wie UTC kontinuierlich sind, man könnte sogar fast sagen, dass Timestamps "in UTC" sind, da sie als die Anzahl der Sekunden (ohne Schaltsekunden) seit dem 1. Januar 1970 00:00:00 UTC definiert sind. Das heißt, wenn Du 86400 Sekunden auf einen Timestamp addierst, dann erhälst Du den Zeitpunkt genau 86400 Sekunden später - ob das Datumsäßig einen Tag später ist, ist vollkommen irrelevant.
Jetzt passiert folgendes in Deinem Script: Du kommst zum Zeitpunkt 2007-10-28T00:00:00 CEST. Als Timestamp ist das 1193522400. Addierst Du 86400 Sekunden, erhälst Du den Timestamp 1193608800. Der ist in Lokalzeit aber 2007-10-28T23:00:00 CET. Damit erhälst Du die Dopplung. Alle weiteren Daten sind erst einmal wieder kontinuierlich verteilt, denn wenn Du wieder 86400 Sekunden addierst, erhälst Du als nächstes 2007-10-29T23:00:00 CET etc.
Allerdings tritt noch ein Fehler auf, der Dir vmtl. noch gar nicht aufgefallen ist: Die Daten der Rückumstellung treten GAR NICHT auf! Rückumstellung im Jahr 2008 wäre am 30. März, wenn Du immer wieder 86400 addierst, dann erhälst Du irgendwann das Datum 2008-03-29T23:00:00 CET, das hat den Timestamp 1206828000. Wenn Du nun 86400 addierst, erhälst Du dann den Timestamp 1206914400 - der ist jedoch 2008-03-31 00:00:00 CEST. Sprich: Das Datum 2008-03-30 taucht auch nicht in Deiner Liste auf - schau mal nach!
So, wie wird man das Problem nun los? Es gibt da eigentlich 3 einfache Möglichkeiten:
1) Eine Uhrzeit irgendwann in der Mitte des Tages verwenden, z.B.
today = time.mktime( time.strptime( '2007-05-20 12:00:00', '%Y-%m-%d %H:%M:%S' ) )
Die Differenzen führen dann lediglich dazu, dass die Uhrzeit eventuell
mal 11:00:00 statt 12:00:00 wird oder eben 13:00:00, falls das Anfangs-
datum mal im Winter liegt. Dafür ist der Datumsanteil immer korrekt.
2) Immer in UTC rechnen, das kennt keine Sommer-/Winterzeit, weil's
kontinuierlich ist. Statt localtime() halt gmtime() nutzen und statt
mktime() halt calendar.timegm() (anderes Modul! Warum das nicht in time
ist weiß ich nicht).
3) Du nutzt gleich richtige Kalenderberechnungsfunktionen (zum Beispiel
liefert Dir calendar.monthrange(jahr,monat)[1] die Anzahl an Tage im
Monat), um Dir die Daten zu generieren und schlägst Dich nicht mit
Timestamps rum.
Viele Grüße,
Christian
[1] Für diejenigen, die länger hier mitlesen: Mein Lieblinsthema. ;-)