Convierta datetime a la marca de tiempo de Unix y vuelva a convertirlo en python

186

Tengo dt = datetime(2013,9,1,11), y me gustaría obtener una marca de tiempo Unix de este objeto de fecha y hora.

Cuando lo hago (dt - datetime(1970,1,1)).total_seconds(), tengo la marca de tiempo 1378033200.

Al convertirlo de nuevo usando datetime.fromtimestamptengo datetime.datetime(2013, 9, 1, 6, 0).

La hora no coincide. ¿Qué extrañé aquí?

puente de agua
fuente
1
Como nota al margen, si está utilizando Python 3.3+, realmente desea utilizar el timestampmétodo en lugar de intentar hacerlo usted mismo. Por un lado, eso evita por completo la posibilidad de restar tiempos ingenuos de diferentes zonas horarias.
abarnert
1
De donde dtviene ¿Es la hora local o la hora en UTC?
jfs
2
@abarnert También me gustaría enviar una solicitud para purgar todas las páginas de códigos y símbolos Unicode, es decir, para prohibir todos los idiomas de páginas de códigos que no sean ascii y no predeterminados. Todavía se permiten pinturas de símbolos.
Daniel F
66
@DanielF: solicitud denegada. Aquí no estoy interesado en crear un idioma de un solo mundo, e incluso si lo hiciéramos, no quisiera hacer la vida imposible para lingüistas históricos y académicos de Tolkien. Unicode ya resuelve el problema, excepto que ciertas organizaciones y productos (el consorcio TRON, el software japonés que usa Shift-JIS sobre UTF-8, Microsoft aún entrega un sistema operativo que por defecto es cp1252 para archivos de texto de usuario y varios SDK que pretenden que UTF -16 es un juego de caracteres de ancho fijo y / o lo mismo que Unicode) necesita ser castigado para alinearlos.
abarnert

Respuestas:

105

Lo que te perdiste aquí son las zonas horarias.

Presumiblemente tiene cinco horas fuera de UTC, así que 2013-09-01T11: 00: 00 local y 2013-09-01T06: 00: 00Z son la misma hora.

Debe leer la parte superior de los datetimedocumentos, que explican las zonas horarias y los objetos "ingenuos" y "conscientes".

Si su fecha y hora ingenua original era UTC, la forma de recuperarla es usarla en utcfromtimestamplugar de fromtimestamp.

Por otro lado, si su fecha y hora ingenua original era local, no debería haberle sustraído una marca de tiempo UTC en primer lugar; utilizar datetime.fromtimestamp(0)en su lugar.

O, si tenía un objeto de fecha y hora consciente, debe usar una época local (consciente) en ambos lados o convertirlo explícitamente desde y hacia UTC.

Si tiene, o puede actualizar a, Python 3.3 o posterior, puede evitar todos estos problemas simplemente usando el timestampmétodo en lugar de tratar de descubrir cómo hacerlo usted mismo. E incluso si no lo hace, puede considerar tomar prestado su código fuente .

(Y si puede esperar a Python 3.4, parece que PEP 341 es probable que llegue a la versión final, lo que significa que todas las cosas de las que hablamos JF Sebastian y yo en los comentarios deberían ser factibles solo con stdlib, y trabajando de la misma manera en Unix y Windows).

abarnert
fuente
1
si dtestá en la zona horaria local, entonces la fórmula en la pregunta es incorrecta datetime.fromtimestamp(0)(epoch en la zona horaria actual) debe usarse en lugar de datetime(1970, 1,1)(epoch unix en UTC).
jfs
@JFSebastian: no quería cubrir las tres posibilidades en detalle, pero creo que tienes razón, debería hacerlo.
abarnert
por cierto, fromtimestamp(0)podría fallar si el sistema no almacena información histórica de zona horaria, por ejemplo, en Windows. pytzpodría usarse en este caso.
jfs
@JFSebastian: Pero pytzno ayuda a menos que ya sepas en qué zona horaria estás; para hacerlo mediante programación, necesita una biblioteca diferente que obtenga la zona horaria actual de Windows y la convierta en una pytzzona horaria y / o busque por nombre.
abarnert
Hay un tzlocalmódulo que también funciona en Windows. Así es como puede convertir la hora utc a la hora local .
jfs
141

la solución es

import time
import datetime
d = datetime.date(2015,1,5)

unixtime = time.mktime(d.timetuple())
DmitrySemenov
fuente
12
se supone que des un objeto ingenuo de fecha / fecha y hora que representa la hora local (puede fallar para tiempos ambiguos o para fechas pasadas / futuras si el sistema operativo no proporciona un tz db histórico (el desplazamiento UTC podría haber sido diferente en el pasado en el local) zona horaria)). Más opciones .
jfs
66
Parece que a nadie le importa la solución de punto flotante. ¿Es tan obvio para todos?
Ivan Balashov
44
Esto cae los microsegundos. Puede ser interesante
Alfe
El único problema es que no tienes en cuenta la zona horaria.
Blairg23
No debería. Si necesita ajustar TZ, debe hacerlo primero.
Marcin Orlowski el
69

Si desea convertir una fecha y hora de Python a segundos desde época, debe hacerlo explícitamente:

>>> import datetime
>>> datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'
>>> (datetime.datetime(2012,04,01,0,0) - datetime.datetime(1970,1,1)).total_seconds()
1333238400.0

