Como converter uma string de hora local para UTC?

308

Como converter uma sequência de data e hora no horário local em uma sequência no horário UTC ?

Tenho certeza de que já fiz isso antes, mas não consigo encontrá-lo, e espero que SO me ajude (e outros) a fazer isso no futuro.

Esclarecimento : Por exemplo, se eu tenho 2008-09-17 14:02:00em meu fuso horário local ( +10), eu gostaria de gerar uma seqüência com o equivalente UTCtempo: 2008-09-17 04:02:00.

Além disso, em http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ , observe que, em geral, isso não é possível, pois no horário de verão e em outros problemas, não há conversão única da hora local para Hora UTC.

Tom
fonte
1
Esteja ciente de que o método mktime () leva uma "hora local" como entrada, que pode não ser o que você espera, usei-o e estragou tudo. Por favor, dê uma olhada neste link
Haifeng Zhang 28/02

Respostas:

288

Primeiro, analise a sequência em um objeto de data e hora ingênuo. Esta é uma instância datetime.datetimesem informações de fuso horário anexadas. Consulte a documentação para datetime.strptimeobter informações sobre a análise da sequência de datas.

Use o pytzmódulo, que vem com uma lista completa de fusos horários + UTC. Descobrir qual é o fuso horário local, construir um objeto de fuso horário a partir dele, manipular e anexá-lo à data e hora ingênuas.

Por fim, use o datetime.astimezone()método para converter a data e hora em UTC.

Código-fonte, usando o fuso horário local "America / Los_Angeles", para a sequência "2001-2-3 10:11:12":

import pytz, datetime
local = pytz.timezone ("America/Los_Angeles")
naive = datetime.datetime.strptime ("2001-2-3 10:11:12", "%Y-%m-%d %H:%M:%S")
local_dt = local.localize(naive, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)

A partir daí, você pode usar o strftime()método para formatar a data e hora UTC, conforme necessário:

utc_dt.strftime ("%Y-%m-%d %H:%M:%S")
John Millikin
fonte
7
Edite sua resposta com o seguinte código: [code] local.localize (ingênuo) [/ code] Veja o documento: link Infelizmente, usando o argumento tzinfo dos construtores de data e hora padrão '' não funciona '' com pytz por muitos fusos horários. >>> datetime (2002, 10, 27, 12, 0, 0, tzinfo = amsterdam) .strftime (fmt) '2002-10-27 12:00:00 AMT + 0020'
Sam Stoelinga
2
Aparentemente, a etapa "descobrir qual é o fuso horário local" é mais difícil do que parece (praticamente impossível).
Jason R. Coombs
2
Use o local.localize como sugerido por @SamStoelinga, caso contrário você não levará em consideração o horário de verão.
Emil Stenström
Essa resposta foi muito útil para mim, mas eu gostaria de apontar uma armadilha que encontrei. Se você especificar a hora, mas não a data, poderá obter resultados inesperados. Portanto, certifique-se de fornecer um carimbo de data / hora completo.
9788 Steven Universo
145

A função utcnow () do módulo datetime pode ser usada para obter a hora UTC atual.

>>> import datetime
>>> utc_datetime = datetime.datetime.utcnow()
>>> utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2010-02-01 06:59:19'

Como o link mencionado acima por Tom: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ diz:

UTC é um fuso horário sem horário de verão e ainda um fuso horário sem alterações de configuração no passado.

Sempre meça e armazene o tempo no UTC .

Se você precisar registrar onde o tempo foi levado, guarde-o separadamente. Não armazene a hora local + informações de fuso horário!

NOTA - Se algum dos seus dados estiver em uma região que usa horário de verão, use pytze dê uma olhada na resposta de John Millikin.

Se você deseja obter o horário UTC de uma determinada sequência e tiver a sorte de estar em uma região do mundo que não usa o horário de verão, ou se você tiver dados que são apenas deslocados do UTC sem o horário de verão aplicado:

-> usando a hora local como base para o valor de compensação:

>>> # Obtain the UTC Offset for the current system:
>>> UTC_OFFSET_TIMEDELTA = datetime.datetime.utcnow() - datetime.datetime.now()
>>> local_datetime = datetime.datetime.strptime("2008-09-17 14:04:00", "%Y-%m-%d %H:%M:%S")
>>> result_utc_datetime = local_datetime + UTC_OFFSET_TIMEDELTA
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

-> Ou, de um deslocamento conhecido, usando datetime.timedelta ():

