Eu preciso analisar as seqüências de caracteres RFC 3339 como "2008-09-03T20:56:35.450686Z"no datetimetipo de Python .
Eu encontrei strptimena 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 datetimetipo de Python .
Eu encontrei strptimena 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-dateutilnão dateutil, então: pip install python-dateutil.
dateutil.parserintencionalmente é 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 datetimebiblioteca padrão introduziu uma função para inversão datetime.isoformat().
classmethoddatetime.fromisoformat(date_string):Retorne a
datetimecorrespondente a adate_stringem 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')
datetimepode 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".
Zscript 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.strptimepara 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:MMou -HH:MMformato, como +05:00ou -10:30.
Conseqüentemente, essas são todas as datas válidas da RFC 3339:
2008-09-03T20:56:35.450686Z2008-09-03T20:56:35.450686+05:002008-09-03T20:56:35.450686-10:30Infelizmente, as seqüências de caracteres de formato usadas strptimee strftimenã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 %za 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 strptimenão implementa a %zdiretiva no Python 2. )
As várias respostas aqui que recomendam strptimetoda a solução para isso, incluindo um literal Zem sua sequência de formatação, que corresponde à Zsequência de data / hora do exemplo do solicitante da pergunta (e a descarta, produzindo um datetimeobjeto 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-dateutilmó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.
toISOStringmé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.
DateTimeFieldusa 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.utcvez de timezone(timedelta(0)). Além disso, o código funciona em Python 2.6+ (pelo menos) se você fornecer utctzinfo objeto
%Zfuso 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.datetimeobjeto 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-0400ou 2008-09-03T20:56:35.450686+05:00use o seguinte. Isso converterá todas as variações em algo sem delimitadores de variáveis, como 20080903T205635.450686+0500torná-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" )
%zdiretiva 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 %zpode 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?