Python strptime () und Zeitzonen?

157

Ich habe ein CSV-Dumpfile aus einem Blackberry IPD-Backup, das mit IPDDump erstellt wurde. Die Datums- / Zeitzeichenfolgen hier sehen ungefähr so ​​aus (wo ESTsich eine australische Zeitzone befindet):

Tue Jun 22 07:46:22 EST 2010

Ich muss in der Lage sein, dieses Datum in Python zu analysieren. Zuerst habe ich versucht, die strptime()Funktion von datettime zu verwenden.

>>> datetime.datetime.strptime('Tue Jun 22 12:10:20 2010 EST', '%a %b %d %H:%M:%S %Y %Z')

Aus irgendeinem Grund datetimescheint dem zurückkommenden Objekt jedoch nichts zugeordnet zu sein tzinfo.

Ich habe auf dieser Seite gelesen, dass anscheinend datetime.strptimestillschweigend verworfen tzinfowird. Ich habe jedoch die Dokumentation überprüft und kann hier nichts Dokumentiertes finden .

Ich konnte das Datum mithilfe einer Python-Bibliothek eines Drittanbieters, dateutil , analysieren . Ich bin jedoch immer noch neugierig, wie ich das eingebaute Datum strptime()falsch verwendet habe. Gibt es eine Möglichkeit, strptime()gut mit Zeitzonen zu spielen?

victorhooi
quelle
1
Können Sie nicht einfach ... alle Daten in GMT konvertieren?
Robus
2
@Robus: Hmm, ich hatte gehofft, das zu tun - aber ich nahm an, dass strftime / datetime das irgendwie tun könnte? In jedem Fall muss ich die Tatsache speichern / analysieren, dass sich die Datumszeiten in der EST-Zeitzone befinden oder in welcher Zeitzone sie mir auch passieren. Das Skript muss in der Lage sein, generische Datenzeiten mit Zeitzoneninformationen zu analysieren (z. B. ETC kann eine andere Zeitzone sein).
Victorhooi
3
EST ist auch eine Abkürzung für die US-Zeitzone. (In ähnlicher Weise ist BST sowohl eine Abkürzung für die britische als auch für die brasilianische Zeitzone.) Solche Abkürzungen sind nur von Natur aus mehrdeutig. Verwenden Sie stattdessen Offsets relativ zu UTC / GMT. (Wenn Sie Abkürzungen unterstützen müssen, müssen Sie das Mapping-Gebietsschema abhängig machen, und das ist ein chaotisches Rattenloch.)
Donal Fellows

Antworten:

58

Die datetimeModuldokumentation lautet:

Gibt eine Datums- / Uhrzeitangabe zurück, die date_string entspricht und nach Format analysiert wird. Dies entspricht datetime(*(time.strptime(date_string, format)[0:6])).

Sehen Sie das [0:6]? Das bringt dich (year, month, day, hour, minute, second). Nichts anderes. Keine Erwähnung von Zeitzonen.

Interessanterweise time.strptimefunktioniert [Win XP SP2, Python 2.6, 2.7] , wenn Sie Ihr Beispiel an übergeben , nicht, aber wenn Sie "% Z" und "EST" entfernen, funktioniert es. Auch die Verwendung von "UTC" oder "GMT" anstelle von "EST" funktioniert. "PST" und "MEZ" funktionieren nicht. Rätselhaft.

Es ist erwähnenswert, dass dies ab Version 3.2 aktualisiert wurde und in derselben Dokumentation nun auch Folgendes angegeben ist:

Wenn die% z-Direktive für die strptime () -Methode bereitgestellt wird, wird ein bewusstes datetime-Objekt erstellt. Das tzinfo des Ergebnisses wird auf eine Zeitzoneninstanz gesetzt.

Beachten Sie, dass dies mit% Z nicht funktioniert, daher ist der Fall wichtig. Siehe folgendes Beispiel:

In [1]: from datetime import datetime

In [2]: start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z')

In [3]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: None

In [4]: start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z')