>>> UTC_OFFSET = 10
>>> result_utc_datetime = local_datetime - datetime.timedelta(hours=UTC_OFFSET)
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

ATUALIZAR:

Desde python 3.2 datetime.timezoneestá disponível. Você pode gerar um objeto datetime com reconhecimento de fuso horário com o comando abaixo:

import datetime

timezone_aware_dt = datetime.datetime.now(datetime.timezone.utc)

Se você está pronto para assumir conversões de fuso horário, leia o seguinte:

https://medium.com/@eleroy/10-things-you-need-to-know-about-date-and-time-in-python-with-datetime-pytz-dateutil-timedelta-309bfbafb3f7

monkut
fonte
14
Isso só converte o horário atual, preciso levar um tempo qualquer (como uma string) e converter para UTC.
Tom
Eu não acho que deveria importar aqui, se você estiver usando valores de deslocamento padrão. local_time = utc_time + utc_offset AND utc_time = local_time - utc_offset.
10131 monkut
5
Funciona apenas com a hora atual, porque os carimbos de data e hora anteriores e futuros podem ter deslocamento UTC diferente devido ao horário de verão.
Alex B
bom ponto ... se você tiver que lidar com o horário de verão, provavelmente deverá usar o pytz, como mencionado por John Millikin.
Monkut
1
Existe um delta esporádico entre as chamadas para utcnow () e now (). Essa é uma linha de código perigosa que pode levar a erros estranhos mais tarde, tzinfo.utcoffset() must return a whole number of minutese assim por diante.
Pavel Vlasov
62

Obrigado @rofly, a conversão completa de string para string é a seguinte:

time.strftime("%Y-%m-%d %H:%M:%S", 
              time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00", 
                                                    "%Y-%m-%d %H:%M:%S"))))

Meu resumo das funções time/ calendar:

time.strptime
string -> tupla (nenhum fuso horário aplicado, portanto, corresponde à string)

time.mktime
tupla da hora local -> segundos desde a época (sempre hora local)

time.gmtime
segundos desde a época -> tupla no UTC

e

calendar.timegm
tupla em UTC -> segundos desde a época

time.localtime
segundos desde a época -> tupla no fuso horário local

Tom
fonte
4
(always local time)parece estar errado: a entrada para mktime () é hora local, a saída é segundos desde a época ( 1970-01-01 00:00:00 +0000 (UTC)) que não depende do fuso horário.
precisa saber é
3
Também há 50% de chance de falha durante a transição do horário de verão. Veja Problemas com o Localtime
jfs
38

Aqui está um resumo das conversões de tempo comuns do Python.

Alguns métodos eliminam frações de segundos e são marcados com (s) . Uma fórmula explícita como ts = (d - epoch) / unitpode ser usada (obrigado jfs).

  • struct_time (UTC) → POSIX (s) :
    calendar.timegm(struct_time)
  • Data e hora ingênuas (local) → POSIX (s) :
    calendar.timegm(stz.localize(dt, is_dst=None).utctimetuple())
    (exceção durante transições de horário de verão, ver comentário de jfs)
  • Data e hora naïve (UTC) → POSIX (s) :
    calendar.timegm(dt.utctimetuple())
  • Data e hora conscientes → POSIX (s) :
    calendar.timegm(dt.utctimetuple())
  • POSIX → struct_time (UTC, s ):
    time.gmtime(t)
    (ver comentário de jfs)
  • Data e hora naïve (local) → struct_time (UTC, s ):
    stz.localize(dt, is_dst=None).utctimetuple()
    (exceção durante transições de horário de verão, veja o comentário de jfs)
  • Data e hora naïve (UTC) → struct_time (UTC, s ):
    dt.utctimetuple()
  • Datetime consciente → struct_time (UTC, s ):
    dt.utctimetuple()
  • POSIX → Data e hora naïve (local):
    datetime.fromtimestamp(t, None)
    (pode falhar em determinadas condições, veja o comentário de jfs abaixo)
  • struct_time (UTC) → Data e hora naïve (local, s ):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
    não pode representar segundos bissextos, veja o comentário de jfs)
  • Data e hora naïve (UTC) → Data e hora naïve (local):
    dt.replace(tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
  • Datetime consciente → Datetime ingênuo (local):
    dt.astimezone(tz).replace(tzinfo=None)
  • POSIX → Data e hora naïve (UTC):
    datetime.utcfromtimestamp(t)
  • struct_time (UTC) → Data e hora naïve (UTC, s ):
    datetime.datetime(*struct_time[:6])
    (não pode representar segundos bissextos, veja o comentário de jfs)
  • Naïve datetime (local) → Naïve datetime (UTC):
    stz.localize(dt, is_dst=None).astimezone(UTC).replace(tzinfo=None)
    (exceção durante transições de horário de verão, veja o comentário de jfs)
  • Data / hora consciente → Data / hora naïve (UTC):
    dt.astimezone(UTC).replace(tzinfo=None)
  • POSIX → Datetime ciente:
    datetime.fromtimestamp(t, tz)
    (pode falhar para fusos horários não pytz)
  • struct_time (UTC) → Datetime (s) ciente (s) :
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz)
    (não pode representar segundos bissextos, veja o comentário de jfs)
  • Data e hora ingênuas (local) → Data e hora conscientes:
    stz.localize(dt, is_dst=None)
    (exceção durante transições de horário de verão, consulte o comentário de jfs)
  • Data e hora ingênuas (UTC) → Data e hora conscientes:
    dt.replace(tzinfo=UTC)

