Ich muss RFC 3339- Zeichenfolgen wie "2008-09-03T20:56:35.450686Z"
in Pythons datetime
Typ analysieren .
Ich habe strptime
in der Python-Standardbibliothek gefunden, aber es ist nicht sehr praktisch.
Was ist der beste Weg, dies zu tun?
Ich muss RFC 3339- Zeichenfolgen wie "2008-09-03T20:56:35.450686Z"
in Pythons datetime
Typ analysieren .
Ich habe strptime
in der Python-Standardbibliothek gefunden, aber es ist nicht sehr praktisch.
Was ist der beste Weg, dies zu tun?
Antworten:
Das Python-Dateutil- Paket kann nicht nur RFC 3339-Datums- / Uhrzeitzeichenfolgen wie die betreffende analysieren, sondern auch andere ISO 8601- Datums- und Zeitzeichenfolgen, die nicht RFC 3339 entsprechen (z. B. solche ohne UTC-Offset oder solche, die repräsentieren nur ein Datum).
>>> 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)
Beachten Sie, dass dies dateutil.parser.isoparse
vermutlich strenger ist als das Hacky dateutil.parser.parse
, aber beide sind ziemlich verzeihend und versuchen, die von Ihnen übergebene Zeichenfolge zu interpretieren. Wenn Sie die Möglichkeit von Fehlinterpretationen ausschließen möchten, müssen Sie etwas Strengeres als beide verwenden Funktionen.
Der Pypi-Name lautet python-dateutil
nicht dateutil
(danke code3monk3y ):
pip install python-dateutil
Wenn Sie Python 3.7 verwenden, sehen Sie sich diese Antwort an datetime.datetime.fromisoformat
.
python-dateutil
nicht installiert dateutil
, also : pip install python-dateutil
.
dateutil.parser
absichtlich hackig ist: Es versucht, das Format zu erraten und macht in mehrdeutigen Fällen unvermeidliche Annahmen (nur von Hand anpassbar). Verwenden Sie es daher NUR, wenn Sie Eingaben mit unbekanntem Format analysieren müssen und gelegentliche Fehlinterpretationen tolerieren können.
Die datetime
Standardbibliothek führte eine Funktion zum Invertieren ein datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
::Geben Sie a zurück
datetime
, das adate_string
in einem der vondate.isoformat()
und ausgegebenen Formate entsprichtdatetime.isoformat()
.Insbesondere unterstützt diese Funktion Zeichenfolgen in den folgenden Formaten:
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
wo
*
kann mit jedem einzelnen Zeichen übereinstimmen.Achtung : Dies unterstützt nicht das Parsen beliebiger ISO 8601-Zeichenfolgen - es ist nur als inverse Operation von gedacht
datetime.isoformat()
.
Anwendungsbeispiel:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
möglicherweise a enthält tzinfo
und somit eine Zeitzone ausgibt, aber datetime.fromisoformat()
das tzinfo nicht analysiert? scheint wie ein Fehler ..
isoformat
. Das Beispiel in der Frage wird "2008-09-03T20:56:35.450686Z"
aufgrund des Nachlaufs Z
nicht akzeptiert, aber akzeptiert "2008-09-03T20:56:35.450686"
.
Z
das Eingabeskript richtig zu unterstützen, kann es mit geändert werden date_string.replace("Z", "+00:00")
.
Beachten Sie, dass in Python 2.6+ und Py3K das Zeichen% f Mikrosekunden abfängt.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Siehe Ausgabe hier
strptime
tatsächlich unmöglich ist.
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
also hat dies den Trick
Mehrere Antworten hier empfehlen , mit datetime.datetime.strptime
der Frage zeigt RFC 3339 oder ISO 8601 Datetimes mit Zeitzonen, wie das analysieren:
2008-09-03T20:56:35.450686Z
Das ist eine schlechte Idee.
Angenommen, Sie möchten das vollständige RFC 3339-Format unterstützen, einschließlich der Unterstützung anderer UTC-Offsets als Null, dann funktioniert der Code, den diese Antworten vorschlagen, nicht. In der Tat kann es nicht funktionieren, weil das Parsen der RFC 3339-Syntax mitstrptime
möglich ist. Die vom datetime-Modul von Python verwendeten Formatzeichenfolgen können die RFC 3339-Syntax nicht beschreiben.
Das Problem sind UTC-Offsets. Das Internet- Datums- / Zeitformat nach RFC 3339 erfordert, dass jede Datums- und Uhrzeit einen UTC-Versatz enthält und dass diese Versätze entweder Z
(kurz für "Zulu-Zeit") oder in +HH:MM
oder im -HH:MM
Format wie +05:00
oder sein können -10:30
.
Folglich sind dies alles gültige RFC 3339-Datenzeiten:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Leider haben die von strptime
und verwendeten strftime
Formatzeichenfolgen keine Direktive, die UTC-Offsets im RFC 3339-Format entspricht. Eine vollständige Liste der von ihnen unterstützten Direktiven finden Sie unter https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior . Die einzige in der Liste enthaltene UTC-Offset-Direktive lautet %z
:
% z
UTC-Offset in der Form + HHMM oder -HHMM (leere Zeichenfolge, wenn das Objekt naiv ist).
Beispiel: (leer), +0000, -0400, +1030
Dies stimmt nicht mit dem Format eines RFC 3339-Offsets überein. Wenn wir versuchen, die Formatzeichenfolge zu verwenden %z
und ein RFC 3339-Datum zu analysieren, schlagen wir fehl:
>>> 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'
(Tatsächlich ist das Obige genau das, was Sie in Python 3 sehen werden. In Python 2 werden wir aus einem noch einfacheren Grund scheitern, nämlich dass strptime
die %z
Direktive in Python 2 überhaupt nicht implementiert wird .)
Die mehrfachen Antworten hier, die strptime
alle empfehlen , umgehen dies, indem sie ein Literal Z
in ihre Z
Formatzeichenfolge aufnehmen , das mit der datetime-Zeichenfolge des Fragestellers übereinstimmt (und diese verwirft und ein datetime
Objekt ohne Zeitzone erzeugt):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Da hierdurch Zeitzoneninformationen verworfen werden, die in der ursprünglichen datetime-Zeichenfolge enthalten waren, ist es fraglich, ob wir auch dieses Ergebnis als korrekt betrachten sollten. Da dieser Ansatz jedoch die Hardcodierung eines bestimmten UTC-Offsets in die Formatzeichenfolge umfasst, wird er erstickt, sobald versucht wird, eine RFC 3339-Datumszeit mit einem anderen UTC-Offset zu analysieren:
>>> 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'
Verwenden Sie diese Option nicht, es sei denn, Sie sind sicher, dass Sie nur RFC 3339-Datumszeiten in Zulu-Zeit unterstützen müssen, und nicht solche mit anderen Zeitzonen-Offsets strptime
. Verwenden Sie stattdessen einen der vielen anderen in den Antworten beschriebenen Ansätze.
strptime()
in Python 3.7 unterstützt jetzt alles, was in dieser Antwort als unmöglich beschrieben wird ('Z'-Literal und': 'im Zeitzonenversatz). Leider gibt es einen anderen Eckfall, der RFC 3339 grundsätzlich mit ISO 8601 inkompatibel macht, nämlich erstere erlaubt einen negativen Null-Zeitzonenversatz von -00: 00 und letztere nicht.
Probieren Sie das iso8601- Modul aus. es macht genau das.
Auf der WorkingWithTime- Seite im python.org-Wiki werden mehrere andere Optionen erwähnt .
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
import re, datetime s = "2008-09-03T20: 56: 35.450686Z" d = datetime.datetime (* map (int, re.split ('[^ \ d]', s) [: - 1]))
datetime.datetime(*map(int, re.findall('\d+', s))
Was ist der genaue Fehler, den Sie erhalten? Ist es wie folgt?
>>> 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
Wenn ja, können Sie Ihre Eingabezeichenfolge auf "." Teilen und dann die Mikrosekunden zu der Datumszeit hinzufügen, die Sie erhalten haben.
Versuche dies:
>>> def gt(dt_str):
dt, _, us= dt_str.partition(".")
dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
us= int(us.rstrip("Z"), 10)
return dt + datetime.timedelta(microseconds=us)
>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)
""
oder ist "Z"
, muss es sich um einen Versatz in Stunden / Minuten handeln, der direkt zum Datum / Uhrzeit-Objekt hinzugefügt / von diesem subtrahiert werden kann. Sie könnten eine tzinfo-Unterklasse erstellen, um damit umzugehen, aber das wird wahrscheinlich nicht empfohlen.
Ab Python 3.7 unterstützt strptime Doppelpunktbegrenzer in UTC-Offsets ( Quelle ). So können Sie dann verwenden:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
BEARBEITEN:
Wie Martijn betont hat, können Sie, wenn Sie das datetime-Objekt mit isoformat () erstellt haben, einfach datetime.fromisoformat () verwenden.
datetime.fromisoformat()
welche Zeichenfolgen wie Ihre Eingabe automatisch behandelt werden : datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
unddatetime.isoformat()
In diesen Tagen kann Arrow auch als Lösung von Drittanbietern verwendet werden:
>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
Verwenden Sie einfach das python-dateutil
Modul:
>>> 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())
455051100
(überprüft bei epochconverter.com ) ,,, es sei denn, ich vermisse etwas?
Wenn Sie dateutil nicht verwenden möchten, können Sie diese Funktion ausprobieren:
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 type
return datetime.datetime.strptime(utcTime, fmt)
Prüfung:
from_utc("2007-03-04T21:08:12.123Z")
Ergebnis:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Dies ist eine schlechte Idee, da keine Datums- und Uhrzeitangabe mit einem anderen UTC-Offset analysiert und eine Ausnahme ausgelöst werden kann. Siehe meine Antwort , die beschreibt, wie das Parsen von RFC 3339 mit strptime tatsächlich unmöglich ist.
toISOString
Methode generiert wurde . In dieser Antwort wird jedoch weder die Beschränkung auf Zulu-Zeitdaten erwähnt, noch wurde in der Frage darauf hingewiesen, dass dies alles ist, was benötigt wird, und die bloße Verwendung dateutil
ist in der Regel gleichermaßen praktisch und weniger eng in der Analyse.
Wenn Sie mit Django arbeiten, wird das Dateparse-Modul bereitgestellt , das eine Reihe von Formaten akzeptiert, die dem ISO-Format ähnlich sind, einschließlich der Zeitzone.
Wenn Sie Django nicht verwenden und keine der anderen hier genannten Bibliotheken verwenden möchten, können Sie wahrscheinlich den Django-Quellcode für dateparse an Ihr Projekt anpassen .
DateTimeField
verwendet dies, wenn Sie einen Zeichenfolgenwert festlegen.
Ich habe festgestellt, dass ciso8601 der schnellste Weg ist, ISO 8601-Zeitstempel zu analysieren. Wie der Name schon sagt, ist es in C implementiert.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
Die GitHub Repo README zeigt ihre> 10-fache Beschleunigung im Vergleich zu allen anderen Bibliotheken, die in den anderen Antworten aufgeführt sind.
Mein persönliches Projekt beinhaltete viel ISO 8601-Parsing. Es war schön, einfach den Anruf umschalten und 10x schneller gehen zu können. :) :)
Bearbeiten: Ich bin seitdem ein Betreuer von ciso8601 geworden. Es ist jetzt schneller als je zuvor!
datetime.strptime()
die nächstschnellste Lösung ist. Vielen Dank, dass Sie all diese Informationen zusammengestellt haben!
datetime.strptime()
es sich nicht um eine vollständige ISO 8601-Analysebibliothek handelt. Wenn Sie mit Python 3.7 arbeiten, können Sie datetime.fromisoformat()
die etwas flexiblere Methode verwenden. Diese vollständigere Liste von Parsern könnte Sie interessieren, die bald in die cISo8601 README integriert werden sollten.
Dies funktioniert für stdlib ab Python 3.2 (vorausgesetzt, alle Zeitstempel sind UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Zum Beispiel,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Dies ist eine schlechte Idee, da keine Datums- und Uhrzeitangabe mit einem anderen UTC-Offset analysiert und eine Ausnahme ausgelöst werden kann. Siehe meine Antwort , die beschreibt, wie das Parsen von RFC 3339 mit strptime tatsächlich unmöglich ist.
timezone.utc
anstelle von verwenden timezone(timedelta(0))
. Auch der Code funktioniert in Python 2.6+ (zumindest) , wenn Sie liefern utc
Tzinfo Objekt
%Z
for-Zeitzone in den neuesten Versionen von Python verwenden.
Ich bin der Autor von iso8601 utils. Es kann auf GitHub oder auf PyPI gefunden werden . So können Sie Ihr Beispiel analysieren:
>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Eine einfache Möglichkeit, eine ISO 8601-ähnliche Datumszeichenfolge datetime.datetime
in allen unterstützten Python-Versionen in einen UNIX-Zeitstempel oder ein UNIX- Objekt zu konvertieren, ohne Module von Drittanbietern zu installieren, ist die Verwendung des Datums-Parsers von SQLite .
#!/usr/bin/env python
from __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)
Ausgabe:
2016-08-25T16:01:26.123456Z is 1472140886 after epoch
datetime is 2016-08-25 12:01:26
2016-08-25T16:01:29 is 1472140889 after epoch
datetime is 2016-08-25 12:01:29
Ich habe einen Parser für den ISO 8601-Standard codiert und auf GitHub gestellt: https://github.com/boxed/iso8601 . Diese Implementierung unterstützt alles in der Spezifikation mit Ausnahme von Dauer, Intervallen, periodischen Intervallen und Daten außerhalb des unterstützten Datumsbereichs des Python-Datums- / Uhrzeitmoduls.
Tests sind enthalten! : P.
Die Funktion parse_datetime () von Django unterstützt Datumsangaben mit UTC-Offsets:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Es kann also zum Parsen von ISO 8601-Daten in Feldern innerhalb des gesamten Projekts verwendet werden:
from django.utils import formats
from django.forms.fields import DateTimeField
from django.utils.dateparse import parse_datetime
class DateTimeFieldFixed(DateTimeField):
def strptime(self, value, format):
if format == 'iso-8601':
return parse_datetime(value)
return super().strptime(value, format)
DateTimeField.strptime = DateTimeFieldFixed.strptime
formats.ISO_INPUT_FORMATS['DATETIME_INPUT_FORMATS'].insert(0, 'iso-8601')
Da ISO 8601 grundsätzlich viele Variationen optionaler Doppelpunkte und Bindestriche zulässt CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Wenn Sie strptime verwenden möchten, müssen Sie diese Variationen zuerst entfernen.
Ziel ist es, ein utc datetime-Objekt zu generieren.
2016-06-29T19:36:29.3453Z
:
datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")
2016-06-29T19:36:29.3453-0400
oder 2008-09-03T20:56:35.450686+05:00
verwenden möchten . Diese konvertieren alle Variationen in etwas ohne variable Trennzeichen, wie z. B. 20080903T205635.450686+0500
eine konsistentere / einfachere Analyse.
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" )
%z
strptime-Direktive nicht unterstützt (Sie sehen so etwas wie ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
), müssen Sie die Zeit von Z
(UTC) manuell versetzen . Hinweis %z
funktioniert möglicherweise nicht auf Ihrem System in Python-Versionen <3, da dies von der Unterstützung der c-Bibliothek abhängt, die je nach System- / Python-Build-Typ (z. B. Jython, Cython usw.) unterschiedlich ist.
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
Für etwas, das mit der 2.X-Standardbibliothek funktioniert, versuchen Sie:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm ist die fehlende gm-Version von time.mktime.
Das Python-Dateutil löst eine Ausnahme aus, wenn ungültige Datumszeichenfolgen analysiert werden. Daher möchten Sie möglicherweise die Ausnahme abfangen.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
Heutzutage gibt es Maya: Datetimes for Humans ™ vom Autor des beliebten Requests: HTTP for Humans ™ -Pakets:
>>> import maya
>>> str = '2008-09-03T20:56:35.450686Z'
>>> maya.MayaDT.from_rfc3339(str).datetime()
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=<UTC>)
Eine andere Möglichkeit, einen speziellen Parser für ISO-8601 zu verwenden, ist die Verwendung der Isoparse- Funktion des Dateutil-Parsers:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Ausgabe:
2008-09-03 20:56:35.450686+01:00
Diese Funktion wird auch in der Dokumentation zur Standard-Python-Funktion datetime.fromisoformat erwähnt :
Dateutil.parser.isoparse, ein umfassenderer ISO 8601-Parser, ist im Paket dateutil eines Drittanbieters verfügbar.
Dank der großartigen Antwort von Mark Amery habe ich eine Funktion entwickelt, die alle möglichen ISO-Formate von datetime berücksichtigt:
class FixedOffset(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')
except ValueError:
pass
try:
return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S')
except ValueError:
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))
def parseISO8601DateTime(datetimeStr):
import time
from datetime import datetime, timedelta
def log_date_string(when):
gmt = time.gmtime(when)
if time.daylight and gmt[8]:
tz = time.altzone
else:
tz = time.timezone
if tz > 0:
neg = 1
else:
neg = 0
tz = -tz
h, rem = divmod(tz, 3600)
m, rem = divmod(rem, 60)
if neg:
offset = '-%02d%02d' % (h, m)
else:
offset = '+%02d%02d' % (h, m)
return time.strftime('%d/%b/%Y:%H:%M:%S ', gmt) + offset
dt = datetime.strptime(datetimeStr, '%Y-%m-%dT%H:%M:%S.%fZ')
timestamp = dt.timestamp()
return dt + timedelta(hours=dt.hour-time.gmtime(timestamp).tm_hour)
Beachten Sie, dass wir prüfen sollten, ob der String nicht mit endet Z
, wir könnten ihn mit analysieren %z
.
Anfangs habe ich versucht mit:
from operator import neg, pos
from time import strptime, mktime
from datetime import datetime, tzinfo, timedelta
class MyUTCOffsetTimezone(tzinfo):
@staticmethod
def with_offset(offset_no_signal, signal): # type: (str, str) -> MyUTCOffsetTimezone
return MyUTCOffsetTimezone((pos if signal == '+' else neg)(
(datetime.strptime(offset_no_signal, '%H:%M') - datetime(1900, 1, 1))
.total_seconds()))
def __init__(self, offset, name=None):
self.offset = timedelta(seconds=offset)
self.name = name or self.__class__.__name__
def utcoffset(self, dt):
return self.offset
def tzname(self, dt):
return self.name
def dst(self, dt):
return timedelta(0)
def to_datetime_tz(dt): # type: (str) -> datetime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
if dt[-6] in frozenset(('+', '-')):
dt, sign, offset = strptime(dt[:-6], fmt), dt[-6], dt[-5:]
return datetime.fromtimestamp(mktime(dt),
tz=MyUTCOffsetTimezone.with_offset(offset, sign))
elif dt[-1] == 'Z':
return datetime.strptime(dt, fmt + 'Z')
return datetime.strptime(dt, fmt)
Bei negativen Zeitzonen funktionierte das jedoch nicht. Dies funktionierte jedoch in Python 3.7.3 einwandfrei:
from datetime import datetime
def to_datetime_tz(dt): # type: (str) -> datetime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
if dt[-6] in frozenset(('+', '-')):
return datetime.strptime(dt, fmt + '%z')
elif dt[-1] == 'Z':
return datetime.strptime(dt, fmt + 'Z')
return datetime.strptime(dt, fmt)
Bei einigen Tests ist zu beachten, dass sich der Ausgang nur durch die Genauigkeit von Mikrosekunden unterscheidet. Ich habe 6 Stellen Genauigkeit auf meiner Maschine, aber YMMV:
for dt_in, dt_out in (
('2019-03-11T08:00:00.000Z', '2019-03-11T08:00:00'),
('2019-03-11T08:00:00.000+11:00', '2019-03-11T08:00:00+11:00'),
('2019-03-11T08:00:00.000-11:00', '2019-03-11T08:00:00-11:00')
):
isoformat = to_datetime_tz(dt_in).isoformat()
assert isoformat == dt_out, '{} != {}'.format(isoformat, dt_out)
frozenset(('+', '-'))
? Sollte ein normales Tupel ('+', '-')
nicht in der Lage sein, dasselbe zu erreichen?