Para ser claros: ISO 8601 es el estándar principal. RFC 3339 es un autoproclamado "perfil" de ISO 8601 que hace algunas anulaciones imprudentes de las normas ISO 8601.
Basil Bourque
3
No se pierda la solución python3.7 + a continuación para invertir isoformat ()
Brad M
2
Esta pregunta no debe cerrarse como engañar a la publicación vinculada. Dado que este solicita analizar una cadena de tiempo ISO 8601 (que no era compatible de forma nativa con python pre a 3.7) y la otra es formatear un objeto de fecha y hora en una cadena de época utilizando un método obsoleto.
abccd
Respuestas:
462
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.isoparsees 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
Para los perezosos, que se instala a través python-dateutilno dateutil, por lo que: pip install python-dateutil.
cod3monk3y
29
Tenga en cuenta que dateutil.parseres 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.
ivan_pozdeev
2
Convenido. Un ejemplo es pasar una "fecha" de 9999. Esto devolverá lo mismo que datetime (9999, mes actual, día actual). No es una fecha válida en mi opinión.
timbo
1
@ivan_pozdeev ¿qué paquete recomendaría para el análisis sin adivinanzas?
bgusach
2
@ivan_pozdeev hay una actualización del módulo que lee las fechas iso8601
theEpsilon
198
Nuevo en Python 3.7+
La datetimebiblioteca estándar introdujo una función para invertir datetime.isoformat().
Eso es raro. ¿Porque a datetimepuede contener a tzinfo, y por lo tanto generar una zona horaria, pero datetime.fromisoformat()no analiza el tzinfo? parece un error ...
Hendy Irawan
20
No se pierda esa nota en la documentación, esto no acepta todas las cadenas ISO 8601 válidas, solo las generadas por 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".
Flimm
26
Para soportar adecuadamente el Zscript de entrada se puede modificar con date_string.replace("Z", "+00:00").
jox
77
Tenga en cuenta que durante segundos solo maneja exactamente 0, 3 o 6 decimales. Si los datos de entrada tienen 1, 2, 4, 5, 7 o más decimales, el análisis fallará.
Felk
1
@JDOaktown Este ejemplo utiliza la biblioteca de fecha y hora nativa de Python, no el analizador de dateutil. En realidad, fallará si los lugares decimales no son 0, 3 o 6 con este enfoque.
abccd
174
Tenga en cuenta que en Python 2.6+ y Py3K, el carácter% f captura microsegundos.
Nota: si usa fechas de fecha ingenuas, creo que no tiene TZ en absoluto, Z puede no coincidir con nada.
Danny Staple
24
Esta respuesta (en su forma actual editada) se basa en la codificación rígida de un desplazamiento UTC particular (es decir, "Z", que significa +00: 00) en la cadena de formato. 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 strptimees de hecho imposible.
Mark Amery
1
en mi caso,% f captó microsegundos en lugar de Z, datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') así que esto funcionó
ashim888,
¿Py3K significa Python 3000?!?
Robino
2
@Robino IIRC, "Python 3000" es un nombre antiguo para lo que ahora se conoce como Python 3.
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 strptimees 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:MMo -HH:MMformato, como +05:00o -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 strptimey strftimeno 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 %zen 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'
Las múltiples respuestas aquí que recomiendan strptimeevitarlo incluyen un literal Zen su cadena de formato, que coincide con la Zcadena de fecha y hora del ejemplo del autor de la pregunta (y la descarta, produciendo un datetimeobjeto sin zona horaria):
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.
Es alucinante por qué strptime no tiene una directiva para la información de zona horaria en formato ISO, y por qué no se puede analizar. Increíble.
Csaba Toth
2
@CsabaToth Totalmente de acuerdo: si tengo algo de tiempo para matar, tal vez intente agregarlo al idioma. O podría hacerlo, si estuviera tan inclinado: veo que tiene algo de experiencia en C, a diferencia de mí.
Mark Amery
1
@CsabaToth - ¿Por qué increíble? Funciona lo suficientemente bien para la mayoría de las personas, o encontraron una solución lo suficientemente fácil. Si necesita la función, es de código abierto y puede agregarla. O paga a alguien para que lo haga por ti. ¿Por qué alguien debería ofrecer su propio tiempo libre para resolver sus problemas específicos? Deja que la fuente te acompañe.
Peter M. - representa a Mónica el
2
@PeterMasiar Increíble porque generalmente uno descubre que las cosas en Python se han implementado de manera cuidadosa y completa. Esta atención al detalle nos ha echado a perder y, por lo tanto, cuando nos topamos con algo en el lenguaje que no es "propónico", tiramos nuestros juguetes del cochecito, ya que estoy a punto de hacerlo ahora. Whaaaaaaaaaa Whaa wahaaaaa :-(
Robino
2
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.
La pregunta no era "cómo analizo las fechas ISO 8601", sino "cómo analizo este formato de fecha exacto".
Nicholas Riley
3
@tiktak El OP preguntó "Necesito analizar cadenas como X" y mi respuesta a eso, después de haber probado ambas bibliotecas, es usar otra, porque iso8601 tiene problemas importantes aún abiertos. Mi participación o falta de ella en dicho proyecto no tiene relación alguna con la respuesta.
Tobia
2
Tenga en cuenta que la versión pip de iso8601 no se ha actualizado desde 2007 y tiene algunos errores graves que son sobresalientes. Recomiendo aplicar algunos críticos de los parches usted mismo o encontrar uno de los muchos tenedores github que ya lo han hecho github.com/keithhackbarth/pyiso8601-strict
keithhackbarth
66
iso8601 , también conocido como pyiso8601 , se actualizó en febrero de 2014. La última versión admite un conjunto mucho más amplio de cadenas ISO 8601. He estado usando con buenos resultados en algunos de mis proyectos.
Dave Hein
34
importar re, datetime
s = "2008-09-03T20: 56: 35.450686Z"
d = datetime.datetime (* map (int, re.split ('[^ \ d]', s) [: - 1]))
No estoy de acuerdo, esto es prácticamente ilegible y, por lo que puedo decir, no tiene en cuenta el Zulu (Z) que hace que esta fecha y hora sea ingenua a pesar de que se proporcionaron datos de zona horaria.
umbrae
14
Me parece bastante legible. De hecho, es probablemente la forma más fácil y eficiente de realizar la conversión sin instalar paquetes adicionales.
Tobia
2
Esto es equivalente a d = datetime.datetime (* map (int, re.split ('\ D', s) [: - 1])) supongo.
Xuan
44
una variación:datetime.datetime(*map(int, re.findall('\d+', s))
jfs
3
Esto da como resultado un objeto de fecha y hora ingenuo sin zona horaria, ¿verdad? ¿Entonces el bit UTC se pierde en la traducción?
w00t
32
¿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.
No puede simplemente quitar .Z porque significa zona horaria y puede ser diferente. Necesito convertir la fecha a la zona horaria UTC.
Alexander Artemenko
Un objeto simple de fecha y hora no tiene concepto de zona horaria. Si todas sus horas terminan en "Z", todas las horas que obtiene son UTC (hora zulú).
tzot
si la zona horaria no es ""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.
SingleNegationElimination
8
Además, "% f" es el especificador de microsegundos, por lo que una cadena de strptime (timezone-naive) se ve así: "% Y-% m-% dT% H:% M:% S.% f".
quodlibetor
1
Esto generará una excepción si la cadena de fecha y hora dada tiene un desplazamiento UTC distinto de "Z". No es compatible con todo el formato RFC 3339 y es una respuesta inferior a otras que manejan las compensaciones UTC correctamente.
Mark Amery
25
A partir de Python 3.7, strptime admite delimitadores de colon en desplazamientos UTC ( fuente ). Entonces puedes usar:
Pero en 3.7, que también tiene datetime.fromisoformat()que maneja cadenas como su entrada automáticamente: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00').
Martijn Pieters
2
Buen punto. Estoy de acuerdo, recomiendo usar datetime.fromisoformat()ydatetime.isoformat()
Andreas Profous
19
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())
Simplemente use python-dateutil: la flecha requiere python-dateutil.
danizen
Arrow ahora es compatible con ISO8601. Los problemas a los que se hace referencia ahora están cerrados.
Altus
18
Solo usa el 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())
¿No es esto exactamente @Flimms respuesta anterior?
leo
1
¿Dónde lo ves analizando en segundos? Encontré este artículo tratando de obtener tiempo de época, así que pensé que alguien más también lo estaría.
Blairg23
1
Esto no es UTC en mi sistema. Más bien, la salida en segundos es el tiempo de época de Unix como si la fecha estuviera en mi zona horaria local.
Elliot
1
Esta respuesta tiene errores y no debe aceptarse. Probablemente toda la pregunta debería marcarse como un duplicado de stackoverflow.com/questions/11743019/…
tripleee
@tripleee En realidad, acabo de comprobar el código y parece que devuelve la respuesta correcta: 455051100(comprobado en epochconverter.com ), ¿a menos que me falte algo?
Blairg23
13
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 typereturn datetime.datetime.strptime(utcTime, fmt)
Esta respuesta se basa en la codificación rígida de un desplazamiento UTC particular (es decir, "Z", que significa +00: 00) en la cadena de formato que se pasa 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.
Mark Amery
1
Está codificado pero es suficiente para el caso cuando solo necesita analizar zulu.
Sasha
1
@alexander yes, que puede ser el caso si, por ejemplo, sabe que su cadena de fecha se generó con el toISOStringmé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 dateutilsuele ser igualmente conveniente y menos limitado en lo que puede analizar.
Mark Amery
11
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.
¡Esto parece una gran biblioteca! Para aquellos que desean optimizar el análisis ISO8601 en Google App Engine, lamentablemente, no podemos usarlo ya que es una biblioteca C, pero sus puntos de referencia fueron perspicaces para mostrar que Native datetime.strptime()es la próxima solución más rápida. ¡Gracias por reunir toda esa información!
hamx0r
3
@ hamx0r, tenga en cuenta que 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.
movermeyer
ciso8601 funciona bastante bien, pero primero hay que hacer "pip install pytz", porque no se puede analizar una marca de tiempo con información de zona horaria sin la dependencia de pytz. El ejemplo sería: dob = ciso8601.parse_datetime (result ['dob'] ['date'])
Esta respuesta se basa en la codificación rígida de un desplazamiento UTC particular (es decir, "Z", que significa +00: 00) en la cadena de formato que se pasa 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.
Mark Amery
1
En teoría, sí, esto falla. En la práctica, nunca me he encontrado con una fecha con formato ISO 8601 que no estuviera en tiempo de zulú. Para mi necesidad muy ocasional, esto funciona muy bien y no depende de alguna biblioteca externa.
Benjamin Riggs
44
podrías usar en timezone.utclugar de timezone(timedelta(0)). Además, el código funciona en Python 2.6+ (al menos) si proporciona el utcobjeto
tzinfo
No importa si lo has encontrado, no coincide con la especificación.
locutor
Puede usar la %Zzona horaria for en las versiones más recientes de Python.
sventechie
7
Soy el autor de las utilidades iso8601. Se puede encontrar en GitHub o en PyPI . Así es como puedes analizar tu ejemplo:
Una forma sencilla de convertir una cadena de fecha similar a ISO 8601 en una marca de tiempo u datetime.datetimeobjeto 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 pythonfrom __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.123456Zis1472140886 after epoch
datetime is2016-08-2512:01:262016-08-25T16:01:29is1472140889 after epoch
datetime is2016-08-2512: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.
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.
Si solo desea un caso básico que funcione para UTC con el sufijo Z como 2016-06-29T19:36:29.3453Z:
Si desea manejar compensaciones de zona horaria como 2016-06-29T19:36:29.3453-0400o 2008-09-03T20:56:35.450686+05:00usar lo siguiente. Esto convertirá todas las variaciones en algo sin delimitadores variables, como 20080903T205635.450686+0500hacerlo 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")
Si su sistema no es compatible con la %zdirectiva 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 %zposible 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
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:
classFixedOffset(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')exceptValueError:passtry:return datetime.strptime(isodatetime,'%Y-%m-%dT%H:%M:%S')exceptValueError: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))
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).
Tenga en cuenta que
dateutil.parser.isoparse
es presumiblemente más estricto que el más hackydateutil.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 funcionesEl nombre de Pypi es
python-dateutil
, nodateutil
(gracias code3monk3y ):Si está utilizando Python 3.7, echar un vistazo a esta respuesta acerca
datetime.datetime.fromisoformat
.fuente
python-dateutil
nodateutil
, 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.Nuevo en Python 3.7+
La
datetime
biblioteca estándar introdujo una función para invertirdatetime.isoformat()
.Ejemplo de uso:
fuente
datetime
puede contener atzinfo
, y por lo tanto generar una zona horaria, perodatetime.fromisoformat()
no analiza el tzinfo? parece un error ...isoformat
. No acepta el ejemplo en la pregunta"2008-09-03T20:56:35.450686Z"
debido al seguimientoZ
, pero sí acepta"2008-09-03T20:56:35.450686"
.Z
script de entrada se puede modificar condate_string.replace("Z", "+00:00")
.Tenga en cuenta que en Python 2.6+ y Py3K, el carácter% f captura microsegundos.
Ver problema aquí
fuente
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: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
ystrftime
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
: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:(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 literalZ
en su cadena de formato, que coincide con laZ
cadena de fecha y hora del ejemplo del autor de la pregunta (y la descarta, produciendo undatetime
objeto sin zona horaria):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:
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.fuente
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.
fuente
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
fuente
datetime.datetime(*map(int, re.findall('\d+', s))
¿Cuál es el error exacto que obtienes? ¿Es como lo siguiente?
En caso afirmativo, puede dividir su cadena de entrada en "." Y luego agregar los microsegundos a la fecha y hora que obtuvo.
Prueba esto:
fuente
""
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:
EDITAR:
Como señaló Martijn, si creó el objeto datetime usando isoformat (), simplemente puede usar datetime.fromisoformat ()
fuente
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:
fuente
Solo usa el
python-dateutil
módulo:Documentación
fuente
455051100
(comprobado en epochconverter.com ), ¿a menos que me falte algo?Si no desea utilizar dateutil, puede probar esta función:
Prueba:
Resultado:
fuente
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 usarlodateutil
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.
fuente
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.
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!
fuente
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 eldatetime.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):
Por ejemplo,
fuente
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 detimezone(timedelta(0))
. Además, el código funciona en Python 2.6+ (al menos) si proporciona elutc
objeto%Z
zona horaria for en las versiones más recientes de Python.Soy el autor de las utilidades iso8601. Se puede encontrar en GitHub o en PyPI . Así es como puedes analizar tu ejemplo:
fuente
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 .Salida:
fuente
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
fuente
La función parse_datetime () de Django admite fechas con compensaciones UTC:
Por lo tanto, podría usarse para analizar las fechas ISO 8601 en campos de todo el proyecto:
fuente
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.
Si solo desea un caso básico que funcione para UTC con el sufijo Z como
2016-06-29T19:36:29.3453Z
:Si desea manejar compensaciones de zona horaria como
2016-06-29T19:36:29.3453-0400
o2008-09-03T20:56:35.450686+05:00
usar lo siguiente. Esto convertirá todas las variaciones en algo sin delimitadores variables, como20080903T205635.450686+0500
hacerlo más consistente / más fácil de analizar.Si su sistema no es compatible con la
%z
directiva strptime (ve algo comoValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
) entonces necesita compensar manualmente el tiempo desdeZ
(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.).fuente
Para algo que funciona con la biblioteca estándar 2.X intente:
calendar.timegm es la versión gm faltante de time.mktime.
fuente
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.
fuente
Hoy en día está Maya: Datetimes for Humans ™ , del autor del popular paquete Solicitudes: HTTP for Humans ™:
fuente
Una otra forma es utilizar analizador especializado para ISO-8601 es utilizar isoparse función de dateutil analizador:
Salida:
Esta función también se menciona en la documentación de la función estándar de Python datetime.fromisoformat :
fuente
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:
fuente
Tenga en cuenta que debemos mirar si la cadena no termina con
Z
, podríamos analizar usando%z
.fuente
Inicialmente probé con:
Pero eso no funcionó en zonas horarias negativas. Sin embargo, esto funcionó bien, en Python 3.7.3:
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:
fuente
frozenset(('+', '-'))
? ¿No debería una tupla normal como('+', '-')
ser capaz de lograr lo mismo?