Ho bisogno di analizzare le stringhe RFC 3339 come "2008-09-03T20:56:35.450686Z"
nel datetime
tipo di Python .
Ho trovato strptime
nella libreria standard di Python, ma non è molto conveniente.
Qual è il modo migliore per farlo?
Ho bisogno di analizzare le stringhe RFC 3339 come "2008-09-03T20:56:35.450686Z"
nel datetime
tipo di Python .
Ho trovato strptime
nella libreria standard di Python, ma non è molto conveniente.
Qual è il modo migliore per farlo?
Risposte:
Il pacchetto python-dateutil può analizzare non solo stringhe datetime RFC 3339 come quella nella domanda, ma anche altre stringhe di data e ora ISO 8601 che non sono conformi a RFC 3339 (come quelle senza offset UTC o che rappresentano solo una data).
>>> 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)
Si noti che dateutil.parser.isoparse
è presumibilmente più rigoroso di quello più confuso dateutil.parser.parse
, ma entrambi sono abbastanza indulgenti e tenteranno di interpretare la stringa in cui si passa. Se si desidera eliminare la possibilità di eventuali fraintendimenti, è necessario utilizzare qualcosa di più rigoroso di uno di questi funzioni.
Il nome Pypi è python-dateutil
, non dateutil
(grazie code3monk3y ):
pip install python-dateutil
Se stai usando Python 3.7, dare un'occhiata a questa risposta su datetime.datetime.fromisoformat
.
python-dateutil
non dateutil
, in modo da: pip install python-dateutil
.
dateutil.parser
è intenzionalmente confuso: cerca di indovinare il formato e fa ipotesi inevitabili (personalizzabili solo a mano) in casi ambigui. Quindi utilizzalo SOLO se devi analizzare input di formato sconosciuto e va bene tollerare errori di lettura occasionali.
La datetime
libreria standard ha introdotto una funzione per l'inversione datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Restituisce un
datetime
corrispondente adate_string
in uno dei formati emessi dadate.isoformat()
edatetime.isoformat()
.In particolare, questa funzione supporta le stringhe nei formati:
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
dove
*
può abbinare qualsiasi singolo personaggio.Attenzione : questo non supporta l'analisi di stringhe ISO 8601 arbitrarie - è inteso solo come operazione inversa di
datetime.isoformat()
.
Esempio di utilizzo:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
può contenere a tzinfo
e quindi generare un fuso orario, ma datetime.fromisoformat()
non analizza tzinfo? sembra un insetto ...
isoformat
. Non accetta l'esempio nella domanda a "2008-09-03T20:56:35.450686Z"
causa del trascinamento Z
, ma accetta "2008-09-03T20:56:35.450686"
.
Z
script di input può essere modificato con date_string.replace("Z", "+00:00")
.
Nota in Python 2.6+ e Py3K, il carattere% f cattura i microsecondi.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Vedi il problema qui
strptime
sia di fatto impossibile.
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
quindi questo ha funzionato
Diverse risposte qui suggeriscono di utilizzare datetime.datetime.strptime
per analizzare i dati RFC 3339 o ISO 8601 con fusi orari, come quello mostrato nella domanda:
2008-09-03T20:56:35.450686Z
Questa è una cattiva idea.
Supponendo che si desideri supportare l'intero formato RFC 3339, incluso il supporto per offset UTC diversi da zero, il codice suggerito da queste risposte non funziona. In effetti, non può funzionare perché analizza la sintassi RFC 3339 usandostrptime
è possibile. Le stringhe di formato utilizzate dal modulo datetime di Python non sono in grado di descrivere la sintassi RFC 3339.
Il problema sono gli offset UTC. Il formato data / ora Internet RFC 3339 richiede che ogni data e ora includa un offset UTC e che tali offset possano essere Z
(abbreviazione di "ora Zulu") o in +HH:MM
o -HH:MM
formato, come +05:00
o -10:30
.
Di conseguenza, si tratta di tutti i periodi di dati RFC 3339 validi:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Purtroppo, le stringhe di formato utilizzate da strptime
e strftime
non hanno una direttiva corrispondente agli offset UTC nel formato RFC 3339. Un elenco completo delle direttive che supportano è disponibile all'indirizzo https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior e l'unica direttiva offset UTC inclusa nell'elenco è %z
:
% z
Offset UTC nel formato + HHMM o -HHMM (stringa vuota se l'oggetto è ingenuo).
Esempio: (vuoto), +0000, -0400, +1030
Questo non corrisponde al formato di un offset RFC 3339, e infatti se proviamo a utilizzare %z
nella stringa di formato e analizziamo una data RFC 3339, falliremo:
>>> 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'
(In realtà, quanto sopra è proprio quello che vedrai in Python 3. In Python 2 falliremo per un motivo ancora più semplice, ovvero che strptime
non implementa affatto la %z
direttiva in Python 2 )
Le risposte multiple qui che raccomandano di strptime
aggirare tutto ciò includendo un letterale Z
nella loro stringa di formato, che corrisponde alla Z
stringa datetime di esempio del richiedente (e la scarta, producendo un datetime
oggetto senza un fuso orario):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Poiché ciò elimina le informazioni sul fuso orario incluse nella stringa datetime originale, è lecito chiedersi se anche questo risultato debba essere considerato corretto. Ma, cosa ancora più importante, poiché questo approccio prevede la codifica hardware di un determinato offset UTC nella stringa di formato , si bloccherà nel momento in cui tenta di analizzare qualsiasi datetime RFC 3339 con un offset UTC diverso:
>>> 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'
A meno che tu non sia sicuro di dover solo supportare i tempi di trasmissione dei dati RFC 3339 in ora Zulu e non quelli con altri offset del fuso orario, non utilizzarli strptime
. Usa invece uno dei molti altri approcci descritti nelle risposte qui.
strptime()
in Python 3.7 ora supporta tutto ciò che è descritto come impossibile in questa risposta ('Z' letterale e ':' nell'offset del fuso orario). Sfortunatamente, c'è un altro caso angolare che rende RFC 3339 fondamentalmente incompatibile con ISO 8601, vale a dire, il primo consente un offset del fuso orario nullo negativo -00: 00 e il successivo no.
Prova il modulo iso8601 ; fa esattamente questo.
Ci sono molte altre opzioni menzionate nella pagina WorkingWithTime sul wiki di python.org.
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))
Qual è l'errore esatto che ricevi? È come il seguente?
>>> 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
Se sì, puoi dividere la stringa di input su ".", Quindi aggiungere i microsecondi al datetime ottenuto.
Prova questo:
>>> 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)
""
o "Z"
, allora deve essere un offset in ore / minuti, che può essere aggiunto / sottratto direttamente dall'oggetto datetime. è possibile creare una sottoclasse tzinfo per gestirla, ma probabilmente non è consigliabile.
A partire da Python 3.7, strptime supporta i delimitatori dei due punti negli offset UTC ( sorgente ). Quindi puoi quindi usare:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
MODIFICARE:
Come sottolineato da Martijn, se hai creato l'oggetto datetime usando isoformat (), puoi semplicemente usare datetime.fromisoformat ()
datetime.fromisoformat()
che gestisce le stringhe come il vostro input automaticamente: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
edatetime.isoformat()
In questi giorni, Arrow può anche essere usato come soluzione di terze parti:
>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
Basta usare il python-dateutil
modulo:
>>> 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
(controllato su epochconverter.com ) ,,, a meno che non mi manchi qualcosa?
Se non vuoi usare dateutil, puoi provare questa funzione:
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)
Test:
from_utc("2007-03-04T21:08:12.123Z")
Risultato:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Questa è una cattiva idea perché non analizzerà alcun datetime con un diverso offset UTC e genererà un'eccezione. Vedi la mia risposta che descrive come analizzare RFC 3339 con strptime sia di fatto impossibile.
toISOString
metodo JavaScript . Ma in questa risposta non si fa menzione della limitazione alle date di tempo di Zulu, né la domanda indica che è tutto ciò che serve, e il solo utilizzo dateutil
è di solito altrettanto conveniente e meno stretto in ciò che può analizzare.
Se lavori con Django, fornisce il modulo dateparse che accetta un sacco di formati simili al formato ISO, incluso il fuso orario.
Se non stai usando Django e non vuoi usare una delle altre librerie menzionate qui, probabilmente potresti adattare il codice sorgente di Django per dateparse al tuo progetto.
DateTimeField
usa quando si imposta un valore di stringa.
Ho trovato ciso8601 il modo più veloce per analizzare i timestamp ISO 8601. Come suggerisce il nome, è implementato in C.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
Il README di GitHub Repo mostra il loro speedup> 10x rispetto a tutte le altre librerie elencate nelle altre risposte.
Il mio progetto personale prevedeva un sacco di analisi ISO 8601. È stato bello poter semplicemente cambiare chiamata e andare 10 volte più veloce. :)
Modifica: da allora sono diventato un manutentore di ciso8601. Ora è più veloce che mai!
datetime.strptime()
è la soluzione più veloce successiva. Grazie per aver raccolto tutte queste informazioni!
datetime.strptime()
non è una libreria di analisi ISO 8601 completa. Se usi Python 3.7, puoi usare il datetime.fromisoformat()
metodo, che è un po 'più flessibile. Potresti essere interessato a questo elenco più completo di parser che dovrebbe essere unito al README ciso8601 al più presto.
Funziona con stdlib da Python 3.2 in poi (supponendo che tutti i timestamp siano UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Per esempio,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Questa è una cattiva idea perché non analizzerà alcun datetime con un diverso offset UTC e genererà un'eccezione. Vedi la mia risposta che descrive come analizzare RFC 3339 con strptime sia di fatto impossibile.
timezone.utc
posto di timezone(timedelta(0))
. Inoltre, il codice funziona in Python 2.6+ (almeno) se si fornisce l' utc
oggetto tzinfo
%Z
per il fuso orario nelle versioni più recenti di Python.
Un modo semplice per convertire una stringa di data simile a ISO 8601 in un timestamp o datetime.datetime
oggetto UNIX in tutte le versioni di Python supportate senza installare moduli di terze parti è utilizzare il parser di date di 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)
Produzione:
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
Ho codificato un parser per lo standard ISO 8601 e l'ho messo su GitHub: https://github.com/boxed/iso8601 . Questa implementazione supporta tutto nelle specifiche tranne durate, intervalli, intervalli periodici e date al di fuori dell'intervallo di date supportato del modulo datetime di Python.
I test sono inclusi! : P
La funzione parse_datetime () di Django supporta le date con offset UTC:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Quindi potrebbe essere utilizzato per l'analisi delle date ISO 8601 in campi all'interno dell'intero progetto:
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')
Perché ISO 8601 consente in sostanza molte varianti di due punti e trattini opzionali CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Se vuoi usare strptime, devi prima eliminare quelle variazioni.
L'obiettivo è generare un oggetto datetime utc.
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
o 2008-09-03T20:56:35.450686+05:00
utilizzare quanto segue. Questi convertiranno tutte le variazioni in qualcosa senza delimitatori variabili come 20080903T205635.450686+0500
renderlo più coerente / più facile da analizzare.
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
direttiva strptime (vedi qualcosa di simile ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
), devi compensare manualmente l'ora da Z
(UTC). Nota %z
potrebbe non funzionare sul tuo sistema nelle versioni di Python <3 poiché dipendeva dal supporto della libreria c che varia a seconda del tipo di build di sistema / python (es. Jython, Cython, ecc.).
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
Per qualcosa che funziona con la libreria standard 2.X prova:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm è la versione gm mancante di time.mktime.
Python-dateutil genererà un'eccezione se analizza stringhe di date non valide, quindi potresti voler catturare l'eccezione.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
Oggi c'è Maya: Datetimes for Humans ™ , dall'autore del popolare pacchetto Requests: HTTP for Humans ™:
>>> 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>)
Un altro modo è quello di usare parser specializzata per ISO-8601 è quello di utilizzare isoparse funzione dateutil parser:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Produzione:
2008-09-03 20:56:35.450686+01:00
Questa funzione è anche menzionata nella documentazione per la funzione standard Python datetime.fromisoformat :
Un parser ISO 8601 più completo, dateutil.parser.isoparse è disponibile nel pacchetto di terze parti dateutil.
Grazie alla grande risposta di Mark Amery, ho ideato la funzione per tenere conto di tutti i possibili formati ISO di datetime:
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)
Nota che dovremmo cercare se la stringa non termina con Z
, potremmo analizzare usando %z
.
Inizialmente ho provato con:
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)
Ma questo non ha funzionato su fusi orari negativi. Questo comunque ho funzionato bene, in Python 3.7.3:
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)
Alcuni test, notano che l'uscita differisce solo per precisione dei microsecondi. Sono arrivato a 6 cifre di precisione sulla mia macchina, ma 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(('+', '-'))
? Una tupla normale non dovrebbe ('+', '-')
essere in grado di realizzare la stessa cosa?