obtener la marca de tiempo UTC en python con fecha y hora

82

¿Hay alguna forma de obtener la marca de tiempo UTC especificando la fecha? Lo que esperaría:

datetime(2008, 1, 1, 0, 0, 0, 0)

debería resultar en

 1199145600

Crear un objeto de fecha y hora ingenuo significa que no hay información de zona horaria. Si miro la documentación de datetime.utcfromtimestamp, crear una marca de tiempo UTC significa omitir la información de la zona horaria. Entonces, supongo que crear un objeto de fecha y hora ingenuo (como lo hice yo) daría como resultado una marca de tiempo UTC. Sin embargo:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

resultados en

2007-12-31 23:00:00

¿Todavía hay información de zona horaria oculta en el objeto de fecha y hora? ¿Qué estoy haciendo mal?

palmadita
fuente
el problema es then.strftime('%s')que espera la hora local pero la marca de tiempo indica que datetime(2008, 1, 1)está en UTC.
jfs

Respuestas:

90

Ingenuo datetimeversus conscientedatetime

datetimeSe dice que los objetos predeterminados son "ingenuos": mantienen la información de la hora sin la información de la zona horaria. Piense en ingenuo datetimecomo un número relativo (es decir:) +4sin un origen claro (de hecho, su origen será común en todos los límites de su sistema).

Por el contrario, piense en el conocimiento datetimecomo números absolutos (es decir:) 8con un origen común para todo el mundo.

Sin información de la zona horaria, no se puede convertir la fecha y hora "ingenua" en una representación de la hora no ingenua (¿dónde están los +4objetivos si no sabemos por dónde empezar?). Por eso no puedes tener un datetime.datetime.toutctimestamp()método. (cf: http://bugs.python.org/issue1457227 )

Para comprobar si datetime dtes ingenuo, compruebe dt.tzinfo, si None, entonces es ingenuo:

datetime.now()        ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1)  ## returns naïve datetime pointing on user given time

Tengo citas ingenuas, ¿qué puedo hacer?

Debes hacer una suposición dependiendo de tu contexto particular: La pregunta que debes hacerte es: ¿estabas datetimeen UTC? o era la hora local?

  • Si estaba usando UTC (no tiene problemas):

    import calendar
    
    def dt2ts(dt):
        """Converts a datetime object to UTC timestamp
    
        naive datetime will be considered UTC.
    
        """
    
        return calendar.timegm(dt.utctimetuple())
    
  • Si NO usaba UTC , bienvenido al infierno.

    Tienes que hacer que tu datetimeno sea ingenuo antes de usar la función anterior, devolviéndoles su zona horaria prevista.

    Necesitará el nombre de la zona horaria y la información sobre si el horario de verano estaba en vigor al producir la fecha y hora ingenua de destino (la última información sobre el horario de verano es necesaria para las esquinas):

    import pytz     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Set your timezone
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Set is_dst accordingly
    

    Consecuencias de no proporcionaris_dst :

    No usar is_dstgenerará una hora incorrecta (y una marca de tiempo UTC) si se produjo la fecha y hora objetivo mientras se implementó un DST hacia atrás (por ejemplo, cambiando el tiempo de DST eliminando una hora).

    Por is_dstsupuesto, proporcionar información incorrecta generará una hora incorrecta (y una marca de tiempo UTC) solo en la superposición o los huecos de DST. Y, al proporcionar también un tiempo incorrecto, que ocurre en "huecos" (tiempo que nunca existió debido al cambio de horario de verano hacia adelante), is_dstdará una interpretación de cómo considerar este tiempo falso, y este es el único caso en el .normalize(..)que realmente se hará algo aquí. ya que luego lo traducirá como una hora válida real (cambiando la fecha y hora Y el objeto DST si es necesario). Tenga en cuenta que .normalize()no es necesario para tener una marca de tiempo UTC correcta al final, pero probablemente se recomienda si no le gusta la idea de tener tiempos falsos en sus variables, especialmente si reutiliza esta variable en otro lugar.

    y EVITE USAR LO SIGUIENTE : (cf: Conversión de fecha y hora usando pytz )

    dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))  ## BAD !!
    

    ¿Por qué? porque .replace()reemplaza ciegamente el tzinfosin tener en cuenta el tiempo objetivo y elegirá un objeto DST incorrecto. Considerando que .localize()utiliza el tiempo objetivo y su is_dstpista para seleccionar el objeto DST correcto.

