Tiempo ISO (ISO 8601) en Python


340

Tengo un archivo En Python, me gustaría tomar su tiempo de creación y convertirlo a una cadena de tiempo ISO (ISO 8601) mientras preservo el hecho de que se creó en la Zona Horaria del Este (ET) .

¿Cómo tomo el ctime del archivo y lo convierto en una cadena de tiempo ISO que indica la zona horaria del este (y tiene en cuenta el horario de verano, si es necesario)?



11
@ Nick- Enlace útil, pero no un duplicado- está preguntando sobre la conversión a la inversa.
Yarin

1
@ Joseph- Acabo de preguntar esto con respecto a RFC 3999- debería funcionar para esto - Generar marca de tiempo RFC 3339 en Python
Yarin

Respuestas:


560

Local según ISO 8601:

import datetime
datetime.datetime.now().isoformat()
>>> 2020-03-20T14:28:23.382748

UTC a ISO 8601:

import datetime
datetime.datetime.utcnow().isoformat()
>>> 2020-03-20T01:30:08.180856

Local a ISO 8601 sin microsegundo:

import datetime
datetime.datetime.now().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:30:43

UTC a ISO 8601 con información de TimeZone (Python 3):

import datetime
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()
>>> 2020-03-20T01:31:12.467113+00:00

UTC a ISO 8601 con información de zona horaria local sin microsegundos (Python 3):

import datetime
datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:31:43+13:00

Local a ISO 8601 con información de TimeZone (Python 3):

import datetime
datetime.datetime.now().astimezone().isoformat()
>>> 2020-03-20T14:32:16.458361+13:00

Observe que hay un error cuando se usa astimezone()en tiempo utc. Esto da un resultado incorrecto:

datetime.datetime.utcnow().astimezone().isoformat() #Incorrect result

Para Python 2, vea y use pytz .


17
.isoformat()puede tomar un parámetro separador, (en caso de que desee algo diferente a 'T'):.isoformat(' ')
ThorSummoner

55
@ThorSummoner es bueno saberlo! Pero tenga cuidado de que cambiar el separador ya no cumpla con ISO-8601 ... lo que tiene poco sentido (además, hay mejores formas de imprimir fechas, pero esa no era la cuestión aquí). RFC
estani

44
Se permite espacio como parte del estándar ISO-8601. It is permitted to omit the 'T' character by mutual agreement.
raychi

44
@raychi Siempre se permite cambiar un estándar por acuerdo mutuo (que en muchos casos romperá el estándar, pero a quién le importa si es un acuerdo mutuo, ¿verdad?). Mi 2c: simplemente no, déjalo como está.
estani

14
Esta respuesta es incorrecta porque datetime.datetime.now (). Isoformat () devuelve una cadena local ISO 8601 sin etiquetarla como local. Debe tener una fecha y hora que represente la zona horaria local para que isoformato haga lo correcto.
Sam Hartman

66

Esto es lo que uso para convertir al formato de fecha y hora XSD:

from datetime import datetime
datetime.now().replace(microsecond=0).isoformat()
# You get your ISO string

Encontré esta pregunta cuando buscaba el formato de fecha y hora XSD ( xs:dateTime). Necesitaba eliminar los microsegundos de isoformat.


44
¿Qué es "XSD" (en este contexto)?
Peter Mortensen

1
Creo que se refiere xs:datea las hojas de estilo XSD / XML.
sventechie

1
Significaxs:dateTime
radtek el

56

Representación de tiempo ISO 8601

El estándar internacional ISO 8601 describe una representación de cadena para fechas y horas. Dos ejemplos simples de este formato son

2010-12-16 17:22:15
20101216T172215

(que representan el 16 de diciembre de 2010), pero el formato también permite tiempos de resolución inferiores a un segundo y especificar zonas horarias. Por supuesto, este formato no es específico de Python, pero es bueno para almacenar fechas y horas en un formato portátil. Los detalles sobre este formato se pueden encontrar en la entrada Markus Kuhn .

Recomiendo el uso de este formato para almacenar tiempos en archivos.

Una forma de obtener el tiempo actual en esta representación es usar strftime del módulo de tiempo en la biblioteca estándar de Python:

