Żeby było jasne: ISO 8601 jest głównym standardem. RFC 3339 to samozwańczy „profil” ISO 8601, który powoduje niemądre zastąpienie reguł ISO 8601.
Basil Bourque,
3
Nie przegap poniższego rozwiązania python3.7 + do odwracania izoformatu ()
Brad M
2
To pytanie nie powinno być zamknięte jako duplikat do powiązanego postu. Ponieważ ten prosi o przeanalizowanie ciągu czasu ISO 8601 (który nie był obsługiwany natywnie przez Pythona przed wersją 3.7), a drugi polega na sformatowaniu obiektu daty / godziny w ciągu epoki przy użyciu przestarzałej metody.
abccd
Odpowiedzi:
462
Pakiet python-dateutil może analizować nie tylko ciągi daty i godziny RFC 3339, jak ten w pytaniu, ale także inne ciągi daty i godziny ISO 8601 , które nie są zgodne z RFC 3339 (takie jak te bez przesunięcia UTC lub te, które reprezentują tylko randka).
>>>import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z')# RFC 3339 format
datetime.datetime(2008,9,3,20,56,35,450686, tzinfo=tzutc())>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686')# ISO 8601 extended format
datetime.datetime(2008,9,3,20,56,35,450686)>>> dateutil.parser.isoparse('20080903T205635.450686')# ISO 8601 basic format
datetime.datetime(2008,9,3,20,56,35,450686)>>> dateutil.parser.isoparse('20080903')# ISO 8601 basic format, date only
datetime.datetime(2008,9,3,0,0)
Zauważ, że dateutil.parser.isoparsejest to prawdopodobnie bardziej rygorystyczne niż bardziej hackerskie dateutil.parser.parse, ale oba są dość wybaczające i będą próbowały zinterpretować przekazany ciąg. Jeśli chcesz wyeliminować możliwość jakichkolwiek błędnych odczytów, musisz użyć czegoś surowszego niż którekolwiek z tych Funkcje.
Dla leniwych, to zainstalowany przez python-dateutilnie dateutil, tak: pip install python-dateutil.
cod3monk3y
29
Ostrzegamy, że dateutil.parserjest celowo włamany: próbuje odgadnąć format i przyjmuje nieuniknione założenia (konfigurowalne tylko ręcznie) w niejednoznacznych przypadkach. Używaj go TYLKO, jeśli chcesz przeanalizować dane wejściowe o nieznanym formacie i dobrze jest tolerować okazjonalne błędne odczytywanie.
ivan_pozdeev
2
Zgoda. Przykładem jest przekazanie „daty” 9999. Zwróci to tyle samo, co data i godzina (9999, bieżący miesiąc, bieżący dzień). Moim zdaniem nie jest to prawidłowa data.
timbo
1
@ivan_pozdeev jaki pakiet poleciłbyś do analizy bez zgadywania?
bgusach
2
@ivan_pozdeev dostępna jest aktualizacja modułu odczytującego daty iso8601
TheEililon
196
Nowości w Python 3.7+
datetimeStandardowa biblioteka wprowadziła funkcję odwracania datetime.isoformat().
To jest dziwne. Ponieważ a datetimemoże zawierać tzinfostrefę czasową, a tym samym wyświetla strefę czasową, ale datetime.fromisoformat()nie analizuje tzinfo? wygląda jak błąd ..
Hendy Irawan
20
Nie przegap tej uwagi w dokumentacji, to nie akceptuje wszystkich prawidłowych ciągów ISO 8601, tylko te wygenerowane przez isoformat. Nie akceptuje przykładu w pytaniu z "2008-09-03T20:56:35.450686Z"powodu końcowego Z, ale akceptuje "2008-09-03T20:56:35.450686".
Flimm
26
Aby poprawnie obsługiwać Zskrypt wejściowy, można go zmodyfikować za pomocą date_string.replace("Z", "+00:00").
jox
7
Pamiętaj, że przez kilka sekund obsługuje tylko dokładnie 0, 3 lub 6 miejsc po przecinku. Jeśli dane wejściowe mają 1, 2, 4, 5, 7 lub więcej miejsc dziesiętnych, parsowanie zakończy się niepowodzeniem!
Felk
1
@JDOaktown W tym przykładzie użyto rodzimej biblioteki datetime Pythona, a nie parsera dateutil. W rzeczywistości nie powiedzie się, jeśli miejsca dziesiętne nie są 0, 3 lub 6 przy takim podejściu.
abccd
174
Uwaga w Pythonie 2.6+ i Py3K znak% f przechwytuje mikrosekundy.
Uwaga - jeśli używasz danych Naive - Myślę, że w ogóle nie masz TZ - Z może nie pasować do niczego.
Danny Staple
24
Ta odpowiedź (w obecnej, edytowanej formie) polega na zakodowaniu na stałe określonego przesunięcia UTC (a mianowicie „Z”, co oznacza +00: 00) w ciągu formatu. Jest to zły pomysł, ponieważ nie uda mu się parsować daty i godziny z innym przesunięciem UTC i zgłosić wyjątek. Zobacz moją odpowiedź, która opisuje, w jaki sposób parsowanie RFC 3339 strptimejest w rzeczywistości niemożliwe.
Mark Amery
1
w moim przypadku% f złapał mikrosekundy zamiast Z, datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') więc to
załatwiło sprawę
Czy Py3K oznacza Python 3000?!?
Robino,
2
@Robino IIRC, „Python 3000” to stara nazwa tego, co jest obecnie znane jako Python 3.
Zakładając, że chcesz obsługiwać pełny format RFC 3339, w tym obsługę przesunięć UTC innych niż zero, kod sugerowany przez te odpowiedzi nie działa. Rzeczywiście, nie może działać, ponieważ analiza składni RFC 3339 przy użyciu strptimejest niemożliwa. Ciągi formatujące używane przez moduł datetime Pythona nie są w stanie opisać składni RFC 3339.
Problemem są przesunięcia UTC. RFC 3339 Internet Date / Time Format wymaga, aby każda data-czas zawiera UTC offset, i że te przesunięcia mogą być albo Z(skrót od „czasu Zulu”) lub w +HH:MMlub -HH:MMformatu, jak +05:00i -10:30.
W związku z tym wszystkie są poprawnymi danymi RFC 3339:
Przesunięcie UTC w postaci + HHMM lub -HHMM (pusty ciąg, jeśli obiekt jest naiwny).
Przykład: (pusty), +0000, -0400, +1030
To nie pasuje do formatu przesunięcia RFC 3339, a jeśli spróbujemy użyć %zw ciągu formatu i przeanalizować datę RFC 3339, nie powiedzie się:
>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
Wiele odpowiedzi tutaj, które zalecają strptimeobejście tego problemu, poprzez dołączenie literału Zw łańcuchu formatu, który odpowiada Zprzykładowemu ciągowi datetime zapytania pytającego (i odrzuca go, tworząc datetimeobiekt bez strefy czasowej):
Ponieważ powoduje to odrzucenie informacji o strefie czasowej zawartych w oryginalnym ciągu daty i godziny, wątpliwe jest, czy nawet ten wynik należy uznać za poprawny. Ale co ważniejsze, ponieważ takie podejście wymaga zakodowania na stałe określonego przesunięcia UTC w ciągu formatu , zadławi się w chwili, gdy spróbuje parsować dowolną datę / godzinę RFC 3339 z innym przesunięciem UTC:
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'
Jeśli nie masz pewności , że potrzebujesz obsługi danych RFC 3339 tylko w czasie Zulu, a nie tych z innymi przesunięciami stref czasowych, nie używaj strptime. Zamiast tego użyj jednego z wielu innych podejść opisanych w odpowiedziach tutaj.
Zastanawia się, dlaczego strptime nie ma dyrektywy dotyczącej informacji o strefie czasowej w formacie ISO i dlaczego nie można jej przeanalizować. Niesamowite.
Csaba Toth,
2
@CsabaToth Całkowicie się zgodził - jeśli mam trochę czasu na zabicie, być może spróbuję dodać to do języka. Lub możesz to zrobić, jeśli jesteś tak skłonny - widzę, że masz trochę doświadczenia w C, w przeciwieństwie do mnie.
Mark Amery
1
@CsabaToth - Dlaczego niesamowity? Działa wystarczająco dobrze dla większości ludzi, lub znaleźli dość łatwe obejście. Jeśli potrzebujesz tej funkcji, jest to program typu open source i możesz go dodać. Lub zapłać komuś za to. Dlaczego ktoś miałby poświęcić swój wolny czas na rozwiązanie określonych problemów? Niech źródło będzie z tobą.
Peter M. - oznacza Monica
2
@PeterMasiar Incredible, ponieważ zwykle odkrywa się, że rzeczy w Pythonie zostały zaimplementowane w sposób przemyślany i pełny. Zaskoczyło nas to przywiązanie do szczegółów, więc kiedy natkniemy się na coś w języku „niepythonic”, wyrzucamy zabawki z wózka, co właśnie zamierzam zrobić. Whaaaaaaaaaa Whaa wahaaaaa :-(
Robino
2
strptime()w Pythonie 3.7 obsługuje teraz wszystko opisane w tej odpowiedzi jako niemożliwe (literał „Z” i „:” w przesunięciu strefy czasowej). Niestety, istnieje inny przypadek narożny, który sprawia, że RFC 3339 jest zasadniczo niezgodny z ISO 8601, a mianowicie ten pierwszy pozwala na ujemne przesunięcie strefy czasowej zerowej -00: 00, a później nie.
Pytanie nie brzmiało „jak parsować daty ISO 8601”, lecz „jak parsować ten dokładny format daty”.
Nicholas Riley,
3
@tiktak OP zapytał: „Muszę parsować łańcuchy takie jak X”, a moja odpowiedź na to, po wypróbowaniu obu bibliotek, polega na użyciu innej, ponieważ iso8601 wciąż ma ważne problemy. Moje zaangażowanie lub jego brak w takim projekcie nie ma żadnego związku z odpowiedzią.
Tobia,
2
Należy pamiętać, że wersja pip iso8601 nie została zaktualizowana od 2007 roku i ma kilka poważnych błędów, które są nierozstrzygnięte. Zalecam samodzielne zastosowanie niektórych krytycznych poprawek lub znaleźć jeden z wielu rozwidleń github, które już to zrobiły, github.com/keithhackbarth/pyiso8601-strict
keithhackbarth
6
iso8601 , znany również jako pyiso8601 , został zaktualizowany dopiero w lutym 2014 r. Najnowsza wersja obsługuje znacznie szerszy zestaw ciągów znaków ISO 8601. Korzystałem z dobrych efektów w niektórych moich projektach.
Dave Hein
34
import re, datetime
s = "2008-09-03T20: 56: 35.450686Z"
d = datetime.datetime (* map (int, re.split ('[^ \ d]', s) [: - 1]))
Nie zgadzam się, jest to praktycznie nieczytelne i, o ile mogę stwierdzić, nie bierze pod uwagę Zulu (Z), co czyni tę datę naiwną, nawet jeśli podano dane strefy czasowej.
umbrae
14
Uważam to za dość czytelne. W rzeczywistości jest to prawdopodobnie najłatwiejszy i najskuteczniejszy sposób przeprowadzenia konwersji bez instalowania dodatkowych pakietów.
Tobia,
2
Jest to równoważne z d = datetime.datetime (* map (int, re.split ('\ D', s) [: - 1])).
W rezultacie powstaje naiwny obiekt typu data-godzina bez strefy czasowej, prawda? Więc bit UTC gubi się w tłumaczeniu?
w00t
32
Jaki dokładnie otrzymujesz błąd? Czy to wygląda następująco?
>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z","%Y-%m-%dT%H:%M:%S.Z")ValueError: time data did not match format: data=2008-08-12T12:20:30.656234Z fmt=%Y-%m-%dT%H:%M:%S.Z
Jeśli tak, możesz podzielić ciąg wejściowy na „.”, A następnie dodać mikrosekundy do otrzymanego czasu danych.
Nie można po prostu rozebrać pliku .Z, ponieważ oznacza on strefę czasową i może być inny. Muszę przekonwertować datę na strefę czasową UTC.
Alexander Artemenko
Zwykły obiekt daty-godziny nie ma pojęcia strefy czasowej. Jeśli wszystkie czasy kończą się na „Z”, wszystkie otrzymane czasy danych to UTC (czas Zulu).
tzot
jeśli strefa czasowa jest inna niż ""lub "Z", to musi być przesunięciem w godzinach / minutach, które można bezpośrednio dodawać / odejmować od obiektu datetime. Państwo mogłoby stworzyć tzinfo podklasy, aby go obsługiwać, ale to chyba nie polecamy.
SingleNegationElimination
8
Dodatkowo „% f” jest specyfikatorem mikrosekundowym, więc łańcuch czasu (naiwny dla strefy czasowej) wygląda następująco: „% Y-% m-% dT% H:% M:% S.% f”.
quodlibetor
1
Spowoduje to wyjątek, jeśli podany ciąg datetime ma przesunięcie UTC inne niż „Z”. Nie obsługuje całego formatu RFC 3339 i jest gorszą odpowiedzią dla innych, które odpowiednio obsługują przesunięcia UTC.
Mark Amery
24
Począwszy od Python 3.7, strptime obsługuje ograniczniki jelita grubego w przesunięciach UTC ( źródło ). Możesz więc użyć:
Ale w 3.7, to również posiadają datetime.fromisoformat()który obsługuje ciągi jak twój wprowadzania automatycznie: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00').
Martijn Pieters
2
Słuszna uwaga. Zgadzam się, polecam używać datetime.fromisoformat()idatetime.isoformat()
Andreas Profous
19
W tych dniach, Arrow może być również używany jako rozwiązanie zewnętrzne:
>>>import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")>>> date.datetime
datetime.datetime(2008,9,3,20,56,35,450686, tzinfo=tzutc())
Wystarczy użyć python-dateutil - strzałka wymaga python-dateutil.
danizen
Arrow obsługuje teraz ISO8601. Problemy, o których mowa, są teraz zamknięte.
Altus
17
Wystarczy użyć python-dateutilmodułu:
>>>import dateutil.parser as dp
>>> t ='1984-06-02T19:05:00.000Z'>>>parsed_t= dp.parse(t)>>>print(parsed_t)
datetime.datetime(1984,6,2,19,5, tzinfo=tzutc())
@tripleee Właściwie właśnie sprawdziłem kod i wydaje się, że zwraca poprawną odpowiedź: 455051100(sprawdzone na epochconverter.com ) ,,, chyba że czegoś brakuje?
Blairg23,
13
Jeśli nie chcesz używać dateutil, możesz wypróbować tę funkcję:
def from_utc(utcTime,fmt="%Y-%m-%dT%H:%M:%S.%fZ"):"""
Convert UTC time string to time.struct_time
"""# change datetime.datetime to time, return time.struct_time typereturn datetime.datetime.strptime(utcTime, fmt)
Ta odpowiedź polega na zakodowaniu na stałe określonego przesunięcia UTC (mianowicie „Z”, co oznacza +00: 00) w ciągu formatu przekazanego do strptime. Jest to zły pomysł, ponieważ nie uda mu się parsować daty i godziny z innym przesunięciem UTC i nie zgłosi wyjątku. Zobacz moją odpowiedź, która opisuje, w jaki sposób parsowanie RFC 3339 ze strptime jest w rzeczywistości niemożliwe.
Mark Amery
1
Jest sztywno zakodowany, ale wystarcza na wypadek, gdy trzeba parsować tylko zulu.
Sasha
1
@alexander tak - może tak być, jeśli na przykład wiesz, że łańcuch daty został wygenerowany toISOStringmetodą JavaScript . Ale w tej odpowiedzi nie ma wzmianki o ograniczeniu dat Zulu, ani pytanie nie wskazywało, że to wszystko, co jest potrzebne, a samo użycie dateutiljest zwykle równie wygodne i mniej wąskie w tym, co może przeanalizować.
Mark Amery
11
Jeśli pracujesz z Django, udostępnia on moduł dateparse który akceptuje wiele formatów podobnych do formatu ISO, w tym strefę czasową.
Jeśli nie korzystasz z Django i nie chcesz używać żadnej z wymienionych tu bibliotek, prawdopodobnie możesz dostosować kod źródłowy Django dla dateparse do swojego projektu.
To wygląda na świetną bibliotekę! Dla tych, którzy chcą zoptymalizować analizę ISO8601 w Google App Engine, niestety nie możemy jej użyć, ponieważ jest to biblioteka C, ale twoje testy porównawcze wykazały, że natywna datetime.strptime()jest kolejnym najszybszym rozwiązaniem. Dziękujemy za połączenie wszystkich tych informacji!
hamx0r
3
@ hamx0r, pamiętaj, że datetime.strptime()nie jest to pełna biblioteka parsująca ISO 8601. Jeśli korzystasz z języka Python 3.7, możesz użyć datetime.fromisoformat()metody, która jest nieco bardziej elastyczna. Być może zainteresuje Cię ta bardziej kompletna lista parserów, która wkrótce powinna zostać scalona z ciso8601 README.
movermeyer,
ciso8601 działa całkiem nieźle, ale najpierw trzeba wykonać „pip install pytz”, ponieważ nie można przeanalizować znacznika czasu z informacjami o strefie czasowej bez zależności od pytza. Przykład może wyglądać następująco: dob = ciso8601.parse_datetime (result ['dob'] ['date'])
Ta odpowiedź polega na zakodowaniu na stałe określonego przesunięcia UTC (mianowicie „Z”, co oznacza +00: 00) w ciągu formatu przekazanego do strptime. Jest to zły pomysł, ponieważ nie uda mu się parsować daty i godziny z innym przesunięciem UTC i nie zgłosi wyjątku. Zobacz moją odpowiedź, która opisuje, w jaki sposób parsowanie RFC 3339 ze strptime jest w rzeczywistości niemożliwe.
Mark Amery
1
Teoretycznie tak, to się nie udaje. W praktyce nigdy nie spotkałem daty w formacie ISO 8601, która nie była w czasach Zulu. W moich bardzo rzadkich przypadkach działa to świetnie i nie zależy od niektórych zewnętrznych bibliotek.
Benjamin Riggs,
4
możesz użyć timezone.utczamiast timezone(timedelta(0)). Również prace kodu w Pythonie 2.6+ (co najmniej), jeśli dostarczy utctzinfo obiektu
JFS
Nie ma znaczenia, czy go spotkałeś, nie pasuje do specyfikacji.
theouncouncer
Możesz użyć %Zstrefy czasowej for w najnowszych wersjach Pythona.
sventechie
7
Jestem autorem narzędzi iso8601. Można go znaleźć na GitHub lub na PyPI . Oto jak możesz przeanalizować swój przykład:
Jednym prostym sposobem na konwersję łańcucha daty podobnego do ISO 8601 na znacznik czasu lub datetime.datetimeobiekt UNIX we wszystkich obsługiwanych wersjach Pythona bez instalowania modułów innych firm jest użycie analizatora daty SQLite .
#!/usr/bin/env pythonfrom __future__ import with_statement, division, print_function
import sqlite3
import datetime
testtimes =["2016-08-25T16:01:26.123456Z","2016-08-25T16:01:29",]
db = sqlite3.connect(":memory:")
c = db.cursor()for timestring in testtimes:
c.execute("SELECT strftime('%s', ?)",(timestring,))
converted = c.fetchone()[0]print("%s is %s after epoch"%(timestring, converted))
dt = datetime.datetime.fromtimestamp(int(converted))print("datetime is %s"% dt)
Wynik:
2016-08-25T16:01:26.123456Zis1472140886 after epoch
datetime is2016-08-2512:01:262016-08-25T16:01:29is1472140889 after epoch
datetime is2016-08-2512:01:29
Co za niesamowity, niesamowity, piękny hack! Dzięki!
Havok
6
Zakodowałem parser dla standardu ISO 8601 i umieściłem go w GitHub: https://github.com/boxed/iso8601 . Ta implementacja obsługuje wszystko w specyfikacji, z wyjątkiem czasów trwania, interwałów, interwałów okresowych i dat poza obsługiwanym zakresem dat modułu datetime modułu Python.
Ponieważ ISO 8601 pozwala zasadniczo na wiele odmian opcjonalnych dwukropków i myślników CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]. Jeśli chcesz użyć Strptime, musisz najpierw usunąć te odmiany.
Celem jest wygenerowanie obiektu datetime utc.
Jeśli chcesz tylko podstawowy przypadek, który działa dla UTC z sufiksem Z, taki jak 2016-06-29T19:36:29.3453Z:
Jeśli chcesz obsługiwać przesunięcia stref czasowych, takie jak 2016-06-29T19:36:29.3453-0400lub 2008-09-03T20:56:35.450686+05:00skorzystaj z poniższych. Spowodują to przekształcenie wszystkich odmian w coś bez ograniczników zmiennych, takich jak 20080903T205635.450686+0500uczynienie go bardziej spójnym / łatwiejszym do parsowania.
import re
# this regex removes all colons and all # dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))",'', timestamp)
datetime.datetime.strptime(conformed_timestamp,"%Y%m%dT%H%M%S.%f%z")
Jeśli twój system nie obsługuje %zdyrektywy Strptime (widzisz coś takiego ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'), musisz ręcznie przesunąć czas od Z(UTC). Uwaga %zmoże nie działać w systemie w wersjach języka Python <3, ponieważ zależała od obsługi biblioteki c, która różni się w zależności od typu kompilacji system / python (tj. Jython, Cython itp.).
import re
import datetime
# this regex removes all colons and all # dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))",'', timestamp)# split on the offset to remove it. use a capture group to keep the delimiter
split_timestamp = re.split(r"[+|-]",conformed_timestamp)
main_timestamp = split_timestamp[0]if len(split_timestamp)==3:
sign = split_timestamp[1]
offset = split_timestamp[2]else:
sign =None
offset =None# generate the datetime object without the offset at UTC time
output_datetime = datetime.datetime.strptime(main_timestamp +"Z","%Y%m%dT%H%M%S.%fZ")if offset:# create timedelta based on offset
offset_delta = datetime.timedelta(hours=int(sign+offset[:-2]), minutes=int(sign+offset[-2:]))# offset datetime with timedelta
output_datetime = output_datetime + offset_delta
Dzięki wielkiej odpowiedzi Marka Ameryka opracowałem funkcję rozliczania wszystkich możliwych formatów ISO daty / godziny:
classFixedOffset(tzinfo):"""Fixed offset in minutes: `time = utc_time + utc_offset`."""def __init__(self, offset):
self.__offset = timedelta(minutes=offset)
hours, minutes = divmod(offset,60)#NOTE: the last part is to remind about deprecated POSIX GMT+h timezones# that have the opposite sign in the name;# the corresponding numeric value is not used e.g., no minutes
self.__name ='<%+03d%02d>%+d'%(hours, minutes,-hours)def utcoffset(self, dt=None):return self.__offset
def tzname(self, dt=None):return self.__name
def dst(self, dt=None):return timedelta(0)def __repr__(self):return'FixedOffset(%d)'%(self.utcoffset().total_seconds()/60)def __getinitargs__(self):return(self.__offset.total_seconds()/60,)def parse_isoformat_datetime(isodatetime):try:return datetime.strptime(isodatetime,'%Y-%m-%dT%H:%M:%S.%f')exceptValueError:passtry:return datetime.strptime(isodatetime,'%Y-%m-%dT%H:%M:%S')exceptValueError:pass
pat = r'(.*?[+-]\d{2}):(\d{2})'
temp = re.sub(pat, r'\1\2', isodatetime)
naive_date_str = temp[:-5]
offset_str = temp[-5:]
naive_dt = datetime.strptime(naive_date_str,'%Y-%m-%dT%H:%M:%S.%f')
offset = int(offset_str[-4:-2])*60+ int(offset_str[-2:])if offset_str[0]=="-":
offset =-offset
return naive_dt.replace(tzinfo=FixedOffset(offset))
Odpowiedzi:
Pakiet python-dateutil może analizować nie tylko ciągi daty i godziny RFC 3339, jak ten w pytaniu, ale także inne ciągi daty i godziny ISO 8601 , które nie są zgodne z RFC 3339 (takie jak te bez przesunięcia UTC lub te, które reprezentują tylko randka).
Zauważ, że
dateutil.parser.isoparse
jest to prawdopodobnie bardziej rygorystyczne niż bardziej hackerskiedateutil.parser.parse
, ale oba są dość wybaczające i będą próbowały zinterpretować przekazany ciąg. Jeśli chcesz wyeliminować możliwość jakichkolwiek błędnych odczytów, musisz użyć czegoś surowszego niż którekolwiek z tych Funkcje.Nazwa Pypi to
python-dateutil
niedateutil
(dzięki code3monk3y ):Jeśli używasz języka Python 3.7, zapoznaj się z tą odpowiedzią na temat
datetime.datetime.fromisoformat
.źródło
python-dateutil
niedateutil
, tak:pip install python-dateutil
.dateutil.parser
jest celowo włamany: próbuje odgadnąć format i przyjmuje nieuniknione założenia (konfigurowalne tylko ręcznie) w niejednoznacznych przypadkach. Używaj go TYLKO, jeśli chcesz przeanalizować dane wejściowe o nieznanym formacie i dobrze jest tolerować okazjonalne błędne odczytywanie.Nowości w Python 3.7+
datetime
Standardowa biblioteka wprowadziła funkcję odwracaniadatetime.isoformat()
.Przykład zastosowania:
źródło
datetime
może zawieraćtzinfo
strefę czasową, a tym samym wyświetla strefę czasową, aledatetime.fromisoformat()
nie analizuje tzinfo? wygląda jak błąd ..isoformat
. Nie akceptuje przykładu w pytaniu z"2008-09-03T20:56:35.450686Z"
powodu końcowegoZ
, ale akceptuje"2008-09-03T20:56:35.450686"
.Z
skrypt wejściowy, można go zmodyfikować za pomocądate_string.replace("Z", "+00:00")
.Uwaga w Pythonie 2.6+ i Py3K znak% f przechwytuje mikrosekundy.
Zobacz problem tutaj
źródło
strptime
jest w rzeczywistości niemożliwe.datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
więc toKilka odpowiedzi tutaj sugeruje użycie
datetime.datetime.strptime
do parsowania czasów danych RFC 3339 lub ISO 8601 ze strefami czasowymi, takich jak ta pokazana w pytaniu:To jest zły pomysł.
Zakładając, że chcesz obsługiwać pełny format RFC 3339, w tym obsługę przesunięć UTC innych niż zero, kod sugerowany przez te odpowiedzi nie działa. Rzeczywiście, nie może działać, ponieważ analiza składni RFC 3339 przy użyciu
strptime
jest niemożliwa. Ciągi formatujące używane przez moduł datetime Pythona nie są w stanie opisać składni RFC 3339.Problemem są przesunięcia UTC. RFC 3339 Internet Date / Time Format wymaga, aby każda data-czas zawiera UTC offset, i że te przesunięcia mogą być albo
Z
(skrót od „czasu Zulu”) lub w+HH:MM
lub-HH:MM
formatu, jak+05:00
i-10:30
.W związku z tym wszystkie są poprawnymi danymi RFC 3339:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Niestety, ciągi formatu używane przez
strptime
istrftime
nie mają dyrektywy, która odpowiada przesunięciom UTC w formacie RFC 3339. Pełna lista obsługiwanych przez nie dyrektyw znajduje się na stronie https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior , a jedyną dyrektywą offsetową UTC zawartą na liście jest%z
:To nie pasuje do formatu przesunięcia RFC 3339, a jeśli spróbujemy użyć
%z
w ciągu formatu i przeanalizować datę RFC 3339, nie powiedzie się:(W rzeczywistości powyższe jest dokładnie tym, co zobaczysz w Pythonie 3. W Pythonie 2 nie powiedzie się z jeszcze prostszego powodu, to
strptime
znaczy,%z
że w ogóle nie implementuje dyrektywy w Pythonie 2 ).Wiele odpowiedzi tutaj, które zalecają
strptime
obejście tego problemu, poprzez dołączenie literałuZ
w łańcuchu formatu, który odpowiadaZ
przykładowemu ciągowi datetime zapytania pytającego (i odrzuca go, tworzącdatetime
obiekt bez strefy czasowej):Ponieważ powoduje to odrzucenie informacji o strefie czasowej zawartych w oryginalnym ciągu daty i godziny, wątpliwe jest, czy nawet ten wynik należy uznać za poprawny. Ale co ważniejsze, ponieważ takie podejście wymaga zakodowania na stałe określonego przesunięcia UTC w ciągu formatu , zadławi się w chwili, gdy spróbuje parsować dowolną datę / godzinę RFC 3339 z innym przesunięciem UTC:
Jeśli nie masz pewności , że potrzebujesz obsługi danych RFC 3339 tylko w czasie Zulu, a nie tych z innymi przesunięciami stref czasowych, nie używaj
strptime
. Zamiast tego użyj jednego z wielu innych podejść opisanych w odpowiedziach tutaj.źródło
strptime()
w Pythonie 3.7 obsługuje teraz wszystko opisane w tej odpowiedzi jako niemożliwe (literał „Z” i „:” w przesunięciu strefy czasowej). Niestety, istnieje inny przypadek narożny, który sprawia, że RFC 3339 jest zasadniczo niezgodny z ISO 8601, a mianowicie ten pierwszy pozwala na ujemne przesunięcie strefy czasowej zerowej -00: 00, a później nie.Wypróbuj moduł iso8601 ; robi dokładnie to.
Istnieje kilka innych opcji wymienionych na stronie WorkingWithTime na wiki python.org.
źródło
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
źródło
datetime.datetime(*map(int, re.findall('\d+', s))
Jaki dokładnie otrzymujesz błąd? Czy to wygląda następująco?
Jeśli tak, możesz podzielić ciąg wejściowy na „.”, A następnie dodać mikrosekundy do otrzymanego czasu danych.
Spróbuj tego:
źródło
""
lub"Z"
, to musi być przesunięciem w godzinach / minutach, które można bezpośrednio dodawać / odejmować od obiektu datetime. Państwo mogłoby stworzyć tzinfo podklasy, aby go obsługiwać, ale to chyba nie polecamy.Począwszy od Python 3.7, strptime obsługuje ograniczniki jelita grubego w przesunięciach UTC ( źródło ). Możesz więc użyć:
EDYTOWAĆ:
Jak zauważył Martijn, jeśli utworzyłeś obiekt datetime za pomocą isoformat (), możesz po prostu użyć datetime.fromisoformat ()
źródło
datetime.fromisoformat()
który obsługuje ciągi jak twój wprowadzania automatycznie:datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.datetime.fromisoformat()
idatetime.isoformat()
W tych dniach, Arrow może być również używany jako rozwiązanie zewnętrzne:
źródło
Wystarczy użyć
python-dateutil
modułu:Dokumentacja
źródło
455051100
(sprawdzone na epochconverter.com ) ,,, chyba że czegoś brakuje?Jeśli nie chcesz używać dateutil, możesz wypróbować tę funkcję:
Test:
Wynik:
źródło
strptime
. Jest to zły pomysł, ponieważ nie uda mu się parsować daty i godziny z innym przesunięciem UTC i nie zgłosi wyjątku. Zobacz moją odpowiedź, która opisuje, w jaki sposób parsowanie RFC 3339 ze strptime jest w rzeczywistości niemożliwe.toISOString
metodą JavaScript . Ale w tej odpowiedzi nie ma wzmianki o ograniczeniu dat Zulu, ani pytanie nie wskazywało, że to wszystko, co jest potrzebne, a samo użyciedateutil
jest zwykle równie wygodne i mniej wąskie w tym, co może przeanalizować.Jeśli pracujesz z Django, udostępnia on moduł dateparse który akceptuje wiele formatów podobnych do formatu ISO, w tym strefę czasową.
Jeśli nie korzystasz z Django i nie chcesz używać żadnej z wymienionych tu bibliotek, prawdopodobnie możesz dostosować kod źródłowy Django dla dateparse do swojego projektu.
źródło
DateTimeField
używa tego podczas ustawiania wartości ciągu.Odkryłem, że ciso8601 jest najszybszym sposobem na analizowanie znaczników czasu ISO 8601. Jak sama nazwa wskazuje, jest zaimplementowany w C.
The GitHub Repo README pokazuje ich> 10x przyspieszenie w porównaniu do wszystkich innych bibliotek wymienionych w innych odpowiedzi.
Mój osobisty projekt wymagał dużej analizy ISO 8601. Miło było po prostu przełączyć rozmowę i przejść 10 razy szybciej. :)
Edycja: Od tego czasu stałem się opiekunem ciso8601. Jest teraz szybszy niż kiedykolwiek!
źródło
datetime.strptime()
jest kolejnym najszybszym rozwiązaniem. Dziękujemy za połączenie wszystkich tych informacji!datetime.strptime()
nie jest to pełna biblioteka parsująca ISO 8601. Jeśli korzystasz z języka Python 3.7, możesz użyćdatetime.fromisoformat()
metody, która jest nieco bardziej elastyczna. Być może zainteresuje Cię ta bardziej kompletna lista parserów, która wkrótce powinna zostać scalona z ciso8601 README.Działa to dla stdlib w Pythonie 3.2 i nowszych (zakładając, że wszystkie znaczniki czasu to UTC):
Na przykład,
źródło
strptime
. Jest to zły pomysł, ponieważ nie uda mu się parsować daty i godziny z innym przesunięciem UTC i nie zgłosi wyjątku. Zobacz moją odpowiedź, która opisuje, w jaki sposób parsowanie RFC 3339 ze strptime jest w rzeczywistości niemożliwe.timezone.utc
zamiasttimezone(timedelta(0))
. Również prace kodu w Pythonie 2.6+ (co najmniej), jeśli dostarczyutc
tzinfo obiektu%Z
strefy czasowej for w najnowszych wersjach Pythona.Jestem autorem narzędzi iso8601. Można go znaleźć na GitHub lub na PyPI . Oto jak możesz przeanalizować swój przykład:
źródło
Jednym prostym sposobem na konwersję łańcucha daty podobnego do ISO 8601 na znacznik czasu lub
datetime.datetime
obiekt UNIX we wszystkich obsługiwanych wersjach Pythona bez instalowania modułów innych firm jest użycie analizatora daty SQLite .Wynik:
źródło
Zakodowałem parser dla standardu ISO 8601 i umieściłem go w GitHub: https://github.com/boxed/iso8601 . Ta implementacja obsługuje wszystko w specyfikacji, z wyjątkiem czasów trwania, interwałów, interwałów okresowych i dat poza obsługiwanym zakresem dat modułu datetime modułu Python.
Testy są włączone! : P
źródło
Funkcja parse_datetime () Django obsługuje daty z przesunięciami UTC:
Można go zatem wykorzystać do analizy dat ISO 8601 w polach w ramach całego projektu:
źródło
Ponieważ ISO 8601 pozwala zasadniczo na wiele odmian opcjonalnych dwukropków i myślników
CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Jeśli chcesz użyć Strptime, musisz najpierw usunąć te odmiany.Celem jest wygenerowanie obiektu datetime utc.
Jeśli chcesz tylko podstawowy przypadek, który działa dla UTC z sufiksem Z, taki jak
2016-06-29T19:36:29.3453Z
:Jeśli chcesz obsługiwać przesunięcia stref czasowych, takie jak
2016-06-29T19:36:29.3453-0400
lub2008-09-03T20:56:35.450686+05:00
skorzystaj z poniższych. Spowodują to przekształcenie wszystkich odmian w coś bez ograniczników zmiennych, takich jak20080903T205635.450686+0500
uczynienie go bardziej spójnym / łatwiejszym do parsowania.Jeśli twój system nie obsługuje
%z
dyrektywy Strptime (widzisz coś takiegoValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
), musisz ręcznie przesunąć czas odZ
(UTC). Uwaga%z
może nie działać w systemie w wersjach języka Python <3, ponieważ zależała od obsługi biblioteki c, która różni się w zależności od typu kompilacji system / python (tj. Jython, Cython itp.).źródło
Aby uzyskać coś, co działa ze standardową biblioteką 2.X, spróbuj:
calendar.timegm to brakująca wersja gm time.mktime.
źródło
Python-dateutil zgłosi wyjątek, jeśli parsuje niepoprawne ciągi daty, więc możesz chcieć złapać wyjątek.
źródło
Obecnie jest Maya: Datetimes for Humans ™ , autor popularnego pakietu Requests: HTTP for Humans ™:
źródło
Innym sposobem jest użycie specjalistycznego parser ISO-8601 jest użycie isoparse funkcję dateutil parsera:
Wynik:
Ta funkcja jest również wspomniana w dokumentacji standardowej funkcji Python datetime.fromisoformat :
źródło
Dzięki wielkiej odpowiedzi Marka Ameryka opracowałem funkcję rozliczania wszystkich możliwych formatów ISO daty / godziny:
źródło
Zauważ, że powinniśmy sprawdzić, czy ciąg nie kończy się na
Z
, możemy przeanalizować za pomocą%z
.źródło
Początkowo próbowałem z:
Ale to nie działało na ujemnych strefach czasowych. To jednak działało dobrze w Pythonie 3.7.3:
W niektórych testach należy zauważyć, że wyjście różni się jedynie precyzją w mikrosekundach. Mam 6 cyfr precyzji na moim komputerze, ale YMMV:
źródło
frozenset(('+', '-'))
? Czy normalny krotek nie powinien('+', '-')
być w stanie osiągnąć tego samego?