ANTIGUA respuesta incorrecta (gracias @JFSebastien por mencionar esto):

Con suerte, es bastante fácil adivinar la zona horaria (su origen local) cuando crea su datetimeobjeto ingenuo, ya que está relacionado con la configuración del sistema que, con suerte, NO cambiaría entre la creación del objeto de fecha y hora ingenuo y el momento en que desea obtener el Marca de tiempo UTC. Este truco se puede utilizar para dar una pregunta imperfecta .

Al usar time.mktimepodemos crear un utc_mktime:

def utc_mktime(utc_tuple):
    """Returns number of seconds elapsed since epoch

    Note that no timezone are taken into consideration.

    utc tuple must be: (year, month, day, hour, minute, second)

    """

    if len(utc_tuple) == 6:
        utc_tuple += (0, 0, 0)
    return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))

def datetime_to_timestamp(dt):
    """Converts a datetime object to UTC timestamp"""

    return int(utc_mktime(dt.timetuple()))

Debe asegurarse de que su datetimeobjeto se cree en la misma zona horaria que la que creó su datetime.

Esta última solución es incorrecta porque supone que el desfase UTC a partir de ahora es el mismo que el desfase UTC de EPOCH. Lo cual no es el caso de muchas zonas horarias (en un momento específico del año para las compensaciones del horario de verano (DST)).

vaab
fuente
4
El objeto de fecha y hora ingenuo siempre debe representar la hora en UTC. Otras zonas horarias deben usarse solo para E / S (pantalla). Hay un datetime.timestamp()método en Python 3.3.
jfs
2
time.mktime()debe usarse solo para la hora local. calendar.timegm()podría usarse para convertir la tupla de tiempo utc en una marca de tiempo posix. O mejor aún, use solo métodos de fecha y hora. Ver mi respuesta
jfs
4
-1. Su código asume que utc_offset (ahora) y utc_offset (época) son iguales en la zona horaria local. No es así en 116 zonas horarias (de 430 zonas horarias comunes).
jfs
1
still -1: no lo use .replace()con una zona horaria que tenga un desplazamiento utc no fijo como 'Europe/Amsterdam'. Consulte Conversión de fecha y hora con pytz .
jfs
1
1- ¿Entiendes por qué no debes usar .replace(tzinfo=get_localzone())? 2- timegm()devuelve intya. No es necesario envolverlo int. Además, .timetuple()cae fracciones de segundo.
jfs
29

Otra posibilidad es:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

Esto funciona ya que tanto "d" como "época" son fechas y horas ingenuas, lo que hace que el operador "-" sea válido y devuelve un intervalo. total_seconds()convierte el intervalo en segundos. Tenga en cuenta que total_seconds()devuelve un flotador, inclusod.microsecond == 0

Chris Cogdon
fuente
12
Bueno, en realidad no, la idea es la misma, pero esta es más fácil de entender :)
Natim
3
Uno pensaría que habría un solo método en la biblioteca de tiempo o algo ...
sheesh
21

También tenga en cuenta la función calendar.timegm () como se describe en esta entrada de blog:

import calendar
calendar.timegm(utc_timetuple)

La salida debe coincidir con la solución de vaab.

Sr. Sr.
fuente
13

Si el objeto de fecha y hora de entrada está en UTC:

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

Nota: devuelve float, es decir, los microsegundos se representan como fracciones de segundo.

Si el objeto de fecha de entrada está en UTC:

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

Vea más detalles en Conversión de datetime.date a marca de tiempo UTC en Python .

jfs
fuente
8

