Datums-String in Timestamp umwandeln

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Hallo,

ich versuche einen Datums-String in einen Timestamp umzuwandeln. Der Übersichtlichkeit halber wandle ich den Timestamp gleich wieder in ein für Menschen lesbares Format.

Unter PHP mache ich das z.B. so:

Code: Alles auswählen

echo strftime("%c", mktime (22, 05, 44, 02, 29, 2012));
echo strftime("%c", mktime (22, 05, 44, 03, 29, 2012));
Wed Feb 29 22:05:44 2012
Thu Mar 29 22:05:44 2012
Unter Python habe ich das so versucht:

Code: Alles auswählen

print time.strftime('%c', time.localtime(time.mktime((2012,02,29,22,05,44,0,0,0))))
print time.strftime('%c', time.localtime(time.mktime((2012,03,29,22,05,44,0,0,0))))
02/29/12 22:05:44
03/29/12 23:05:44
Die zweite Zeile ist falsch. Erst so ist die Berechnung korrekt:

Code: Alles auswählen

print time.strftime('%c', time.localtime(time.mktime((2012,03,29,22,05,44,0,0,1))))
Also im letzten Element eine 1 setzen.

Erste Frage, wo bekomme ich die 1 her? Zweite Frage, wofür ist dieses Verhalten sinnvoll?

Als work-o-round nutze ich:

Code: Alles auswählen

print time.strftime('%c', time.gmtime(time.mktime((2012,02,29,22,05,44,0,0,0))+3600))
print time.strftime('%c', time.gmtime(time.mktime((2012,03,29,22,05,44,0,0,0))+3600))
02/29/12 22:05:44
03/29/12 22:05:44

Richtig glücklich bin ich damit nicht. Weil lediglich die für Menschen lesbare Version korrigiert wird, derTimestamp selbst bleibt falsch!

Gruß Marcia
Zuletzt geändert von marcia am Samstag 31. März 2012, 09:36, insgesamt 1-mal geändert.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

marcia hat geschrieben:Erste Frage, wo bekomme ich die 1 her? Zweite Frage, wofür ist dieses Verhalten sinnvoll?
Die eins bzw. die null steht für „Sommerzeit“ oder „Winterzeit“ und weil am 29. März 2012 Sommerzeit ist, muss da eine eins gesetzt sein. Eigentlich setzt man Daten auch anders zusammen.

Es gibt übrigens eine Extrafunktion für dieses Datumsformat und ich würde dir eigentlich nicht dazu raten, Zahlen eine null voranzustellen, da du dann den Wert der Zahl im oktalen Zahlensystem erhältst:

Code: Alles auswählen

In [12]: time.asctime((2012, 02, 29, 22, 05, 44, 0, 0, 0))
Out[12]: 'Mon Feb 29 22:05:44 2012'

In [17]: from datetime import datetime

In [18]: datetime(2012, 3, 29, 22, 5, 44).ctime()
Out[18]: 'Wed Feb 29 22:05:44 2012'
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Hallo,
nomnom hat geschrieben:Die eins bzw. die null steht für „Sommerzeit“ oder „Winterzeit“ und weil am 29. März 2012 Sommerzeit ist, muss da eine eins gesetzt sein.
Danke für den Tip. So sollte es nun funktionieren:

Code: Alles auswählen

time.mktime((int(dat[0][6:10]),int(dat[0][3:5]),int(dat[0][0:2]),int(dat[1][0:2]),int(dat[1][3:5]),int(dat[1][6:8]),0,0,time.timezone))
nomnom hat geschrieben: Eigentlich setzt man Daten auch anders zusammen.
Ja? Wie denn? Basis ist "29.03.2012 23:42:02", ist leider eine Vorgabe. Zur weiteren Verarbeitung benötige ich einen Timestamp.
nomnom hat geschrieben: Es gibt übrigens eine Extrafunktion für dieses Datumsformat und ich würde dir eigentlich nicht dazu raten, Zahlen eine null voranzustellen, da du dann den Wert der Zahl im oktalen Zahlensystem erhältst:
Danke für den Hinweis mit der Vornull. Wie du oben sehen kannst muß ich sowieso alle substrings in Ints umwandeln. ;-)

Was bleibt ist die Frage, wofür ist denn die Angabe von time.timezone als letztes Element in der Liste für mktime sinnvoll? Außer das dann auch die Uhrzeit während der Sommerzeit korrekt ausgegeben wird. Denn das macht PHP ja auch ohne diese extra Angabe richtig.

Gruß Marcia
BlackJack

@marcia: Zum Parsen eines Datums (mit oder ohne Uhrzeitangaben) hätte ich ja eher `strptime()` erwartet.

Du benutzt den Begriff „Timestamp” als wenn das etwas eindeutig definiertes wäre. Also vom Format her. Das ist es aber nicht.

PHP kümmert sich anscheinend nicht um Sommer-/Winterzeit. Ob man da nun sagen kann es macht es *richtig*, ist wohl Ansichtssache. In vielen in PHP geschriebenen Webanwendungen, zum Beispiel in Foren muss man deshalb bei der Umstellung immer in den eigenen Profileinstellungen die Zeit von Hand umstellen, damit die Zeit bei den Beiträgen richtig angezeigt wird.
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Hallo BlackJack,

