Python strptime () i strefy czasowe?

157

Mam plik zrzutu CSV z kopii zapasowej telefonu Blackberry IPD, utworzony za pomocą IPDDump. Ciągi daty / czasu tutaj wyglądają mniej więcej tak (gdzie ESTjest australijska strefa czasowa):

Tue Jun 22 07:46:22 EST 2010

Muszę móc przeanalizować tę datę w Pythonie. Na początku próbowałem użyć strptime()funkcji z datettime.

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

Jednak z jakiegoś powodu datetimeobiekt, który wraca, nie wydaje się być tzinfoz nim powiązany.

Czytałem na tej stronie , że pozornie datetime.strptimecicho usuwa tzinfojednak sprawdziłem dokumentację, i nie mogę znaleźć nic w tym zakresie udokumentowane tutaj .

Udało mi się uzyskać analizę daty za pomocą biblioteki Python innej firmy, dateutil , ale nadal jestem ciekawy, w jaki sposób strptime()nieprawidłowo korzystałem z wbudowanej biblioteki ? Czy jest jakiś sposób, aby strptime()ładnie bawić się strefami czasowymi?

victorhooi
źródło
1
Nie możesz po prostu ... przekonwertować wszystkich dat na GMT?
Robus
2
@Robus: Hmm, miałem nadzieję, że to zrobię - ale zakładałem, że strftime / datetime może jakoś to zrobić? Tak czy inaczej, muszę zapisać / przeanalizować fakt, że daty są w strefie czasowej EST lub w innej strefie czasowej, w której mi się przytrafiły. Skrypt musi mieć możliwość analizowania ogólnych czasów danych z informacjami o strefie czasowej (np. ETC może być dowolną inną strefą czasową).
victorhooi
3
EST to także skrót dla strefy czasowej w USA. (Podobnie BST jest skrótem zarówno brytyjskiej, jak i brazylijskiej strefy czasowej.) Takie skróty są po prostu z natury niejednoznaczne. Zamiast tego użyj przesunięć względem UTC / GMT. (Jeśli potrzebujesz obsługiwać skróty, musisz uczynić mapowanie zależnym od lokalizacji i to jest bałaganiarska dziura na szczury.)
Donal Fellows,

Odpowiedzi:

58

Dokumentacja datetimemodułu mówi:

Zwraca datę i godzinę odpowiadającą ciągowi_date, przeanalizowaną zgodnie z formatem. Jest to równoważne z datetime(*(time.strptime(date_string, format)[0:6])).

Widzisz to [0:6]? To cię dostaje (year, month, day, hour, minute, second). Nic więcej. Brak wzmianki o strefach czasowych.

Co ciekawe, [Win XP SP2, Python 2.6, 2.7] przekazanie twojego przykładu do time.strptimenie działa, ale jeśli usuniesz „% Z” i „EST”, to zadziała. Działa również używanie „UTC” lub „GMT” zamiast „EST”. „PST” i „MEZ” nie działają. Zagadkowe.

Warto zauważyć, że zostało to zaktualizowane w wersji 3.2, a ta sama dokumentacja zawiera teraz również następujące informacje:

Po przekazaniu dyrektywy% z do metody strptime () zostanie utworzony świadomy obiekt datetime. Informacja tzinfo wyniku zostanie ustawiona na instancję strefy czasowej.

Zauważ, że to nie działa z% Z, więc wielkość liter jest ważna. Zobacz poniższy przykład:

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
źródło
13
Powiązany błąd Pythona: % Z w strptime nie pasuje do EST i innych
jfs
353

Polecam używanie python-dateutil . Jego parser był w stanie przeanalizować każdy format daty, jaki do tej pory użyłem.

>>> 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)

i tak dalej. Nie ma strptime()sensu zajmować się formatowaniem ... po prostu rzuć na to randkę i zrobi to dobrze.

Aktualizacja : Ups. Brakowało mi w twoim pierwotnym pytaniu, o którym wspomniałeś dateutil, przepraszam za to. Mam jednak nadzieję, że ta odpowiedź jest nadal przydatna dla innych osób, które natkną się na to pytanie, gdy mają pytania dotyczące analizy daty i widzą użyteczność tego modułu.

Joe Shaw
źródło
Biorąc pod uwagę, że tak wiele osób używa python-dateutil, chciałbym wskazać nam jedno ograniczenie tej biblioteki. >>> 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 musisz zastąpić ostatni przecinek kropką. Następnieparser.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
@flyingfoxlee, tak, rozumiem. Chcę tylko powiedzieć ludziom ograniczenia związane z python-dateutil. Robi magiczne rzeczy, ale czasami się to nie udaje. Więc „po prostu umów się na randkę i zrobi to dobrze”. nie jest w 100% prawdziwe.
wanghq
4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")zwraca: datetime.datetime(2016, 10, 27, 9, 6)nie
udało się ustalić
2
To zależy od celu. dateutil parsermoże być prosty w użyciu, ale strptime()jest szybszy. Poza tym jego formaty są dość łatwe do nauczenia.
Rapture
9

Twój ciąg czasu jest podobny do formatu czasu w rfc 2822 (format daty w wiadomości e-mail, nagłówki http) . Możesz go przeanalizować używając tylko standardowej biblioteki:

>>> 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)

Zobacz rozwiązania, które zapewniają obiekty daty i godziny uwzględniające strefę czasową dla różnych wersji Pythona: analizowanie daty ze strefą czasową z wiadomości e-mail .

W tym formacie ESTjest semantycznie równoważny z-0500 . Chociaż, ogólnie rzecz biorąc, skrót strefy czasowej nie wystarczy, aby jednoznacznie zidentyfikować strefę czasową .

jfs
źródło
0

Wpadłem na dokładnie ten problem.

Co ostatecznie zrobiłem:

# 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)
Krzysztof
źródło