Fonte: taaviburns.ca

akaihola
fonte
1
(1) fromtimestamp(t, None)pode falhar se o fuso horário local tiver um deslocamento utc diferente em t e a biblioteca C não tiver acesso ao banco de dados tz na plataforma especificada. Você pode usar tzlocal.get_localzone()para fornecer tzdata de maneira portátil. (2) fromtimestamp(t, tz)pode falhar em fusos horários que não sejam pytz. (3) datetime(*struct_time[:6])você está desaparecido *. (4) timegm(), utctimetuple(), struct_timesoluções baseadas gota fracções de segundo. Você pode usar uma fórmula explícita, como: em ts = (d - epoch) / unitvez disso.
jfs
1
(5) stz.localize(dt, is_dst=None)gera uma exceção para horários locais ambíguos ou inexistentes, por exemplo, durante transições de horário de verão. Para evitar a exceção, use um stz.normalize(stz.localize(dt))que possa retornar resultados imprecisos. (6) datetime () não pode representar um salto de segundo. Converta struct_timepara "segundos desde a época" como uma solução alternativa primeiro. (7) time.gmtime(t)ao contrário, calendar.timegm()pode esperar uma entrada não POSIX se o fuso horário "correto" for usado. Use a fórmula explícita se a entrada for POSIX:gmtime = lambda t: datetime(1970,1,1, tzinfo=utc) + timedelta(seconds=t)
jfs
Eu desejo que este estava em uma mesa, mais fácil de ler (ele está no link)
MichaelChirico
1
@MichaelChirico, esta resposta afirma que "Não permitimos (e não permitimos) <table>tags. Desculpe. Isso é intencional e por design. Se você precisar de uma" tabela "rápida e suja, use um <pre>layout ASCII". Não acredito que uma tabela ASCII seja mais legível que a lista acima.
Akaihola
bem, isso é lamentável. você tem meu voto positivo de qualquer maneira ... talvez faça referência à tabela no link dentro de sua resposta?
MichaelChirico
25
def local_to_utc(t):
    secs = time.mktime(t)
    return time.gmtime(secs)

def utc_to_local(t):
    secs = calendar.timegm(t)
    return time.localtime(secs)

Fonte: http://feihonghsu.blogspot.com/2008/02/converting-from-local-time-to-utc.html

Exemplo de uso do bd808 : Se sua fonte for um datetime.datetimeobjeto t, chame como:

local_to_utc(t.timetuple())
Chuck Callebs
fonte
5
Se sua fonte é um objeto datetime.datetime t, chamada como: local_to_utc (t.timetuple ())
bd808
2
.timetuple()conjuntos de chamadas tm_isdstpara -1; há 50% de chance de mktime()falha durante a transição do horário de verão.
JFS
1
A solução de Chuck perde informações em milissegundos.
Luca
21

Estou tendo boa sorte com o dateutil (que é amplamente recomendado no SO para outras questões relacionadas):

from datetime import *
from dateutil import *
from dateutil.tz import *

# METHOD 1: Hardcode zones:
utc_zone = tz.gettz('UTC')
local_zone = tz.gettz('America/Chicago')
# METHOD 2: Auto-detect zones:
utc_zone = tz.tzutc()
local_zone = tz.tzlocal()

