Convertendo datetime.date para carimbo de data e hora UTC em Python

295

Estou lidando com datas em Python e preciso convertê-las em carimbos de data e hora UTC para serem usados ​​dentro de Javascript. O código a seguir não funciona:

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

Converter o objeto de data primeiro em datetime também não ajuda. Eu tentei o exemplo neste link de, mas:

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

e agora:

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

ou

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

funciona.

Pergunta tão geral: como posso converter uma data em segundos desde a época, de acordo com o UTC?

Andreas Jung
fonte
possível duplicata de Como converter a hora local para UTC no Python?
Piotr Dobrogost
29
Não tenho certeza se concordaria em marcá-lo como duplicado. Embora as soluções sejam semelhantes, as perguntas não são. Um (este) está tentando criar um registro de data e hora de um datetime.date, o outro está tentando converter uma representação de string de um fuso horário para outro. Como alguém que procura uma solução para esse problema, posso não concluir que o último forneça a resposta que estou procurando.
DRH
5
datetime (date.year, date.month, date.day) .timestamp ()
Julian
4
Possível duplicata do Python, como você converte um objeto `datetime` em segundos?
Trevor Boyd Smith

Respostas:

462

Se d = date(2011, 1, 1)estiver no UTC:

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

Se destiver no fuso horário 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)

timestamp1e timestamp2pode diferir se meia-noite no fuso horário local não for a mesma instância de hora que meia-noite no UTC.

mktime()pode retornar um resultado errado se dcorresponder a um horário local ambíguo (por exemplo, durante a transição do horário de verão) ou se dfor uma data passada (futura) em que o deslocamento utc possa ter sido diferente e o C mktime()não tenha acesso ao banco de dados tz na plataforma fornecida . Você pode usar o pytzmódulo (por exemplo, via tzlocal.get_localzone()) para obter acesso ao banco de dados tz em todas as plataformas . Além disso, utcfromtimestamp()pode falhar e mktime()pode retornar o carimbo de data / hora não POSIX se o "right"fuso horário for usado .


Para converter um datetime.dateobjeto que representa a data no UTC sem 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

Como posso converter uma data em segundos desde a época, de acordo com o UTC?

Converter datetime.datetime(não datetime.date) o objeto que já representa a hora no UTC no carimbo de data / hora POSIX correspondente (a float).

Python 3.3 ou superior

datetime.timestamp():

from datetime import timezone

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

Nota: É necessário fornecer timezone.utcexplicitamente, caso contrário, .timestamp()suponha que seu objeto de data e hora ingênuo esteja no fuso horário local.

Python 3 (<3.3)

Dos documentos para datetime.utcfromtimestamp():

Não existe um método para obter o registro de data e hora de uma instância de data e hora, mas o registro de data e hora POSIX correspondente a uma instância de data e hora dt pode ser facilmente calculado da seguinte maneira. Para um dt ingênuo:

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

E para um dt consciente:

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

Leitura interessante: época da época versus hora do dia sobre a diferença entre Que horas são? e Quantos segundos se passaram?

Consulte também: datetime precisa de um método de "época"

Python 2

Para adaptar o código acima para Python 2:

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

Onde timedelta.total_seconds() é equivalente ao (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6calculado com a divisão verdadeira ativada.

Exemplo

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 com com problemas de ponto flutuante .

Resultado

2012-01-08 15:34:10.022403
1326036850.02

Como converter um datetimeobjeto consciente em carimbo de data / hora POSIX

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

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

No 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
fonte
2
Para o caso Python 2, por que não timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
M01
7
@ m01: datetime(1970, 1, 1)é mais explícito. fromtimestamp()está incorreto aqui ( dtestá no UTC, portanto, utcfromtimestamp()deve ser usado).
jfs
1
@fedorqui veja a totimestamp()função na resposta.
JFS
14
por mais que eu goste de python, sua lib de data e hora está seriamente quebrada.
Realfun
3
@ Realfun: fusos horários, transições de horário de verão, o banco de dados tz, segundos bissextos existem independentemente do Python.
JFS
81

Apenas para sistemas unix :

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

Nota 1: dizzyf observou que isso se aplica a fusos horários localizados . Não use na produção.

Nota 2: Jakub Narębski observou que isso ignora informações de fuso horário, mesmo para data e hora com reconhecimento de offset (testado para Python 2.7).

Ulf Aslak
fonte
4
Uma boa resposta se você souber em qual sistema seu código será executado, mas é importante observar que a sequência de formato '% s' não existe em todos os sistemas operacionais; portanto, esse código não é portátil. Se você quiser verificar se é suportado, pode verificar man strftimesistemas semelhantes ao Unix.
Alice Heaton
5
Deve-se mencionar que isso reduz a parte fracionária de segundos (microssegundos ou o que for).
Alfe
11
AVISO: Isso aplica fusos horários localizados! Você vai ter comportamento strftime diferente para objetos de data e hora idênticas, dependendo do tempo de máquina
dizzyf
3
Além disso, isso ignora as informações do fuso horário e pressupõe que o datetime esteja no fuso horário local, mesmo para o datetime com reconhecimento de deslocamento , com tzinfo configurado. Bem, pelo menos no Python 2.7.
Jakub Narębski
1
timestamp javascript = timestamp python * 1000
Trinh Hoang Nhu
38
  • Suposição 1: você está tentando converter uma data em um carimbo de data / hora. No entanto, como uma data cobre um período de 24 horas, não há um único carimbo de data e hora que represente essa data. Suponho que você queira representar o carimbo de data e hora dessa data à meia-noite (00: 00: 00.000).

  • Suposição 2: a data que você apresenta não está associada a um fuso horário específico, no entanto, você deseja determinar o deslocamento de um fuso horário específico (UTC). Sem saber o fuso horário da data, não é possível calcular um carimbo de data / hora para um fuso horário específico. Suponho que você deseja tratar a data como se estivesse no fuso horário do sistema local.

Primeiro, você pode converter a instância de data em uma tupla representando os vários componentes de hora usando o timetuple()membro:

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)