in der Tat habe ich es mit strptime versucht, bin aber gescheitert. Könntest Du mir ein Beispiel aufzeigen?

Das mit dem Timestamp hast du ganz richtig interpretiert. In der Tat halte ich nichts von der Sommerzeit. Die verstrichenen Sekunden seit dem 1. Januar 1970 00.00 Uhr interessieren sich meiner Meinung nach ebenfalls nicht für Spielchen mit der Uhrzeit. Die im Übrigen lediglich Energie verschwenden (im wahrsten Sinne des Wortes). Erst eine Umwandlung in ein für uns Menschen lesbares Format sollte die Sommerzeit berücksichtigen.

Allerdings beantwortet mir das immer noch nicht, warum "immer" ein time.timezone als letztes Element in der Liste für mktime eingetragen werden muß. Denn dann stimmen die errechneten Timestamps in PHP und Python überein. ;-)
Oder verstehe ich da was nicht richtig?

Gruß Marcia
BlackJack

@marcia: Bastel Dir doch einfach eine Umwandlung von Unix-Timestamp in eine Zeichenkette mit `strftime()` die so aussieht, wie das was Du parsen möchtest. Dann hast Du die Formatzeichenkette, die Du für `strptime()` verwenden kannst.

Die verstrichenen Sekunden seit dem 1.1.1970 interessieren sich nicht für „Spielchen” mit der Uhrzeit, aber wenn Du eine Zeitangabe hast, *die* sich dafür interessiert, oder in eine Zeitangabe umwandelst, die sich dafür interessiert, dann musst Du eben auch alle Rahmenbedingungen berücksichtigen. Sowohl `time.mktime()` als auch der "%c"-Formatierungscode berücksichtigen die Locale, also muss man sich da auch mit Zeitzonen und Sommerzeit herum schlagen, wenn es so etwas in der verwendeten locale gibt. Ich denke mal der Darstellung von "%c" nach passen bei Dir die eingestellte locale und die Datumsangaben die Du parsen möchtest, nicht zusammen!? Wo kommen die überhaupt her und auf welche Zeitzone beziehen die sich? Wenn man Zeiten aufzeichnet, sollte man das möglichst in UTC machen, damit die vergleichbar sind, egal in welcher Zeitzone/Locale sie erstellt wurden.

Die Umkehrfunktion zu `mktime()` ist `localtime()` und die liefert ja auch dieses Flag:

Code: Alles auswählen

In [220]: time.localtime()
Out[220]: time.struct_time(tm_year=2012, tm_mon=3, tm_mday=30, tm_hour=8, tm_min=16, tm_sec=24, tm_wday=4, tm_yday=90, tm_isdst=1)
Man *braucht* dieses Flag wenn man wissen will wie viele Sekunden seit dem 1.1.1970 vergangen sind, denn wenn die Zeit in Sommerzeit angegeben ist, dann muss man eigentlich eine Stunde von der Differenz abziehen. Nur weil Sommerzeit ist, sind ja nicht plötzlich 3600 Sekunden mehr seit dem vergangen.
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Hallo BlackJack,

ehrlich gesagt verstehe ich nicht wie mir struct_time helfen soll einen Timestamp aus einem String zu erzeugen? Könntest Du mir ein funktionales Beispiel zeigen?

Der String ist eine Vorgabe aus einem andreren Programm, auf das ich keinen Einfluss habe und berücksichtigt die Sommerzeit.

Gruß Marcia
BlackJack

@marcia: `struct_time` hilft dabei gar nicht.

Wenn die Zeiten in lokaler Zeit mit Berücksichtigung von Sommerzeit vorliegen, dann musst Du wissen aus welcher Zeitzone die Zeitangaben sind und eine Bibliothek verwenden, die Zeitzonen und ihre — politisch festgelegten — Regelungen kennt. Wobei Zeitzonen nicht nur diese „vertikalen Streifen” auf der Weltkarte sind, sondern Länderbezogen auch noch abweichende Regelungen, wie zum Beispiel Sommerzeit haben (Verschiebung der Zeit ja/nein?, wann?, um wie viel?). Das wäre ganz allgemein die sogenannte Olson Timezone DB und eine Python-Anbindung daran ist das `pytz`-Modul.

Unix-Timestamps sind Sekunden ab dem 1.1.1970 UTC (!). Du musst also die Zeitangabe in ein `datetime.datetime`-Objekt parsen, dem die richtigen Zeitzoneninformation verpassen — dazu muss man wissen für welches Land die lokalen Zeitangaben erzeugt wurden. Dann kann man die Zeitangabe in die UTC-Zeitzone umrechnen und daraus dann die Sekunden seit 1.1.1970 ermitteln.

Wegen dieser ganzen Umwandlungsprobleme sollte man Zeitstempel die von Programmen ausgewertet werden sollen, immer auf Basis von UTC-Zeit oder einer anderen Zeitangabe, die von den ganzen wirren Zeitzonenregelungen nicht betroffen ist, speichern.