In [5]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: UTC+10:00
John Machin
quelle
353

Ich empfehle die Verwendung von Python-Dateutil . Der Parser konnte jedes Datumsformat analysieren, das ich bisher verwendet habe.

>>> from dateutil import parser
>>> parser.parse("Tue Jun 22 07:46:22 EST 2010")
datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzlocal())
>>> parser.parse("Fri, 11 Nov 2011 03:18:09 -0400")
datetime.datetime(2011, 11, 11, 3, 18, 9, tzinfo=tzoffset(None, -14400))
>>> parser.parse("Sun")
datetime.datetime(2011, 12, 18, 0, 0)
>>> parser.parse("10-11-08")
datetime.datetime(2008, 10, 11, 0, 0)

und so weiter. Kein Umgang mit strptime()Format-Unsinn ... werfen Sie einfach ein Datum darauf und es macht das Richtige.

Update : Ups. Ich habe in Ihrer ursprünglichen Frage übersehen, dass Sie erwähnt haben, dass Sie verwendet haben dateutil, tut mir leid. Ich hoffe jedoch, dass diese Antwort für andere Personen nützlich ist, die über diese Frage stolpern, wenn sie Fragen zur Datumsanalyse haben und die Nützlichkeit dieses Moduls erkennen.

Joe Shaw
quelle
Angesichts der Tatsache, dass so viele Leute dazu neigen, Python-Dateutil zu verwenden, möchte ich auf eine Einschränkung dieser Bibliothek hinweisen. >>> parser.parse("Thu, 25 Sep 2003 10:49:41,123 -0300") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 748, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 310, in parse res, skipped_tokens = self._parse(timestr, **kwargs) TypeError: 'NoneType' object is not iterable
Wanghq
1
@wanghq Sie müssen das letzte Komma durch Punkt ersetzen. Dannparser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300") returns: datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800))
Flyingfoxlee
7
@fllyingfoxlee, ja, das verstehe ich. Ich möchte den Leuten nur die Einschränkung von Python-Dateutil erklären. Es macht magische Dinge, aber manchmal scheitert es daran. Also "wirf einfach ein Date drauf und es macht das Richtige." ist nicht 100% wahr.
Wanghq
4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")Rückkehr: datetime.datetime(2016, 10, 27, 9, 6)Zeitzone nicht herausgefunden ...
HaPsantran
2
Es kommt auf das Ziel an. dateutil parsermag einfach zu bedienen sein, ist aber strptime()schneller. Außerdem sind seine Formate recht einfach zu erlernen.
Entrückung
9

Ihre Zeitzeichenfolge ähnelt dem Zeitformat in rfc 2822 (Datumsformat in E-Mail, http-Header) . Sie können es nur mit stdlib analysieren:

>>> from email.utils import parsedate_tz
>>> parsedate_tz('Tue Jun 22 07:46:22 EST 2010')
(2010, 6, 22, 7, 46, 22, 0, 1, -1, -18000)

Sehen Sie sich Lösungen an, die zeitzonenabhängige datetime-Objekte für verschiedene Python-Versionen liefern: Analysieren des Datums mit der Zeitzone aus einer E-Mail .

In diesem Format ESTist semantisch äquivalent zu-0500 . Im Allgemeinen reicht eine Zeitzonenabkürzung jedoch nicht aus, um eine Zeitzone eindeutig zu identifizieren .

jfs
quelle
0

Bin genau auf dieses Problem gestoßen.

Was ich letztendlich gemacht habe:

# starting with date string
sdt = "20190901"
std_format = '%Y%m%d'

# create naive datetime object
from datetime import datetime
dt = datetime.strptime(sdt, sdt_format)

# extract the relevant date time items
dt_formatters = ['%Y','%m','%d']
dt_vals = tuple(map(lambda formatter: int(datetime.strftime(dt,formatter)), dt_formatters))

# set timezone
import pendulum
tz = pendulum.timezone('utc')

dt_tz = datetime(*dt_vals,tzinfo=tz)
Christopher
quelle