Eu preciso analisar as seqüências de caracteres RFC 3339 como "2008-09-03T20:56:35.450686Z"
no datetime
tipo de Python .
Eu encontrei strptime
na biblioteca padrão do Python, mas não é muito conveniente.
Qual é a melhor maneira de fazer isso?
Eu preciso analisar as seqüências de caracteres RFC 3339 como "2008-09-03T20:56:35.450686Z"
no datetime
tipo de Python .
Eu encontrei strptime
na biblioteca padrão do Python, mas não é muito conveniente.
Qual é a melhor maneira de fazer isso?
Respostas:
O pacote python-dateutil pode analisar não apenas as seqüências de data e hora RFC 3339 como a da pergunta, mas também outras seqüências de data e hora ISO 8601 que não estão em conformidade com a RFC 3339 (como aquelas sem deslocamento UTC ou que representam apenas uma 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)
Observe que dateutil.parser.isoparse
é presumivelmente mais rigoroso do que o mais hacky dateutil.parser.parse
, mas ambos são bastante tolerantes e tentarão interpretar a string que você passa. Se você deseja eliminar a possibilidade de erros de leitura, precisará usar algo mais rigoroso do que qualquer um deles. funções.
O nome Pypi é python-dateutil
, não dateutil
(obrigado code3monk3y ):
pip install python-dateutil
Se você estiver usando Python 3.7, ter um olhar para esta resposta sobre datetime.datetime.fromisoformat
.
python-dateutil
não dateutil
, então: pip install python-dateutil
.
dateutil.parser
intencionalmente é hacky: ele tenta adivinhar o formato e cria suposições inevitáveis (personalizáveis apenas à mão) em casos ambíguos. Portanto, APENAS use-o se você precisar analisar entradas de formato desconhecido e estiver bem para tolerar erros de leitura ocasionais.
A datetime
biblioteca padrão introduziu uma função para inversão datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Retorne a
datetime
correspondente a adate_string
em um dos formatos emitidos pordate.isoformat()
edatetime.isoformat()
.Especificamente, esta função suporta seqüências de caracteres no (s) formato (s):
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
onde
*
pode corresponder a qualquer caractere único.Cuidado : Isso não suporta a análise de cadeias arbitrárias da ISO 8601 - destina-se apenas à operação inversa de
datetime.isoformat()
.
Exemplo de uso:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
pode conter ae tzinfo
, assim, datetime.fromisoformat()
gerar um fuso horário, mas não analisa o tzinfo? parece ser um bug ..
isoformat
. Ele não aceita o exemplo da pergunta "2008-09-03T20:56:35.450686Z"
por causa do rastreamento Z
, mas aceita "2008-09-03T20:56:35.450686"
.
Z
script de entrada, pode ser modificado com date_string.replace("Z", "+00:00")
.
Observe no Python 2.6+ e Py3K, o caractere% f captura microssegundos.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Veja a edição aqui
strptime
é de fato impossível.
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
de modo que este fez o truque
Várias respostas aqui sugerem o uso datetime.datetime.strptime
para analisar as datas RFC 3339 ou ISO 8601 com fusos horários, como o exibido na pergunta:
2008-09-03T20:56:35.450686Z
Esta é uma má ideia.
Supondo que você queira oferecer suporte ao formato RFC 3339 completo, incluindo suporte a compensações UTC diferentes de zero, o código sugerido por essas respostas não funcionará. Na verdade, ele não pode funcionar, porque strptime
é impossível analisar a sintaxe da RFC 3339 . As seqüências de formato usadas pelo módulo de data e hora do Python são incapazes de descrever a sintaxe do RFC 3339.
O problema são compensações de UTC. O RFC 3339 Internet Data / Time Format requer que cada data-hora inclui um UTC offset, e que estas compensações tanto pode ser Z
(abreviação de "tempo de Zulu") ou +HH:MM
ou -HH:MM
formato, como +05:00
ou -10:30
.
Conseqüentemente, essas são todas as datas válidas da RFC 3339:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Infelizmente, as seqüências de caracteres de formato usadas strptime
e strftime
não possuem nenhuma diretiva que corresponda às compensações do UTC no formato RFC 3339. Uma lista completa das diretivas suportadas pode ser encontrada em https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior , e a única diretiva de deslocamento UTC incluída na lista é %z
:
% z
Deslocamento UTC no formato + HHMM ou -HHMM (sequência vazia se o objeto for ingênuo).
Exemplo: (vazio), +0000, -0400, +1030
Isso não corresponde ao formato de um deslocamento da RFC 3339 e, de fato, se tentarmos usar %z
a string de formato e analisar uma data da RFC 3339, ocorreremos falhas:
>>> 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'
(Na verdade, o que foi mostrado acima é exatamente o que você verá no Python 3. No Python 2, falharemos por uma razão ainda mais simples, que é que strptime
não implementa a %z
diretiva no Python 2. )
As várias respostas aqui que recomendam strptime
toda a solução para isso, incluindo um literal Z
em sua sequência de formatação, que corresponde à Z
sequência de data / hora do exemplo do solicitante da pergunta (e a descarta, produzindo um datetime
objeto sem um fuso horário):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Como isso descarta as informações do fuso horário incluídas na cadeia de data e hora original, é questionável se devemos considerar mesmo esse resultado correto. Porém, o mais importante é que, como essa abordagem envolve a codificação embutida de um deslocamento UTC específico na cadeia de caracteres de formato , ela sufocará o momento em que tentar analisar qualquer data e hora da RFC 3339 com um deslocamento UTC diferente:
>>> 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 menos que você tenha certeza de que precisa apenas suportar dados RFC 3339 no horário Zulu, e não aqueles com outros desvios de fuso horário, não use strptime
. Use uma das muitas outras abordagens descritas nas respostas aqui.
strptime()
no Python 3.7 agora suporta tudo descrito como impossível nesta resposta (literal 'Z' e ':' no deslocamento do fuso horário). Infelizmente, há outro caso de esquina que torna o RFC 3339 fundamentalmente incompatível com a ISO 8601, a saber, o primeiro permite um deslocamento de fuso horário nulo negativo -00: 00 e o posterior não.
Experimente o módulo iso8601 ; faz exatamente isso.
Existem várias outras opções mencionadas na página WorkingWithTime no wiki do 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 é o erro exato que você recebe? É como o seguinte?
>>> 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 sim, você pode dividir sua sequência de entrada em "." E adicionar os microssegundos à data e hora que você obteve.
Tente o seguinte:
>>> 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)
""
ou "Z"
, deverá ser um deslocamento em horas / minutos, que pode ser diretamente adicionado / subtraído do objeto datetime. você pode criar uma subclasse tzinfo para lidar com isso, mas isso provavelmente não é recomendado.
A partir do Python 3.7, o strptime suporta delimitadores de dois pontos nos desvios do UTC ( origem ). Então você pode usar:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
EDITAR:
Conforme apontado por Martijn, se você criou o objeto datetime usando isoformat (), pode simplesmente usar datetime.fromisoformat ()
datetime.fromisoformat()
que manipula strings como sua entrada automaticamente datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
edatetime.isoformat()
Atualmente, o Arrow também pode ser usado como uma solução de terceiros:
>>> 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 usar o python-dateutil
módulo:
>>> 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
(verificado no epochconverter.com ) ,,, a menos que esteja faltando alguma coisa?
Se você não quiser usar o dateutil, tente esta função:
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)
Teste:
from_utc("2007-03-04T21:08:12.123Z")
Resultado:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Essa é uma péssima idéia, pois falhará na análise de qualquer data e hora com um deslocamento UTC diferente e gerará uma exceção. Veja minha resposta que descreve como analisar o RFC 3339 com strptime é de fato impossível.
toISOString
método JavaScript . Mas não há menção à limitação das datas do horário do Zulu nesta resposta, nem a pergunta indica que é tudo o que é necessário, e o uso dateutil
é geralmente igualmente conveniente e menos restrito no que ele pode analisar.
Se você estiver trabalhando com o Django, ele fornece o módulo dateparse que aceita vários formatos semelhantes ao formato ISO, incluindo o fuso horário.
Se você não estiver usando o Django e não quiser usar uma das outras bibliotecas mencionadas aqui, provavelmente poderá adaptar o código-fonte do Django para dateparse no seu projeto.
DateTimeField
usa isso quando você define um valor de string.
Eu descobri que o ciso8601 é a maneira mais rápida de analisar os carimbos de data / hora ISO 8601. Como o nome sugere, ele é implementado em C.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
O README do GitHub Repo mostra sua aceleração> 10x em relação a todas as outras bibliotecas listadas nas outras respostas.
Meu projeto pessoal envolveu muita análise da ISO 8601. Foi bom poder mudar a chamada e ir 10 vezes mais rápido. :)
Edit: Desde então, me tornei um mantenedor do ciso8601. Agora está mais rápido do que nunca!
datetime.strptime()
é a próxima solução mais rápida. Obrigado por reunir todas essas informações!
datetime.strptime()
não é uma biblioteca de análise ISO 8601 completa. Se você estiver no Python 3.7, poderá usar o datetime.fromisoformat()
método, que é um pouco mais flexível. Você pode estar interessado nesta lista mais completa de analisadores que deve ser mesclada no README do ciso8601 em breve.
Isso funciona para o stdlib no Python 3.2 em diante (assumindo que todos os timestamps são UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Por exemplo,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Essa é uma péssima idéia, pois falhará na análise de qualquer data e hora com um deslocamento UTC diferente e gerará uma exceção. Veja minha resposta que descreve como analisar o RFC 3339 com strptime é de fato impossível.
timezone.utc
vez de timezone(timedelta(0))
. Além disso, o código funciona em Python 2.6+ (pelo menos) se você fornecer utc
tzinfo objeto
%Z
fuso horário for nas versões mais recentes do Python.
Uma maneira simples de converter uma sequência de datas do tipo ISO 8601 em um carimbo de data / hora UNIX ou datetime.datetime
objeto em todas as versões suportadas do Python sem instalar módulos de terceiros é usar o analisador de datas do 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)
Resultado:
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
Codifiquei um analisador para o padrão ISO 8601 e o coloquei no GitHub: https://github.com/boxed/iso8601 . Essa implementação suporta tudo na especificação, exceto por durações, intervalos, intervalos periódicos e datas fora do período suportado do módulo de data e hora do Python.
Os testes estão incluídos! : P
A função parse_datetime () do Django suporta datas com deslocamentos UTC:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Portanto, poderia ser usado para analisar datas ISO 8601 em campos de todo o projeto:
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')
Como a ISO 8601 permite que muitas variações de dois pontos e hífens opcionais estejam presentes, basicamente CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Se você deseja usar o strptime, é necessário eliminar essas variações primeiro.
O objetivo é gerar um objeto utc datetime.
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
ou 2008-09-03T20:56:35.450686+05:00
use o seguinte. Isso converterá todas as variações em algo sem delimitadores de variáveis, como 20080903T205635.450686+0500
torná-lo mais consistente / mais fácil de analisar.
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
diretiva strptime (você vê algo assim ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
), será necessário compensar manualmente a hora Z
(UTC). A nota %z
pode não funcionar em seu sistema nas versões python <3, pois depende do suporte da biblioteca c, que varia de acordo com o tipo de compilação do sistema / python (por exemplo, Jython, Cython, etc.).
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
Para algo que funcione com a biblioteca padrão 2.X, tente:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm é a versão gm ausente do time.mktime.
O python-dateutil lançará uma exceção ao analisar cadeias de datas inválidas, portanto, você pode capturar a exceção.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
Atualmente, existe o Maya: Datetimes for Humans ™ , do autor do popular pacote 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>)
Uma outra maneira é usar o analisador especializado para ISO-8601 é usar a função isoparse do analisador dateutil:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Resultado:
2008-09-03 20:56:35.450686+01:00
Essa função também é mencionada na documentação da função Python padrão datetime.fromisoformat :
Um analisador ISO 8601 com mais recursos, dateutil.parser.isoparse está disponível no pacote de terceiros dateutil.
Graças à excelente resposta de Mark Amery, criei a função para explicar todos os possíveis formatos ISO de data e hora:
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)
Note que devemos procurar se a string não terminar Z
, poderíamos analisar usando %z
.
Inicialmente, tentei com:
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)
Mas isso não funcionou em fusos horários negativos. No entanto, isso funcionou bem, no 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)
Alguns testes, observe que a saída difere apenas pela precisão de microssegundos. Tenho 6 dígitos de precisão na minha máquina, mas 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(('+', '-'))
? Uma tupla normal não deveria ('+', '-')
ser capaz de realizar a mesma coisa?