Mal die Ergebnisse zum Vergleichen wobei die Zeitzone der Datumsangaben jeweils als 'Europe/Berlin' angenommen wird:

2012-02-29 22:05:44 = 1330549544
2012-03-29 22:05:44 = 1333051544

Das sind die Unix-Timestamps, die Du als Ergebnis heraus bekommen willst/solltest. Zumindest wenn die Annahme über die Zeitzone stimmt für die die Datumsangaben erzeugt wurden.

Zum ersten Schritt, dem Parsen mittels `datetime.datetime.strptime()`, lies Dir doch bitte die Dokumentation zu den Formatcodes bei `time.strftime()` durch und bring diese Funktion dazu, die aktuelle Zeit in dem Format auszugeben, wie das welches Du parsen willst. Ich habe da nämlich ganz stark den Verdacht dass Du das überhaupt nicht ernsthaft probiert hast, denn das ist gar nicht so schwer und Du solltest erst einmal zeigen, was Du gemacht hast, bevor Du es Dir von anderen vorkauen lässt.

Um das Problem zu lösen muss man relativ viel Dokumentation, aufmerksam lesen und verstehen. Die Funktionen sind über mehrere Module verteilt, eines davon ist nicht in der Standardbibliothek enthalten, und die Thematik insgesamt ist auch nicht die einfachste. Kommt IMHO gleich nach Unicode. Die Leute denken immer sie wissen was ein Buchstabe oder eine Zeitangabe ist, und es funktioniert auch alles problemlos wenn sie da ganz naiv heran gehen — solange sie in ihrer kleinen Welt bleiben, also nicht mit anderen Kodierungen oder Zeitzonen in Kontakt kommen.
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Wow Blackjack, da hat sich ja bei dir was angestaut. ;)

Vielen Dank für die ausführliche Antwort. Im dritten Posting habe ich gezeigt, wie ich die Umwandlung durchführe.

Code: Alles auswählen

dat=("22.02.2012", "14:45:59")
time.mktime((int(dat[0][6:10]),int(dat[0][3:5]),int(dat[0][0:2]),int(dat[1][0:2]),int(dat[1][3:5]),int(dat[1][6:8]),0,0,time.timezone))
Du hast recht, mit dem datetime Modul und erst recht mit dem pytz Modul habe ich mich gar nicht auseinander gesetzt. Das liegt wohl daran, das ich vor allem Letzteres nicht kenne. Das läst sich aber nachholen. :)

Dein Einverständnis vorausgesetzt, werde ich deine Ausführungen als Argumentationshilfe verwenden. Vielleicht gelingt es mir den Programmierer zu überzeugen, das zukünftig ein Timestamp verwendet wird.

Dein Hinweis auf die UTC Angabe von Timestamps hat mich gewundert. Deshalb habe ich es nachgeprüft, 1329918359 ergibt als GMT +1 die Uhrzeit 14:45:59.Egal ob ich den Stamp mit PHP oder Python formatiert ausgebe.

Gruß Marcia

PS: In dem Punkt mit Unicode, kann ich dir nur zustimmen. Mir ist immer noch nicht klar, warum es verschiedene UTF-x gibt und nicht eine einheitliche Codierung.
BlackJack

@marcia: Dass Du da als letztes Element in dem Tupel `time.timezone` übergibst, ist sehr irreführend. Da muss übergeben werden ob Sommerzeit ist oder nicht und keine Sekundenzahl. Negative Zahlen werden dabei als „keine Ahnung” interpretiert. Nun ist in unseren Breiten die `time.timezone` zufällig negativ, aber darauf kann man sich ja nicht verlassen.
marcia
User
Beiträge: 14
Registriert: Freitag 16. März 2012, 15:36

Hallo Blackjack,

stimmt nicht timezone sondern daylight muß verwendet werden.
Allerdings bekomme ich bei time.daylight immer eine 1 als Ausgabe. Auch wenn ich die Systemzeit auf Januar verstelle. Während der Sommerzeit ist ein Wert von 1 richtig, aber im Januar ist keine Sommerzeit. Das heißt hier müßte eine 0 ausgegeben werden. Richtig?

Außerdem gibt mktime mit 1 einen falschen Timestamp aus. Erst mit -1 funktioniert es. Habe mir die Dokumentation* durch gelesen und bin jetzt noch mehr verwirrt als vorher.

Gruß Marcia

Code: Alles auswählen

time.mktime(time.strptime("30.05.2012 13:14:49", "%d.%m.%Y %H:%M:%S"))
* http://docs.python.org/library/time.html
sunnyraven
User
Beiträge: 17
Registriert: Montag 10. Juni 2013, 13:30

Vielen, vielen Dank für die Hilfe BlackJack,
das Problem lag darin, dass an einigen Stellen aus dem XML, lesen werte so: ''
zurück kamen. Diese Werte habe ich nun durch eine Null ersetzt und schon gehts.
Antworten