Converta data e hora em carimbo de data / hora do Unix e converta-o novamente em python

186

Eu tenho dt = datetime(2013,9,1,11)e gostaria de obter um registro de data e hora do Unix desse objeto datetime.

Quando eu (dt - datetime(1970,1,1)).total_seconds()recebi o timestamp 1378033200.

Ao convertê-lo novamente usando datetime.fromtimestampeu consegui datetime.datetime(2013, 9, 1, 6, 0).

A hora não corresponde. O que eu perdi aqui?

ponte
fonte
1
Como observação, se você estiver usando o Python 3.3+, você realmente deseja usar o timestampmétodo em vez de tentar fazer isso sozinho. Por um lado, isso evita completamente a possibilidade de subtrair tempos ingênuos de diferentes fusos horários.
abarnert
1
De onde dtvem? É uma hora local ou horário no UTC?
JFS
2
@abarnert Também gostaria de enviar uma solicitação para limpar todas as páginas de código e símbolos unicode, ou seja, proibir todos os idiomas de página de código não-ascii e não-padrão. Pinturas de símbolos ainda são permitidas.
Daniel F
6
@DanielF: Pedido negado. Não estou interessado em criar um idioma de mundo único aqui, e mesmo se o fizéssemos, não gostaria de tornar a vida impossível para linguistas históricos e estudiosos de Tolkien. O Unicode já resolve o problema, exceto que certas organizações e produtos (o consórcio TRON, software japonês que usa Shift-JIS sobre UTF-8, a Microsoft ainda oferece um sistema operacional que usa o cp1252 para arquivos de texto do usuário e vários SDKs que pretendem que o UTF -16 é um conjunto de caracteres de largura fixa e / ou o mesmo que Unicode) precisa ser punido para alinhá-los.
#

Respostas:

105

O que você perdeu aqui é o fuso horário.

Presumivelmente, você tem cinco horas de folga no UTC, portanto, 2013-09-01T11: 00: 00 local e 2013-09-01T06: 00: 00Z são o mesmo horário.

Você precisa ler a parte superior dos datetimedocumentos, que explica os fusos horários e os objetos "ingênuos" e "conscientes".

Se o seu data e hora ingênuo original era UTC, a maneira de recuperá-lo é usar em utcfromtimestampvez de fromtimestamp.

Por outro lado, se o seu data e hora ingênuo original fosse local, você não deveria ter subtraído um carimbo de data / hora UTC dele em primeiro lugar; use em datetime.fromtimestamp(0)vez disso.

Ou, se você tivesse um objeto de data e hora consciente, precisará usar uma época local (consciente) dos dois lados ou converter explicitamente para e do UTC.

Se você possui ou pode atualizar para o Python 3.3 ou posterior, pode evitar todos esses problemas apenas usando o timestampmétodo em vez de tentar descobrir como fazê-lo. E mesmo se não o fizer, considere emprestar seu código-fonte .

(E se você pode esperar pelo Python 3.4, parece que o PEP 341 provavelmente chegará ao lançamento final, o que significa que todas as coisas sobre as quais JF Sebastian e eu estávamos falando nos comentários devem ser possíveis apenas com o stdlib, e trabalhando da mesma maneira no Unix e no Windows.)

abarnert
fonte
1
se dtestiver no fuso horário local, a fórmula na pergunta está incorreta datetime.fromtimestamp(0)(época no fuso horário atual) deve ser usada em vez de datetime(1970, 1,1)(época unix no UTC).
JFS
@JFSebastian: Eu não queria cobrir todas as três possibilidades em detalhes, mas acho que você está certo, deveria.
abarnert
Por outro lado, fromtimestamp(0)pode falhar se o sistema não armazenar informações históricas do fuso horário, por exemplo, no Windows. pytzpoderia ser usado neste caso.
JFS
@JFSebastian: Mas pytznão ajuda, a menos que você já saiba em que fuso horário está; para fazer isso programaticamente, você precisa de uma biblioteca diferente que obtenha o fuso horário atual do Windows e o converta em um pytzfuso horário e / ou procure pelo nome.
abarnert
existe um tzlocalmódulo que também funciona no Windows. Veja como você pode converter a hora utc na hora local .
JFS
141

solução é

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

