Convertir datetime.date a marca de hora UTC en Python

295

Estoy tratando con fechas en Python y necesito convertirlas a marcas de tiempo UTC para usarlas dentro de Javascript. El siguiente código no funciona:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

Convertir el objeto de fecha primero en fecha y hora tampoco ayuda. Probé el ejemplo en este enlace desde, pero:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

y ahora tampoco:

mktime(utc.localize(input_date).utctimetuple())

o

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

funciona.

Pregunta general: ¿cómo puedo convertir una fecha a segundos desde la época según UTC?

Andreas Jung
fuente
posible duplicado de ¿Cómo convierto la hora local a UTC en Python?
Piotr Dobrogost el
29
No estoy seguro de estar de acuerdo con marcarlo como un duplicado. Si bien las soluciones son similares, las preguntas no lo son. Uno (este) intenta crear una marca de tiempo a partir de a datetime.date, el otro intenta convertir una representación de cadena de una zona horaria a otra. Como alguien que busca una solución a este problema, no puedo concluir que este último proporcionará la respuesta que estoy buscando.
DRH
55
datetime (date.year, date.month, date.day) .timestamp ()
Julian

Respuestas:

462

Si d = date(2011, 1, 1)está en UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

Si destá en la zona horaria local:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1y timestamp2puede diferir si la medianoche en la zona horaria local no es la misma instancia que la medianoche en UTC.

mktime()puede devolver un resultado incorrecto si dcorresponde a una hora local ambigua (p. ej., durante la transición DST) o si des una fecha pasada (futura) en la que el desplazamiento utc podría haber sido diferente y el C mktime()no tiene acceso a la base de datos tz en la plataforma dada . Puede usar el pytzmódulo (p. Ej., Vía tzlocal.get_localzone()) para obtener acceso a la base de datos tz en todas las plataformas . Además, utcfromtimestamp()puede fallar y mktime()puede devolver una marca de tiempo que no sea POSIX si "right"se utiliza la zona horaria .


Para convertir el datetime.dateobjeto que representa la fecha en UTC sin calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

¿Cómo puedo convertir una fecha a segundos desde la época según UTC?