Siento que la respuesta principal aún no está tan clara, y vale la pena tomarse el tiempo para comprender la hora y las zonas horarias .

¡Lo más importante que hay que entender cuando se trata de tiempo es que el tiempo es relativo !

  • 2017-08-30 13:23:00: (una fecha y hora ingenua), representa una hora local en algún lugar del mundo, pero tenga 2017-08-30 13:23:00en cuenta que en Londres NO ES LA MISMA HORA que 2017-08-30 13:23:00en San Francisco.

Debido a que la misma cadena de tiempo se puede interpretar como diferentes puntos en el tiempo dependiendo de dónde se encuentre en el mundo, existe la necesidad de una noción absoluta de tiempo.

Una marca de tiempo UTC es un número en segundos (o milisegundos) de Epoch (definido como 1 January 1970 00:00:00en la GMTzona horaria +00: 00 offset).

Epoch está anclado en la zona horaria GMT y, por lo tanto, es un punto absoluto en el tiempo. Por lo tanto, una marca de tiempo UTC que es un desplazamiento de un tiempo absoluto define un punto absoluto en el tiempo .

Esto hace posible ordenar eventos en el tiempo.

Sin información de la zona horaria, el tiempo es relativo y no se puede convertir en una noción absoluta de tiempo sin proporcionar alguna indicación de a qué zona horaria se debe anclar la fecha y hora ingenua.

¿Cuáles son los tipos de tiempo utilizados en el sistema informático?

  • fecha y hora ingenua : generalmente para mostrar, en la hora local (es decir, en el navegador) donde el sistema operativo puede proporcionar información de zona horaria al programa.

  • Marcas de tiempo UTC : una marca de tiempo UTC es un punto absoluto en el tiempo, como se mencionó anteriormente, pero está anclado en una zona horaria determinada, por lo que una marca de tiempo UTC se puede convertir en una fecha y hora en cualquier zona horaria, sin embargo, no contiene información de la zona horaria. Qué significa eso? Eso significa que 1504119325 corresponde a 2017-08-30T18:55:24Z, 2017-08-30T17:55:24-0100o también 2017-08-30T10:55:24-0800. No le dice de dónde es la fecha y hora registrada. Por lo general, se usa en el lado del servidor para registrar eventos (registros, etc.) o se usa para convertir una fecha y hora consciente de la zona horaria en un punto absoluto en el tiempo y calcular las diferencias de tiempo .

  • Cadena de fecha y hora ISO-8601 : ISO-8601 es un formato estandarizado para registrar la fecha y hora con la zona horaria. (De hecho, son varios formatos, lea aquí: https://en.wikipedia.org/wiki/ISO_8601 ) Se utiliza para comunicar información de fecha y hora consciente de la zona horaria de una manera serializable entre sistemas.

¿Cuándo usar cuál? o más bien, ¿cuándo debe preocuparse por las zonas horarias?

  • Si necesita preocuparse por la hora del día , necesita información sobre la zona horaria. Un calendario o una alarma necesitan la hora del día para programar una reunión a la hora correcta para cualquier usuario del mundo. Si estos datos se guardan en un servidor, el servidor necesita saber a qué zona horaria corresponde la fecha y hora.

  • Para calcular las diferencias de tiempo entre eventos que provienen de diferentes lugares del mundo, la marca de tiempo UTC es suficiente, pero pierde la capacidad de analizar a qué hora del día ocurrieron los eventos (es decir, para análisis web, es posible que desee saber cuándo los usuarios visitan su sitio en su hora local : ¿ves más usuarios por la mañana o por la noche? No puedes averiguarlo sin la información de la hora del día.

Desplazamiento de zona horaria en una cadena de fecha :

Otro punto que es importante es que el desplazamiento de zona horaria en una cadena de fecha no es fijo . Eso significa que porque 2017-08-30T10:55:24-0800dice la compensación -0800o 8 horas atrás, ¡no significa que siempre lo será!

En el verano bien podría ser en horario de verano, y sería -0700

Lo que eso significa es que el desplazamiento de la zona horaria (+0100) no es lo mismo que el nombre de la zona horaria (Europa / Francia) o incluso la designación de la zona horaria (CET)

America/Los_AngelesLa zona horaria es un lugar del mundo , pero se convierte en una PSTnotación de compensación de zona horaria (Hora estándar del Pacífico) en invierno, yPDT (Hora de verano del Pacífico) en verano.

Por lo tanto, además de obtener el desplazamiento de la zona horaria de la cadena de fecha, también debe obtener el nombre de la zona horaria para que sea preciso.

La mayoría de los paquetes podrán convertir las compensaciones numéricas del horario de verano al horario estándar por sí mismas, pero eso no es necesariamente trivial con solo una compensación. Por ejemplo, la WATdesignación de zona horaria en África Occidental es UTC + 0100 al igual queCET zona horaria en Francia, pero Francia observa el horario de verano, mientras que África occidental no (porque están cerca del ecuador)

Entonces, en resumen, es complicado. MUY complicado, y es por eso que no debe hacerlo usted mismo, sino que confíe en un paquete que lo haga por usted y ¡MANTÉNGALO ACTUALIZADO!

MrE
fuente
vea la publicación de mi blog sobre la fecha y la hora en Python para comprender las trampas de los diferentes paquetes medium.com/@eleroy/…
MrE
3

Una solución sencilla sin utilizar módulos externos:

from datetime import datetime, timezone

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=timezone.utc).timestamp())
Mike Siomkin
fuente
1