# Convert time string to datetime
local_time = datetime.strptime("2008-09-17 14:02:00", '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in local time zone since 
# datetime objects are 'naive' by default
local_time = local_time.replace(tzinfo=local_zone)
# Convert time to UTC
utc_time = local_time.astimezone(utc_zone)
# Generate UTC time string
utc_string = utc_time.strftime('%Y-%m-%d %H:%M:%S')

(O código foi derivado desta resposta para Converter a string de data e hora UTC em data e hora local )

Yarin
fonte
dateutilpode falhar em datas passadas se o fuso horário local tiver um deslocamento utc diferente então (não relacionado às transições de horário de verão) em sistemas nos quais a implementação não usa um banco de dados histórico de fuso horário (principalmente o Windows). pytz+ tzlocalpoderia ser usado em seu lugar.
JFS
@ JFSebastian- Não tinha ouvido falar disso - onde podemos obter mais informações?
Yarin 26/12
pegue a máquina Windows, defina qualquer fuso horário que possua deslocamento de utc diferente no passado, por exemplo, Europa / Moscou. Compare resultados com pytz . Além disso, você pode testar esse bug que falha mesmo no Ubuntu, embora não tenha certeza sobre o uso correto da API neste caso.
JFS
Qual é o resultado? Por favor, publique a saída.
Not2qubit
18

Mais um exemplo com pytz, mas inclui localize (), que salvou meu dia.

import pytz, datetime
utc = pytz.utc
fmt = '%Y-%m-%d %H:%M:%S'
amsterdam = pytz.timezone('Europe/Amsterdam')

dt = datetime.datetime.strptime("2012-04-06 10:00:00", fmt)
am_dt = amsterdam.localize(dt)
print am_dt.astimezone(utc).strftime(fmt)
'2012-04-06 08:00:00'
Paulius Sladkevičius
fonte
12

Eu tive o maior sucesso com python-dateutil :

from dateutil import tz

def datetime_to_utc(date):
    """Returns date in UTC w/o tzinfo"""
    return date.astimezone(tz.gettz('UTC')).replace(tzinfo=None) if date.tzinfo else date
Shu Wu
fonte
7
import time

import datetime

def Local2UTC(LocalTime):

    EpochSecond = time.mktime(LocalTime.timetuple())
    utcTime = datetime.datetime.utcfromtimestamp(EpochSecond)

    return utcTime

>>> LocalTime = datetime.datetime.now()

>>> UTCTime = Local2UTC(LocalTime)

>>> LocalTime.ctime()

'Thu Feb  3 22:33:46 2011'

>>> UTCTime.ctime()

'Fri Feb  4 05:33:46 2011'
Scipythonee
fonte
mktime(dt.timetuple())pode falhar durante a transição do horário de verão . Há LocalTimezonenos docs (ele trabalha para a definição mais recente fuso horário, mas pode falhar por datas passadas (sem relação com DST))
jfs
5

se você preferir datetime.datetime:

dt = datetime.strptime("2008-09-17 14:04:00","%Y-%m-%d %H:%M:%S")
utc_struct_time = time.gmtime(time.mktime(dt.timetuple()))
utc_dt = datetime.fromtimestamp(time.mktime(utc_struct_time))
print dt.strftime("%Y-%m-%d %H:%M:%S")
user235042
fonte
1
Claro, mas não consigo ver por que você prefere isso. Ela exige uma importação extra e mais 3 chamadas de função do que a minha versão ...
Tom
% Z e% z ainda imprimem espaços em branco.
Pavel Vlasov
2
está incorreto. Falha se o fuso horário local não for UTC. mktime()espera a hora local como a entrada. fromtimestamp()retorna a hora local, não utc. Se você corrigi-lo, em seguida, ver esses problemas adicionais (como dateutil, solução apenas de stdlib pode falhar)
JFS
5

Simples

Eu fiz assim:

>>> utc_delta = datetime.utcnow()-datetime.now()
>>> utc_time = datetime(2008, 9, 17, 14, 2, 0) + utc_delta
>>> print(utc_time)
2008-09-17 19:01:59.999996

Implementação chique

Se você quer ser extravagante, pode transformar isso em um functor:

class to_utc():
    utc_delta = datetime.utcnow() - datetime.now()

    def __call__(cls, t):
        return t + cls.utc_delta

Resultado:

>>> utc_converter = to_utc()
>>> print(utc_converter(datetime(2008, 9, 17, 14, 2, 0)))
2008-09-17 19:01:59.999996
uclatommy
fonte
5
isso não funciona no horário de verão - por exemplo, se for verão e a data em que você estiver convertendo for inverno. e a pergunta foi sobre a conversão de datas armazenadas como strings ...
Tom
1
PARA SUA INFORMAÇÃO. O maior problema com esse método (além do horário de verão) são os poucos milissegundos que o delta passará. Todos os meus convites da agenda são exibidos em 1 minuto.
Nostalg.io 15/10
4

Você pode fazer isso com:

>>> from time import strftime, gmtime, localtime
>>> strftime('%H:%M:%S', gmtime()) #UTC time
>>> strftime('%H:%M:%S', localtime()) # localtime
Cristian Salamea
fonte
3

E se -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

se segundos são None, ele converte a hora local em UTC. Caso contrário, converte a hora passada em UTC.

Tom
fonte
2

Para se locomover no horário de verão, etc.

Nenhuma das respostas acima me ajudou particularmente. O código abaixo funciona para GMT.

def get_utc_from_local(date_time, local_tz=None):
    assert date_time.__class__.__name__ == 'datetime'
    if local_tz is None:
        local_tz = pytz.timezone(settings.TIME_ZONE) # Django eg, "Europe/London"
    local_time = local_tz.normalize(local_tz.localize(date_time))
    return local_time.astimezone(pytz.utc)

import pytz
from datetime import datetime

summer_11_am = datetime(2011, 7, 1, 11)
get_utc_from_local(summer_11_am)
>>>datetime.datetime(2011, 7, 1, 10, 0, tzinfo=<UTC>)

winter_11_am = datetime(2011, 11, 11, 11)
get_utc_from_local(winter_11_am)
>>>datetime.datetime(2011, 11, 11, 11, 0, tzinfo=<UTC>)
Dantalion
fonte
2

Usando http://crsmithdev.com/arrow/

arrowObj = arrow.Arrow.strptime('2017-02-20 10:00:00', '%Y-%m-%d %H:%M:%S' , 'US/Eastern')

arrowObj.to('UTC') or arrowObj.to('local') 

Esta biblioteca facilita a vida :)