unixtime = time.mktime(d.timetuple())
DmitrySemenov
fonte
12
ele assume que dé um objeto ingênuo de data / data e hora que representa a hora local (pode falhar em horários ambíguos ou em datas passadas / futuras se o SO não fornecer um histórico tz db (o deslocamento UTC pode ter sido diferente no passado no local) fuso horário)). Mais opções .
JFS
6
Parece que ninguém se importa com a solução de ponto flutuante. Isso é óbvio para todos?
Ivan Balashov
4
Isso reduz os microssegundos. Pode ser interessante.
Alfe
O único problema é que você não leva em consideração o fuso horário.
Blairg23
não deveria. Se você precisar ajustar a TZ, faça isso primeiro.
Marcin Orlowski
69

Se você deseja converter um datetime de python em segundos desde a época, deve fazê-lo explicitamente:

>>> 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

No Python 3.3 ou superior, você pode usar timestamp():

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Francisco Costa
fonte
5
Não leva em consideração os fusos horários.
Blairg23
3
Você não deseja usar %s, pois ele será localizado no relógio do sistema em que você está atualmente. Você só deve usar .timestamp()para obter a hora Epoch / UNIX correta.
precisa saber é o seguinte
3
%snão funciona em sistemas Windows ( ValueError: Invalid format string)
chrki 3/17/17
1
@ amitnair92 basta colocar 8or9
Francisco Costa
54

Em vez desta expressão, para criar um carimbo de data / hora POSIX dt,

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

Usa isto:

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

Eu recebo a resposta certa no seu exemplo usando o segundo método.

EDIT: Alguns acompanhamento ... Depois de alguns comentários (veja abaixo), eu estava curioso sobre a falta de apoio ou documentação para %sno strftime. Aqui está o que eu encontrei:

Na fonte Python para datetimee time, a string STRFTIME_FORMAT_CODESnos diz:

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

Então agora, se nós man strftime(em sistemas BSD como o Mac OS X), você encontrará suporte para %s:

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

De qualquer forma, é por isso que %sfunciona nos sistemas que faz. Mas existem soluções melhores para o problema do OP (que levam em consideração os fusos horários). Veja a resposta aceita do @ abarnert aqui.

Darren Stone
fonte
4
Esse é um comportamento não documentado (acredito). Por exemplo, no Windows, resulta em "Sequência de formato inválida".
Crescent Fresh
@CrescentFresh, interessante. Tu podes estar certo. Embora não veja strftime("%s")na documentação, acabei de confirmar isso para funcionar no Mac e Linux. Obrigado.
precisa
1
aparentemente ele ignora o campo tzinfo.
Daniel F
1
@ DarrenStone: você não precisa ler a fonte. A documentação time.strftime()e a datetime.strftimedelegação das plataformas strftime(3)funcionam para diretivas não suportadas. %spode falhar mesmo no Mac OS X, por exemplo, datetime.strftime ('% s') deve respeitar o tzinfo .
JFS
1
Você nunca deve usar, %spois usará apenas a hora localizada do sistema em que está. Você deseja usar .timestamp()se estiver tentando obter o registro de data e hora real do UNIX.
precisa saber é o seguinte
6

Para trabalhar com fusos horários UTC:

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

datetime.utcfromtimestamp(time_stamp)
Emin Temiz
fonte
Depois de uma hora de busca, esta foi a única resposta que funcionou: ')
Qurashi
4

Você perdeu as informações do fuso horário (já respondidas, concordadas)

arrowpacote permite evitar essa tortura com as datas; Já está escrito, testado, publicado em pypi, com python cruzado (2.6 - 3.xx).

Tudo que você precisa: pip install arrow(ou adicione às dependências)

Solução para o seu 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
fonte
3

Se o seu objeto datetime representar a hora UTC, não use time.mktime, pois assume que a tupla está no fuso horário local. Em vez disso, use calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Matt Kramer
fonte
2

Bem, ao converter o timestamp TO unix, o python está basicamente assumindo o UTC, mas, ao converter novamente, ele fornecerá uma data convertida para o fuso horário local.

Veja esta pergunta / resposta; Obter fuso horário usado por datetime.datetime.fromtimestamp ()

Ronald Portier
fonte
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)

Se você desejar o carimbo de data / hora UTC: time.mktimeapenas para dt local. O usocalendar.timegm é seguro, mas dt deve a zona utc, portanto altere a zona para utc. Se dt no UTC apenas use calendar.timegm.

wyx
fonte
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
fonte
0

Esta classe cobrirá suas necessidades, você pode passar a variável para ConvertUnixToDatetime e chamar a função em que deseja que ela funcione com base.

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
fonte