Creo que la forma correcta de formular tu pregunta es Is there a way to get the timestamp by specifying the date in UTC? porque la marca de tiempo es solo un número absoluto, no relativo. La pieza relativa (o consciente de la zona horaria) es la fecha.

Encuentro que los pandas son muy convenientes para las marcas de tiempo, así que:

import pandas as pd
dt1 = datetime(2008, 1, 1, 0, 0, 0, 0)
ts1 = pd.Timestamp(dt1, tz='utc').timestamp()
# make sure you get back dt1
datetime.utcfromtimestamp(ts1)  
Itamar Katz
fuente
Usar pandas es en mi humilde opinión el enfoque correcto, para la hora actual también hay t = Timestamp.utcnow () para obtener directamente el momento correcto :)
ntg
0

La respuesta aceptada parece no funcionar para mí. Mi solución:

import time
utc_0 = int(time.mktime(datetime(1970, 01, 01).timetuple()))
def datetime2ts(dt):
    """Converts a datetime object to UTC timestamp"""
    return int(time.mktime(dt.utctimetuple())) - utc_0
nube soñando
fuente
falla si el dtdesplazamiento UTC actual ( ) de la zona horaria local y en 1970 difieren. mktime()espera la hora local.
jfs
0

Manera más sencilla:

>>> from datetime import datetime
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> dt.strftime("%s")
'1199163600'

Editar: @Daniel es correcto, esto lo convertiría a la zona horaria de la máquina. Aquí hay una respuesta revisada:

>>> from datetime import datetime, timezone
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> int((dt-epoch).total_seconds())
'1199145600'

De hecho, ni siquiera es necesario especificarlo timezone.utc, porque la diferencia horaria es la misma siempre que ambos datetimetengan la misma zona horaria (o ninguna).

>>> from datetime import datetime
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> int((dt-epoch).total_seconds())
1199145600
Mike Furlender
fuente
este método no devuelve utc, sino que ajusta la fecha y hora a la zona horaria actual. es decir. si ahora son las 12 am en tz +3, devolverá las 9 am en época.
Daniel Dubovski
Ah, tienes razón. Mi zona horaria era UTC, por eso funcionó.
Mike Furlender
Si se va a utilizar el (Python 3.2 y posteriores) timezone.utcobjeto, a continuación, sólo tiene que utilizar con .timestamp(): datetime(2008, 1, 1, tzinfo=timezone.utc).timestamp(). No es necesario crear un objeto de época y restar ..
Martijn Pieters