Yash
fonte
seta é incrível: arrow.get (datetime.now (), 'local') a ( 'UTC') ingênuo..
SteveJ
1

Encontrei a melhor resposta em outra pergunta aqui . Ele usa apenas bibliotecas internas python e não exige que você insira seu fuso horário local (um requisito no meu caso)

import time
import calendar

local_time = time.strptime("2018-12-13T09:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
local_seconds = time.mktime(local_time)
utc_time = time.gmtime(local_seconds)

Estou reposicionando a resposta aqui, pois essa pergunta aparece no google em vez da pergunta vinculada, dependendo das palavras-chave da pesquisa.

franksands
fonte
1

Eu tenho esse código em um dos meus projetos:

from datetime import datetime
## datetime.timezone works in newer versions of python
try:
    from datetime import timezone
    utc_tz = timezone.utc
except:
    import pytz
    utc_tz = pytz.utc

def _to_utc_date_string(ts):
    # type (Union[date,datetime]]) -> str
    """coerce datetimes to UTC (assume localtime if nothing is given)"""
    if (isinstance(ts, datetime)):
        try:
            ## in python 3.6 and higher, ts.astimezone() will assume a
            ## naive timestamp is localtime (and so do we)
            ts = ts.astimezone(utc_tz)
        except:
            ## in python 2.7 and 3.5, ts.astimezone() will fail on
            ## naive timestamps, but we'd like to assume they are
            ## localtime
            import tzlocal
            ts = tzlocal.get_localzone().localize(ts).astimezone(utc_tz)
    return ts.strftime("%Y%m%dT%H%M%SZ")
tobixen
fonte
0

Em python3:

pip install python-dateutil

from dateutil.parser import tz

mydt.astimezone(tz.gettz('UTC')).replace(tzinfo=None) 
spedy
fonte
este não parece trabalho, ele gera uma exceção ValueError: ValueError: astimezone () não pode ser aplicado a uma data e hora ingênuo
Stormsson
Deve ser: from dateutil import tz
Javier
-3

E se -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

se segundos são None, ele converte a hora local em UTC. Caso contrário, converte a hora passada em UTC.

Mohammad Efazati
fonte
1
Isso não ajuda, a questão era converter uma determinada string de horário local em uma string utc.
Tom