>>> from time import strftime
>>> strftime("%Y-%m-%d %H:%M:%S")
'2010-03-03 21:16:45'

Puede usar el constructor strptime de la clase datetime:

>>> from datetime import datetime
>>> datetime.strptime("2010-06-04 21:08:12", "%Y-%m-%d %H:%M:%S")
datetime.datetime(2010, 6, 4, 21, 8, 12)

El más robusto es el módulo Egenix mxDateTime:

>>> from mx.DateTime.ISO import ParseDateTimeUTC
>>> from datetime import datetime
>>> x = ParseDateTimeUTC("2010-06-04 21:08:12")
>>> datetime.fromtimestamp(x)
datetime.datetime(2010, 3, 6, 21, 8, 12)

Referencias


2
¿En qué sentido es "más robusto"?
Stéphane

Fecha:2018-05-25
loxaxs

Fecha y hora combinadas en UTC:2018-05-25T12:16:14+00:00
loxaxs

2
Fecha y hora combinadas en UTC:2018-05-25T12:16:14Z
loxaxs

Fecha y hora combinadas en UTC:20180525T121614Z
loxaxs

49

Encontré el datetime.isoformat en la documentación . Parece hacer lo que quieres:

datetime.isoformat([sep])

Return a string representing the date and time in ISO 8601 format, YYYY-MM-DDTHH:MM:SS.mmmmmm or, if microsecond is 0, YYYY-MM-DDTHH:MM:SS

If utcoffset() does not return None, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or, if microsecond is 0 YYYY-MM-DDTHH:MM:SS+HH:MM

The optional argument sep (default 'T') is a one-character separator, placed between the date and time portions of the result. For example,
>>>

>>> from datetime import tzinfo, timedelta, datetime
>>> class TZ(tzinfo):
...     def utcoffset(self, dt): return timedelta(minutes=-399)
...
>>> datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ')
'2002-12-25 00:00:00-06:39'

30

ISO 8601 permite una representación compacta sin separadores, excepto el T, por lo que me gusta usar esta línea para obtener una cadena de marca de tiempo rápida:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.%fZ")
'20180905T140903.591680Z'

Si no necesita los microsegundos, simplemente omita la .%fparte:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
'20180905T140903Z'

Para hora local:

>>> datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
'20180905T140903'

Editar:

Después de leer esto un poco más, le recomiendo que deje la puntuación en. RFC 3339 recomienda ese estilo porque si todos usan la puntuación, no hay riesgo de que varias cadenas ISO 8601 se ordenen en grupos según su puntuación. Entonces, el único revestimiento para una cadena compatible sería:

>>> datetime.datetime.now().strftime("%Y-%m-%dT%H:%M%SZ")
'2018-09-05T14:09:03Z'

3
¿Hay alguna manera de limitar los dígitos de milisegundos a 3? es decir, mi otra aplicación Javascript producirá 2019-02-23T04:02:04.051Zconnew Date().toISOString()
Miranda

19

El formato de hora ISO 8601 no almacena un nombre de zona horaria, solo se conserva el desplazamiento UTC correspondiente.

Para convertir un archivo ctime en una cadena de tiempo ISO 8601 mientras se preserva el desplazamiento UTC en Python 3:

>>> import os
>>> from datetime import datetime, timezone
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, timezone.utc)
>>> dt.astimezone().isoformat()
'2015-11-27T00:29:06.839600-05:00'

El código asume que su zona horaria local es Zona Horaria del Este (ET) y que su sistema proporciona un desplazamiento UTC correcto para la marca de tiempo POSIX dada ( ts), es decir, Python tiene acceso a una base de datos histórica de zonas horarias en su sistema o la zona horaria tenía el mismas reglas en una fecha dada.

Si necesita una solución portátil; use el pytzmódulo que proporciona acceso a la base de datos tz :

>>> import os
>>> from datetime import datetime
>>> import pytz  # pip install pytz
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, pytz.timezone('America/New_York'))
>>> dt.isoformat()
'2015-11-27T00:29:06.839600-05:00'

El resultado es el mismo en este caso.