Você pode converter isso em um carimbo de data / hora usando time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Você pode verificar esse método testando-o com o próprio horário da época (01-01-2009); nesse caso, a função deve retornar o deslocamento do fuso horário para o fuso horário local nessa data:

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 são 8 horas, o que seria correto para o fuso horário do Pacífico (onde estou).

DRH
fonte
Não sei por que isso foi prejudicado. Ele aborda a questão do OP (o código de trabalho que o OP rotulou como "não está funcionando"). Ele não responde à minha pergunta (converte o UTC datetime.datetime em timestamp), mas ainda assim… com voto positivo.
Thanatos
9
Seja cuidadoso. A suposição de que "você deseja tratar a data como se estivesse no fuso horário do sistema local é a raiz de todos os problemas com o fuso horário no python. A menos que você tenha 100% de certeza da localização da máquina que executará seu script e, especialmente, se atendendo a clientes que podem vir de qualquer lugar do mundo, sempre assumir datas estão em UTC, e fazer a conversão o mais cedo possível vai manter os cabelos em sua cabeça, confie em mim..
Romain G
8

siga o documento python2.7, você precisará usar calendar.timegm () em vez de time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)
Wang
fonte
3
qual é a diferença da minha resposta se destiver no UTC?
JFS
8

Eu defini minhas próprias duas funções

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

aqui:

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
Agittarius
fonte
1
Eu naveguei direto além do seu exemplo .. Maneira de confuso e complicado para uma coisa tão simples .. Não é pitônico.
irritada 84
@ Mayhem: Limpei um pouco e acrescentei comentários. Se você tiver uma solução melhor e mais genérica para converter de tempos em tempos e datas e voltar a definir o fuso horário - informe-nos. Além disso, deixe-me saber como deve ser um exemplo mais pitonico do meu código.
agittarius
3

a pergunta está um pouco confusa. os carimbos de data e hora não são UTC - são uma coisa do Unix. a data pode ser UTC? assumindo que é, e se você estiver usando o Python 3.2+, simple-date torna isso trivial:

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

se você realmente tem o ano, mês e dia, não precisa criar o date:

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

e se a data estiver em outro fuso horário (isso é importante porque estamos assumindo a meia-noite sem um horário associado):

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

[a idéia por trás do simple-date é coletar todas as informações de data e hora do python em uma classe consistente, para que você possa fazer qualquer conversão. então, por exemplo, também irá para o outro lado:

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

]

andrew cooke
fonte
SimpleDate(date(2011,1,1), tz='America/New_York')aumenta NoTimezone, enquanto pytz.timezone('America/New_York')não gera nenhuma exceção ( simple-date==0.4.8, pytz==2014.7).
JFS
Eu recebo um erro pip install simple-date, parece que é apenas python3 / pip3.
NoBugs
3

Usando o pacote arrow :

>>> 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
fonte
Nota: arrowusa dateutil e dateutilconhece há muitos anos bugs relacionados ao manuseio de fuso horário. Não use-o se precisar de resultados corretos para fusos horários com um deslocamento UTC não fixo (não há problema em usá-lo no fuso horário UTC, mas o stdlib também lida com o caso UTC).
JFS
@JFSebastian Esse bug ocorre apenas durante uma transição, em um período ambíguo.
Paul
Parece que o bug foi corrigido em 28 de março
Mathieu Longtin
Parece que é a melhor solução e cross-python. Basta usar a seta . Graças;)
maxkoryukov
@MathieuLongtin dateutil pode ter outros problemas
jfs
1

Uma sequência de tempo completa contém:

  • encontro
  • Tempo
  • utcoffset [+HHMM or -HHMM]

Por exemplo:

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 timestampé um número de ponto flutuante expresso em segundos desde a época, em 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
fonte
6
E como isso responde à minha pergunta?
Andreas Jung
1
@ user908088 Converta 1970-01-01 06:00:00 +0500para 3600como você pediu.
kev
1
@ user908088 Não há timezoneno python2, você pode fazer o cálculo ( 06:00:00- +0500= 01:00:00) manualmente.
kev
5
por que esta resposta no topo mesmo que tenha -1 votos, errado algo com SO
Umair
1

Considerando que você tem um datetimeobjeto chamado d, use o seguinte para obter o registro de data e hora no UTC:

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

E na direção oposta, use o seguinte:

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

Estou impressionado com a profunda discussão.

meus 2 centavos:

da data e hora da importação data e hora da importação

o registro de data e hora em utc é:

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

ou,

timestamp = time.time()

se agora resultar de datetime.now (), no mesmo horário de verão

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
não revelado
fonte