Para convertir datetime.datetime(no datetime.date) objeto que ya representa el tiempo en UTC a la marca de tiempo POSIX correspondiente (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Nota: es necesario proporcionar timezone.utcexplícitamente, de lo contrario, .timestamp()suponga que su ingenuo objeto de fecha y hora se encuentra en la zona horaria local.

Python 3 (<3.3)

De los documentos para datetime.utcfromtimestamp():

No hay ningún método para obtener la marca de tiempo de una instancia de fecha y hora, pero la marca de tiempo POSIX correspondiente a una instancia de fecha y hora dt se puede calcular fácilmente de la siguiente manera. Para un ingenuo dt:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

Y para un dt consciente:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Lectura interesante: Hora de la época versus hora del día sobre la diferencia entre ¿Qué hora es? y ¿Cuántos segundos han transcurrido?

Ver también: datetime necesita un método de "época"

Python 2

Para adaptar el código anterior para Python 2:

timestamp = (dt - datetime(1970, 1, 1)).total_seconds()

donde timedelta.total_seconds()es equivalente a (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6calculado con verdadera división habilitada.

Ejemplo

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Cuidado con los problemas de punto flotante .

Salida

2012-01-08 15:34:10.022403
1326036850.02

Cómo convertir un datetimeobjeto consciente a la marca de tiempo POSIX

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

En Python 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

En Python 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()
jfs
fuente
2
Para el caso de Python 2, ¿por qué no timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01
77
@ m01: datetime(1970, 1, 1)es más explícito. fromtimestamp()es incorrecto aquí ( dtestá en UTC, por lo que utcfromtimestamp()debería usarse en su lugar).
jfs
1
@fedorqui mira la totimestamp()función en la respuesta.
jfs
14
Por mucho que ame Python, su lib de fecha y hora está seriamente rota.
Realfun
3
@Realfun: las zonas horarias, las transiciones DST, la base de datos tz, los segundos bisiestos existen independientemente de Python.
jfs
81

Por sólo sistemas UNIX :

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Nota 1: dizzyf observó que esto aplica zonas horarias localizadas . No utilizar en producción.

Nota 2: Jakub Narębski señaló que esto ignora la información de la zona horaria incluso para datetime con reconocimiento de desplazamiento (probado para Python 2.7).

Ulf Aslak
fuente
44
Una buena respuesta si sabe bajo qué sistema se ejecutará su código, pero es importante tener en cuenta que la cadena de formato '% s' no existe en todos los sistemas operativos, por lo que este código no es portátil. Si desea verificar si es compatible, puede verificar man strftimeen sistemas similares a Unix.
Alice Heaton
55
Cabe mencionar que esto elimina la parte de segundos fraccionales (microsegundos o lo que sea).
Alfe
11
ADVERTENCIA: ¡Esto aplica zonas horarias localizadas! Obtendrá un comportamiento de tiempo de ejecución diferente para objetos de fecha y hora idénticos dependiendo de la hora de la máquina
mareado
3
Además, esto ignora la información de la zona horaria y supone que datetime está en la zona horaria local incluso para datetime con compensación de desplazamiento , con tzinfo configurado. Bueno, al menos en Python 2.7.
Jakub Narębski
1
marca de tiempo de javascript = marca de tiempo de python * 1000
Trinh Hoang Nhu
38
  • Supuesto 1: está intentando convertir una fecha en una marca de tiempo, sin embargo, dado que una fecha cubre un período de 24 horas, no hay una sola marca de tiempo que represente esa fecha. Asumiré que desea representar la marca de tiempo de esa fecha a medianoche (00: 00: 00.000).

  • Supuesto 2: la fecha que presenta no está asociada con una zona horaria en particular, sin embargo, desea determinar el desplazamiento desde una zona horaria en particular (UTC). Sin conocer la zona horaria en la que se encuentra la fecha, no es posible calcular una marca de tiempo para una zona horaria específica. Asumiré que desea tratar la fecha como si estuviera en la zona horaria del sistema local.

Primero, puede convertir la instancia de fecha en una tupla que represente los diversos componentes de tiempo utilizando el timetuple()miembro:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

Luego puede convertir eso en una marca de tiempo usando time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Puede verificar este método probándolo con el tiempo de la época (1970-01-01), en cuyo caso la función debería devolver el desplazamiento de la zona horaria para la zona horaria local en esa fecha:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 son 8 horas, lo que sería correcto para la zona horaria del Pacífico (donde estoy).

DRH
fuente
No estoy seguro de por qué esto ha sido rechazado. Aborda el problema del OP (el código de trabajo que el OP etiquetó como "no funciona"). No responde a mi pregunta (convertir UTC datetime.datetime a timestamp), pero aún así ... votar.
Thanatos
99
Ten cuidado. La suposición de que "desea tratar la fecha como si estuviera en la zona horaria del sistema local es la raíz de todos los problemas con la zona horaria en python. A menos que esté 100% seguro de la localización de la máquina que ejecutará su script y especialmente si atiende a clientes que pueden venir de cualquier parte del mundo, SIEMPRE asuma que las fechas están en UTC y realice la conversión lo antes posible. Le mantendrá los pelos de punta, créame.
Romain G
8

siga el documento python2.7, debe usar calendar.timegm () en lugar de time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)
Wang
fuente
3
¿Cómo es diferente de mi respuesta si destá en UTC?
jfs
8

Definí mis propias dos funciones

  • utc_time2datetime (utc_time, tz = Ninguno)
  • datetime2utc_time (datetime)

aquí:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time
agitario
fuente
1
Eché un vistazo a tu ejemplo ... Muy desordenado y complicado para una cosa tan simple ... Nada pitónico.
Enojado 84
@ Mayhem: Lo limpié un poco y agregué comentarios. Si tiene una solución mejor y más genérica para convertir de vez en cuando a fecha y hora, y volver a establecer la zona horaria, háganoslo saber. También hágame saber cómo debería ser un ejemplo más pitónico de mi código.
agittarius
3

La pregunta es un poco confusa. las marcas de tiempo no son UTC, son una cosa de Unix. la fecha puede ser UTC? suponiendo que lo sea, y si está utilizando Python 3.2+, simple-date hace que esto sea trivial:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

si realmente tiene el año, mes y día, no necesita crear el date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

y si la fecha está en otra zona horaria (esto es importante porque estamos asumiendo la medianoche sin una hora asociada):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[La idea detrás de simple-date es recolectar todas las cosas de fecha y hora de Python en una clase consistente, para que pueda hacer cualquier conversión. entonces, por ejemplo, también irá a la inversa:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]

Andrew Cooke
fuente
SimpleDate(date(2011,1,1), tz='America/New_York')aumenta NoTimezone, mientras pytz.timezone('America/New_York')que no genera ninguna excepción ( simple-date==0.4.8, pytz==2014.7).
jfs
pip install simple-dateAparece un error , parece que solo es python3 / pip3.
NoBugs
3

Usando el paquete de flechas :

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)
Mathieu Longtin
fuente
nota: arrowutiliza dateutil y dateutilconoce desde hace muchos años errores relacionados con el manejo de la zona horaria. No lo use si necesita resultados correctos para zonas horarias con un desplazamiento UTC no fijo (está bien usarlo para la zona horaria UTC, pero stdlib también maneja el caso UTC).
jfs
@JFSebastian Ese error solo ocurre durante una transición, en un tiempo ambiguo.
Paul
Parece que el error fue corregido el 28 de marzo
Mathieu Longtin
Parece que es la mejor solución y cross-python. Solo usa la flecha . Gracias;)
maxkoryukov
@MathieuLongtin dateutil puede tener otros problemas
jfs
1

Una cadena de tiempo completa contiene:

  • fecha
  • hora
  • utcoffset [+HHMM or -HHMM]

Por ejemplo:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

Nota:

UNIX timestampes un número de coma flotante expresado en segundos desde la época, en UTC .


Editar:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600
kev
fuente
66
¿Y cómo responde esto a mi pregunta?
Andreas Jung
1
@ user908088 Convierte 1970-01-01 06:00:00 +0500a 3600lo que pediste.
kev
1
@ user908088 No hay timezoneen el python2, que puede hacer el cálculo ( 06:00:00- +0500= 01:00:00) manualmente.
kev
55
por qué esta respuesta en la parte superior incluso tiene -1 votos, algo mal con SO
Umair
1

Teniendo en cuenta que tiene un datetimeobjeto llamado d, use lo siguiente para obtener la marca de tiempo en UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

Y para la dirección opuesta, use lo siguiente:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Max
fuente
0

Estoy impresionado de la discusión profunda.

mis 2 centavos:

desde datetime import datetime import time

la marca de tiempo en utc es:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

o,

timestamp = time.time()

si ahora resulta de datetime.now (), en el mismo horario de verano

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
no revelado
fuente