Si necesita el nombre / abreviatura / identificación de zona horaria, almacénelo por separado.

>>> dt.astimezone().strftime('%Y-%m-%d %H:%M:%S%z (%Z)')
'2015-11-27 00:29:06-0500 (EST)'

Nota: no, :en el desplazamiento UTC y la ESTabreviatura de zona horaria no forma parte del formato de hora ISO 8601. No es unico.

Diferentes bibliotecas / diferentes versiones de la misma biblioteca pueden usar diferentes reglas de zona horaria para la misma fecha / zona horaria. Si es una fecha futura, entonces las reglas pueden ser desconocidas todavía. En otras palabras, la misma hora UTC puede corresponder a una hora local diferente dependiendo de las reglas que use: guardar una hora en formato ISO 8601 conserva la hora UTC y la hora local que corresponde a las reglas de zona horaria actuales en uso en su plataforma . Es posible que deba volver a calcular la hora local en una plataforma diferente si tiene reglas diferentes.


14

Tendrá que usar os.statpara obtener el tiempo de creación del archivo y una combinación de time.strftimey time.timezonepara formatear:

>>> import time
>>> import os
>>> t = os.stat('C:/Path/To/File.txt').st_ctime
>>> t = time.localtime(t)
>>> formatted = time.strftime('%Y-%m-%d %H:%M:%S', t)
>>> tz = str.format('{0:+06.2f}', float(time.timezone) / 3600)
>>> final = formatted + tz
>>> 
>>> final
'2008-11-24 14:46:08-02.00'

¿La zona horaria representa el horario de verano? Parece que tz será igual independientemente del horario de verano o no.
Joseph Turian el

2
Será cualquier compensación que esté actualmente vigente. Si DST está activo, tendrá un desplazamiento diferente de cuando DST no lo está.
Max Shawabkeh el

4

Corrígeme si estoy equivocado (no lo estoy), pero el desplazamiento de UTC cambia con el horario de verano. Entonces deberías usar

tz = str.format('{0:+06.2f}', float(time.altzone) / 3600)

También creo que el signo debería ser diferente:

tz = str.format('{0:+06.2f}', -float(time.altzone) / 3600)

Podría estar equivocado, pero no lo creo.


2

Estoy de acuerdo con Jarek, y además noto que el carácter separador de desplazamiento ISO es dos puntos, así que creo que la respuesta final debería ser:

isodate.datetime_isoformat(datetime.datetime.now()) + str.format('{0:+06.2f}', -float(time.timezone) / 3600).replace('.', ':')

2

Agregar una pequeña variación a la excelente respuesta de estani

Local a ISO 8601 con TimeZone y sin información de microsegundos (Python 3):

import datetime, time

utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone
utc_offset = datetime.timedelta(seconds=-utc_offset_sec)
datetime.datetime.now().replace(microsecond=0, tzinfo=datetime.timezone(offset=utc_offset)).isoformat()

Salida de muestra:

'2019-11-06T12:12:06-08:00'

Probado que esta salida puede ser analizada por Javascript Datey C # DateTime/DateTimeOffset


1

He desarrollado esta función:

def iso_8601_format(dt):
    """YYYY-MM-DDThh:mm:ssTZD (1997-07-16T19:20:30-03:00)"""

    if dt is None:
        return ""

    fmt_datetime = dt.strftime('%Y-%m-%dT%H:%M:%S')
    tz = dt.utcoffset()
    if tz is None:
        fmt_timezone = "+00:00"
    else:
        fmt_timezone = str.format('{0:+06.2f}', float(tz.total_seconds() / 3600))

    return fmt_datetime + fmt_timezone

-6
import datetime, time    
def convert_enddate_to_seconds(self, ts):
    """Takes ISO 8601 format(string) and converts into epoch time."""
     dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+\
                datetime.timedelta(hours=int(ts[-5:-3]),
                minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
    seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
    return seconds 

>>> import datetime, time
>>> ts = '2012-09-30T15:31:50.262-08:00'
>>> dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+ datetime.timedelta(hours=int(ts[-5:-3]), minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
>>> seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
>>> seconds
1348990310.26
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.