Necesito analizar cadenas RFC 3339 como "2008-09-03T20:56:35.450686Z"
en el datetime
tipo de Python .
He encontrado strptime
en la biblioteca estándar de Python, pero no es muy conveniente.
¿Cuál es la mejor manera de hacer esto?
Necesito analizar cadenas RFC 3339 como "2008-09-03T20:56:35.450686Z"
en el datetime
tipo de Python .
He encontrado strptime
en la biblioteca estándar de Python, pero no es muy conveniente.
¿Cuál es la mejor manera de hacer esto?
Respuestas:
El paquete python-dateutil puede analizar no solo cadenas de fecha y hora RFC 3339 como la de la pregunta, sino también otras cadenas de fecha y hora ISO 8601 que no cumplen con RFC 3339 (como las que no tienen desplazamiento UTC o las que representan solo una cita).
>>> 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)
Tenga en cuenta que dateutil.parser.isoparse
es presumiblemente más estricto que el más hacky dateutil.parser.parse
, pero ambos son bastante indulgentes e intentarán interpretar la cadena que ingresa. Si desea eliminar la posibilidad de cualquier lectura errónea, debe usar algo más estricto que cualquiera de estos funciones
El nombre de Pypi es python-dateutil
, no dateutil
(gracias code3monk3y ):
pip install python-dateutil
Si está utilizando Python 3.7, echar un vistazo a esta respuesta acerca datetime.datetime.fromisoformat
.
python-dateutil
no dateutil
, por lo que: pip install python-dateutil
.
dateutil.parser
es intencionalmente hacky: intenta adivinar el formato y hace suposiciones inevitables (personalizables solo a mano) en casos ambiguos. Así que ÚNICAMENTE úselo si necesita analizar entradas de formato desconocido y está bien tolerar errores de lectura ocasionales.
La datetime
biblioteca estándar introdujo una función para invertir datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Devuelve un
datetime
correspondiente a adate_string
en uno de los formatos emitidos pordate.isoformat()
ydatetime.isoformat()
.Específicamente, esta función admite cadenas en los formatos:
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
donde
*
puede coincidir con cualquier personaje individual.Precaución : Esto no admite el análisis de cadenas arbitrarias ISO 8601; solo está pensado como la operación inversa de
datetime.isoformat()
.
Ejemplo de uso:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
puede contener a tzinfo
, y por lo tanto generar una zona horaria, pero datetime.fromisoformat()
no analiza el tzinfo? parece un error ...
isoformat
. No acepta el ejemplo en la pregunta "2008-09-03T20:56:35.450686Z"
debido al seguimiento Z
, pero sí acepta "2008-09-03T20:56:35.450686"
.
Z
script de entrada se puede modificar con date_string.replace("Z", "+00:00")
.
Tenga en cuenta que en Python 2.6+ y Py3K, el carácter% f captura microsegundos.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Ver problema aquí
strptime
es de hecho imposible.
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
así que esto funcionó
Varias respuestas aquí sugieren usar datetime.datetime.strptime
para analizar las fechas y horas RFC 3339 o ISO 8601 con zonas horarias, como la que se muestra en la pregunta:
2008-09-03T20:56:35.450686Z
Esta es una mala idea.
Suponiendo que desea admitir el formato RFC 3339 completo, incluida la compatibilidad con compensaciones UTC que no sean cero, el código que sugieren estas respuestas no funciona. De hecho, no puede funcionar, porque strptime
es imposible analizar la sintaxis RFC 3339 . Las cadenas de formato utilizadas por el módulo datetime de Python son incapaces de describir la sintaxis RFC 3339.
El problema son las compensaciones UTC. El RFC 3339 Internet Fecha / Hora Formato requiere que cada fecha-hora incluye un desplazamiento de UTC, y que esas compensaciones puede ser Z
(abreviatura de "tiempo Zulu") o en +HH:MM
o -HH:MM
formato, como +05:00
o -10:30
.
En consecuencia, todas estas son fechas válidas RFC 3339:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Por desgracia, las cadenas de formato utilizadas por strptime
y strftime
no tienen una directiva que corresponda a las compensaciones UTC en formato RFC 3339. Puede encontrar una lista completa de las directivas que admiten en https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior , y la única directiva de desplazamiento UTC incluida en la lista es %z
:
% z
Desplazamiento UTC en la forma + HHMM o -HHMM (cadena vacía si el objeto es ingenuo).
Ejemplo: (vacío), +0000, -0400, +1030
Esto no coincide con el formato de un desplazamiento RFC 3339 y, de hecho, si tratamos de usar %z
en la cadena de formato y analizar una fecha RFC 3339, fallaremos:
>>> 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'
(En realidad, lo anterior es justo lo que verá en Python 3. En Python 2 fallaremos por una razón aún más simple, que es que strptime
no implementa la %z
directiva en Python 2 ).
Las múltiples respuestas aquí que recomiendan strptime
evitarlo incluyen un literal Z
en su cadena de formato, que coincide con la Z
cadena de fecha y hora del ejemplo del autor de la pregunta (y la descarta, produciendo un datetime
objeto sin zona horaria):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Dado que esto descarta la información de zona horaria que se incluyó en la cadena de fecha y hora original, es cuestionable si deberíamos considerar incluso este resultado como correcto. Pero lo que es más importante, debido a que este enfoque implica codificar de forma rígida un desplazamiento UTC particular en la cadena de formato , se ahogará en el momento en que intente analizar cualquier fecha y hora RFC 3339 con un desplazamiento 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 esté seguro de que solo necesita admitir las fechas RFC 3339 en hora zulú, y no las que tienen otras compensaciones de zona horaria, no las use strptime
. Utilice uno de los muchos otros enfoques descritos en las respuestas aquí en su lugar.
strptime()
en Python 3.7 ahora admite todo lo que se describe como imposible en esta respuesta ('Z' literal y ':' en el desplazamiento de la zona horaria). Desafortunadamente, hay otro caso de esquina que hace que RFC 3339 sea fundamentalmente incompatible con ISO 8601, a saber, el primero permite un desplazamiento de zona horaria nulo negativo -00: 00 y el segundo no.
Pruebe el módulo iso8601 ; hace exactamente esto
Hay varias otras opciones mencionadas en la página WorkingWithTime en el wiki de python.org.
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
importar 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))
¿Cuál es el error exacto que obtienes? ¿Es como lo siguiente?
>>> 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
En caso afirmativo, puede dividir su cadena de entrada en "." Y luego agregar los microsegundos a la fecha y hora que obtuvo.
Prueba esto:
>>> 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"
, entonces debe ser un desplazamiento en horas / minutos, que se puede agregar / restar directamente del objeto de fecha y hora. se podría crear una subclase tzinfo para manejarlo, pero que probablemente no es recomendable.
A partir de Python 3.7, strptime admite delimitadores de colon en desplazamientos UTC ( fuente ). Entonces puedes usar:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
EDITAR:
Como señaló Martijn, si creó el objeto datetime usando isoformat (), simplemente puede usar datetime.fromisoformat ()
datetime.fromisoformat()
que maneja cadenas como su entrada automáticamente: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
ydatetime.isoformat()
En estos días, Arrow también se puede utilizar como una solución de terceros:
>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
Solo usa el 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
(comprobado en epochconverter.com ), ¿a menos que me falte algo?
Si no desea utilizar dateutil, puede probar esta función:
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)
Prueba:
from_utc("2007-03-04T21:08:12.123Z")
Resultado:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Esta es una mala idea porque no podrá analizar cualquier fecha y hora con un desplazamiento UTC diferente y generará una excepción. Vea mi respuesta que describe cómo analizar RFC 3339 con strptime es de hecho imposible.
toISOString
método de JavaScript . Pero en esta respuesta no se menciona la limitación de las fechas de tiempo en zulú, ni la pregunta indica que eso es todo lo que se necesita, y solo usarlo dateutil
suele ser igualmente conveniente y menos limitado en lo que puede analizar.
Si está trabajando con Django, proporciona el módulo dateparse que acepta varios formatos similares al formato ISO, incluida la zona horaria.
Si no está utilizando Django y no desea utilizar una de las otras bibliotecas mencionadas aquí, probablemente podría adaptar el código fuente de Django para dateparse a su proyecto.
DateTimeField
usa esto cuando establece un valor de cadena.
He encontrado que ciso8601 es la forma más rápida de analizar las marcas de tiempo ISO 8601. Como su nombre indica, se implementa en C.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
El archivo README de GitHub Repo muestra su aceleración> 10x en comparación con todas las otras bibliotecas enumeradas en las otras respuestas.
Mi proyecto personal implicaba mucho análisis ISO 8601. Fue agradable poder cambiar la llamada e ir 10 veces más rápido. :)
Editar: desde entonces me he convertido en un mantenedor de ciso8601. ¡Ahora es más rápido que nunca!
datetime.strptime()
es la próxima solución más rápida. ¡Gracias por reunir toda esa información!
datetime.strptime()
no es una biblioteca de análisis ISO 8601 completa. Si estás en Python 3.7, puedes usar el datetime.fromisoformat()
método, que es un poco más flexible. Es posible que le interese esta lista más completa de analizadores que deberían fusionarse pronto en el archivo README de ciso8601.
Esto funciona para stdlib en Python 3.2 en adelante (suponiendo que todas las marcas de tiempo sean UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Por ejemplo,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Esta es una mala idea porque no podrá analizar cualquier fecha y hora con un desplazamiento UTC diferente y generará una excepción. Vea mi respuesta que describe cómo analizar RFC 3339 con strptime es de hecho imposible.
timezone.utc
lugar de timezone(timedelta(0))
. Además, el código funciona en Python 2.6+ (al menos) si proporciona el utc
objeto
%Z
zona horaria for en las versiones más recientes de Python.
Una forma sencilla de convertir una cadena de fecha similar a ISO 8601 en una marca de tiempo u datetime.datetime
objeto UNIX en todas las versiones compatibles de Python sin instalar módulos de terceros es usar el analizador de fecha de 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)
Salida:
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
Codifiqué un analizador para el estándar ISO 8601 y lo puse en GitHub: https://github.com/boxed/iso8601 . Esta implementación admite todo en la especificación, excepto duraciones, intervalos, intervalos periódicos y fechas fuera del rango de fechas admitido del módulo de fecha y hora de Python.
Las pruebas están incluidas! :PAGS
La función parse_datetime () de Django admite fechas con compensaciones UTC:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Por lo tanto, podría usarse para analizar las fechas ISO 8601 en campos de todo el proyecto:
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')
Debido a que ISO 8601 permite muchas variaciones de puntos y guiones opcionales que están presentes, básicamente CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Si desea utilizar strptime, primero debe eliminar esas variaciones.
El objetivo es generar un 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
o 2008-09-03T20:56:35.450686+05:00
usar lo siguiente. Esto convertirá todas las variaciones en algo sin delimitadores variables, como 20080903T205635.450686+0500
hacerlo más consistente / más fácil de analizar.
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
directiva strptime (ve algo como ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
) entonces necesita compensar manualmente el tiempo desde Z
(UTC). Es %z
posible que Note no funcione en su sistema en las versiones de python <3, ya que depende del soporte de la biblioteca c que varía según el tipo de compilación del sistema / python (es decir, 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 funciona con la biblioteca estándar 2.X intente:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm es la versión gm faltante de time.mktime.
Python-dateutil arrojará una excepción si analiza cadenas de fechas no válidas, por lo que es posible que desee detectar la excepción.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
Hoy en día está Maya: Datetimes for Humans ™ , del autor del popular paquete Solicitudes: 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>)
Una otra forma es utilizar analizador especializado para ISO-8601 es utilizar isoparse función de dateutil analizador:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Salida:
2008-09-03 20:56:35.450686+01:00
Esta función también se menciona en la documentación de la función estándar de Python datetime.fromisoformat :
Un analizador ISO 8601 más completo, dateutil.parser.isoparse está disponible en el paquete de terceros dateutil.
Gracias a la gran respuesta de Mark Amery, ideé una función para tener en cuenta todos los posibles formatos ISO de fecha y 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)
Tenga en cuenta que debemos mirar si la cadena no termina con Z
, podríamos analizar usando %z
.
Inicialmente probé 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)
Pero eso no funcionó en zonas horarias negativas. Sin embargo, esto funcionó bien, en 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)
Algunas pruebas, tenga en cuenta que la salida solo difiere por la precisión de microsegundos. Tengo 6 dígitos de precisión en mi máquina, pero 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(('+', '-'))
? ¿No debería una tupla normal como ('+', '-')
ser capaz de lograr lo mismo?