En Python 3.3+ puedes usar timestamp()en su lugar:

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Francisco Costa
fuente
55
No tiene en cuenta las zonas horarias.
Blairg23
3
No desea utilizarlo %s, ya que estará localizado en el reloj del sistema en el que se encuentra actualmente. Solo debe usar .timestamp()para obtener la hora correcta de Epoch / UNIX.
Blairg23
3
%sno funciona en sistemas Windows ( ValueError: Invalid format string)
chrki
1
@ amitnair92 acaba de poner 8o9
Francisco Costa
54

En lugar de esta expresión para crear una marca de tiempo POSIX dt,

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

Utilizar este:

int(dt.strftime("%s"))

Recibo la respuesta correcta en su ejemplo usando el segundo método.

EDIT: Algunas seguimiento ... Después de algunos comentarios (véase más adelante), tenía curiosidad acerca de la falta de apoyo o documentación %sen strftime. Esto es lo que encontré:

En la fuente de Python para datetimey time, la cadena STRFTIME_FORMAT_CODESnos dice:

"Other codes may be available on your platform.
 See documentation for the C library strftime function."

Así que ahora si man strftime(en sistemas BSD como Mac OS X), encontrará soporte para %s:

"%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3))."

De todos modos, por eso %sfunciona en los sistemas que lo hace. Pero hay mejores soluciones para el problema de OP (que tienen en cuenta las zonas horarias). Ver la respuesta aceptada de @ abarnert aquí.

Piedra de Darren
fuente
44
Este es un comportamiento indocumentado (creo). Por ejemplo, en Windows da como resultado "Cadena de formato no válida".
Crescent Fresh
@CrescentFresh, interesante. Puede que tengas razón. Si bien no veo strftime("%s")en la documentación, acabo de confirmar que funcione en Mac y Linux. Gracias.
Darren Stone
1
aparentemente ignora el campo tzinfo.
Daniel F
1
@DarrenStone: no necesita leer la fuente. Tanto time.strftime()y datetime.strftimedelegado de documentación a las plataformas strftime(3)de función para directivas no compatibles. %spuede fallar incluso en Mac OS X, por ejemplo, datetime.strftime ('% s') debe respetar tzinfo .
jfs
1
Nunca debe usarlo, %sya que solo usará la hora localizada del sistema en el que se encuentra. Desea usar .timestamp()si está intentando obtener la marca de tiempo UNIX real.
Blairg23
6

Para trabajar con zonas horarias UTC:

time_stamp = calendar.timegm(dt.timetuple())

datetime.utcfromtimestamp(time_stamp)
Emin Temiz
fuente
Después de una hora de búsqueda, esta fue la única respuesta que funcionó: ')
Qurashi
4

Te has perdido la información de zona horaria (ya contestada, de acuerdo)

arrowel paquete permite evitar esta tortura con fecha y hora; Ya está escrito, probado, publicado en pypi, cross-python (2.6 - 3.xx).

Todo lo que necesitas: pip install arrow(o agregar a las dependencias)

Solución para tu caso

dt = datetime(2013,9,1,11)
arrow.get(dt).timestamp
# >>> 1378033200

bc = arrow.get(1378033200).datetime
print(bc)
# >>> datetime.datetime(2013, 9, 1, 11, 0, tzinfo=tzutc())
print(bc.isoformat())
# >>> '2013-09-01T11:00:00+00:00'
maxkoryukov
fuente
3

Si su objeto datetime representa la hora UTC, no use time.mktime, ya que supone que la tupla está en su zona horaria local. En su lugar, use calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Matt Kramer
fuente
1
def dt2ts(dt, utc=False):
    if utc:
        return calendar.timegm(dt.timetuple())
    if dt.tzinfo is None:
        return int(time.mktime(dt.timetuple()))
    utc_dt = dt.astimezone(tz.tzutc()).timetuple()
    return calendar.timegm(utc_dt)

Si desea la marca de tiempo UTC: time.mktimesolo para dt local. El uso calendar.timegmes seguro pero debe ser la zona utc, así que cambie la zona a utc. Si dt ​​en UTC solo use calendar.timegm.

wyx
fuente
1
def datetime_to_epoch(d1):

    # create 1,1,1970 in same timezone as d1
    d2 = datetime(1970, 1, 1, tzinfo=d1.tzinfo)
    time_delta = d1 - d2
    ts = int(time_delta.total_seconds())
    return ts


def epoch_to_datetime_string(ts, tz_name="UTC"):
    x_timezone = timezone(tz_name)
    d1 = datetime.fromtimestamp(ts, x_timezone)
    x = d1.strftime("%d %B %Y %H:%M:%S")
    return x
rjha94
fuente
0

Esta clase cubrirá sus necesidades, puede pasar la variable a ConvertUnixToDatetime y llamar a qué función desea que opere en función de.

from datetime import datetime
import time

class ConvertUnixToDatetime:
    def __init__(self, date):
        self.date = date

    # Convert unix to date object
    def convert_unix(self):
        unix = self.date

        # Check if unix is a string or int & proceeds with correct conversion
        if type(unix).__name__ == 'str':
            unix = int(unix[0:10])
        else:
            unix = int(str(unix)[0:10])

        date = datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S')

        return date

    # Convert date to unix object
    def convert_date(self):
        date = self.date

        # Check if datetime object or raise ValueError
        if type(date).__name__ == 'datetime':
            unixtime = int(time.mktime(date.timetuple()))
        else:
            raise ValueError('You are trying to pass a None Datetime object')
        return type(unixtime).__name__, unixtime


if __name__ == '__main__':

    # Test Date
    date_test = ConvertUnixToDatetime(datetime.today())
    date_test = date_test.convert_date()
    print(date_test)

    # Test Unix
    unix_test = ConvertUnixToDatetime(date_test[1])
    print(unix_test.convert_unix())
cryptopotluck
fuente