Como obtenho um valor de datetime.today () no Python que esteja “consciente do fuso horário”?

310

Estou tentando subtrair um valor de data do valor de datetime.today()para calcular há quanto tempo algo estava. Mas reclama:

TypeError: can't subtract offset-naive and offset-aware datetimes

O valor datetime.today()não parece "consciente do fuso horário", enquanto o meu outro valor de data é. Como obtenho um valor datetime.today()ciente do fuso horário?

No momento, está me dando a hora no horário local, que é PST, ou seja, UTC - 8 horas. Na pior das hipóteses, existe uma maneira de inserir manualmente um valor de fuso horário no datetimeobjeto retornado por datetime.today()e defini-lo como UTC-8?

Obviamente, a solução ideal seria conhecer automaticamente o fuso horário.

ladrão de mente
fonte
10
Parece que podemos usar datetime.now().astimezone()desde Python 3.6
johnchen902

Respostas:

362

Na biblioteca padrão, não há uma maneira multiplataforma de criar fusos horários conscientes sem criar sua própria classe de fuso horário.

No Windows, existe win32timezone.utcnow(), mas isso faz parte do pywin32. Prefiro sugerir o uso da biblioteca pytz , que possui um banco de dados constantemente atualizado da maioria dos fusos horários.

Trabalhar com fusos horários locais pode ser muito complicado (consulte os links de "Leituras adicionais" abaixo), portanto, você pode usar o UTC em todo o aplicativo, especialmente para operações aritméticas, como calcular a diferença entre dois pontos no tempo.

Você pode obter a data / hora atual da seguinte forma:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Lembre-se disso datetime.today()e datetime.now()retorne a hora local , não a hora UTC, portanto, aplicar .replace(tzinfo=pytz.utc)a eles não seria correto.

Outra boa maneira de fazer isso é:

datetime.now(pytz.utc)

que é um pouco mais curto e faz o mesmo.


Leitura / observação adicional por que preferir o UTC em muitos casos:

AndiDog
fonte
75
Que tal em datetime.now(pytz.utc)vez de datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro
5
now(utc)não retorna hoje (a menos que seja meia-noite no UTC), retorna o horário atual no UTC. Você precisa também .replace(hour=0, minute=0, ...)de obter o início do dia (como datetime.today())
JFS
1
Os documentos dizem que today()retorna o horário atual, não a meia-noite. Se houver um caso de uso em que a meia-noite é necessária, sim, a substituição precisará ser feita de acordo. Como a pergunta original era sobre diferença de data e hora, não acho que seja necessária meia-noite.
AndiDog
1
@ AndiDog: Meu comentário implica que eu pensei (incorretamente) que datetime.today()é combine(date.today(), time()). datetimepossui métodos .now()e .today()métodos que (como você apontou corretamente) retornam (quase) a mesma coisa. Não existe date.now()método. datee datetimeobjetos não são intercambiáveis. Usar um objeto datetime em vez de um dateobjeto pode produzir erros sutis; Não vejo nenhuma razão para datetime.today()existir se for quase uma duplicata de datetime.now().
precisa saber é
6
Adicionando a esta resposta, se você estiver usando django, sempre use em timezone.now()vez de, datetime.now()pois ele usará o UTC automaticamente se USE_TZ = True. timezoneé localizar em django.utils.timezone, documentação: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan
107

Obtenha a hora atual, em um fuso horário específico:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))
philfreo
fonte
2
Veja isso .
Wim
1
você NÃO deve usar o horário localizado, exceto a saída. Muitas coisas dão errado ao usar o horário do fuso horário: um simples timedelta não leva em consideração o horário de verão, a menos que você esteja no horário UTC. Sempre use fuso horário com base no UTC. converter para fuso horário local na saída quando necessário.
MrE 13/09/17
4
Reiterar um desacordo com o @MrE que eu já havia expressado nos comentários sobre a resposta aceita: existem razões perfeitamente válidas para trabalhar com datetime localizados e "você NÃO deve usar o horário localizado, exceto a saída", é um conselho muito amplo. Suponha que você esteja adicionando 1 dia a uma data e hora algumas horas antes de um limite de horário de verão no qual os relógios retornam uma hora. Qual resultado você quer? Se você acha que a hora deve ser a mesma, use datas localizadas. Se você acha que deve demorar uma hora antes, use o UTC ou as datas do fuso horário. O que faz sentido depende do domínio.
Mark Amery
@MarkAmery, tanto quanto posso concordar que você pode adicionar ou subtrair vários dias ou horas e não se importar com problemas de fuso horário (como o seu exemplo), esse comentário refere-se à passagem do tempo corrigido do fuso horário para um cliente. Como o Python é usado principalmente para processos de back-end, ele passa o tempo para o cliente. O servidor sempre deve passar a data / hora no UTC e o cliente deve convertê-lo em sua própria data / hora / fuso horário local; caso contrário, coisas ruins acontecem: basta verificar a saída datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))e ver se é isso que você esperava
MrE
"O servidor sempre deve passar a data / hora no UTC e o cliente deve convertê-lo em sua própria data / hora / fuso horário local" - não, isso não é universalmente verdade. Às vezes, o uso do fuso horário do cliente é inadequado e o fuso horário apropriado precisa ser transmitido como parte dos dados. Se, como londrino, vejo os horários das reuniões de um clube de xadrez de São Francisco em seu site, devo vê-los no horário de São Francisco, não no horário de Londres.
Mark Amery
69

No Python 3, a biblioteca padrão facilita muito a especificação do UTC como o fuso horário:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

Se você deseja uma solução que use apenas a biblioteca padrão e que funcione no Python 2 e no Python 3, consulte a resposta do jfs .

Se você precisar do fuso horário local, não do UTC, consulte a resposta de Mihai Capotă

Flimm
fonte
19

Aqui está uma solução stdlib que funciona em Python 2 e 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

onde todayé uma instância de data e hora consciente que representa o início do dia (meia-noite) no UTC e utcé um objeto tzinfo ( exemplo da documentação ):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Relacionado: comparação de desempenho de várias maneiras de obter a meia-noite (início de um dia) para um determinado horário UTC . Nota: é mais complexo chegar meia-noite para um fuso horário com um deslocamento UTC não fixo .

jfs
fonte
16

Outro método para construir o objeto datetime com reconhecimento de fuso horário que representa o horário atual:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  
Dariusz Walczak
fonte
nota que pytz.utce pytz.UTCsão ambos definidos (e são as mesmas)
drevicko
2
Essa resposta é melhor que a aceita, pois é mais universal: o replace()fuso horário geralmente é propenso a erros na maioria dos outros usos, enquanto o localize()ing é a maneira preferida de atribuir o fuso horário a registros de data e hora ingênuos.
Antony Hatchkins
@AntonyHatchkins: o .localize()método falha para horários locais ambíguos (entrada não utc). A resposta da @ philfreo que usa.now(pytz_timezone) continua a funcionar nesses casos.
jfs
Conforme especificado nos documentos python, .now(pytz_timezone)faz exatamente o mesmo que localize(utcnow)- primeiro, gera o horário atual no UTC, depois atribui a ele um fuso horário: "<...> Nesse caso, o resultado é equivalente a tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Ambas as respostas estão corretas e funcionam sempre.
Antony Hatchkins
1
O único horário ingênuo (não utc) que pode ser conscientizado com fuso horário é agora : o sistema subjacente deve saber o valor UTC e, pytzatravés do OLSON db, deve saber como convertê-lo em qualquer fuso horário do mundo. Tornar ciente de qualquer outro fuso horário ingênuo (não utc) é difícil por causa da ambiguidade durante os turnos de verão. Isso não é problema .localize(alimentar um is_dstvalor faz com que funcione em qualquer data). Esse é um problema inerente à prática de horário de verão.
Antony Hatchkins
16

Um one-liner usando apenas a biblioteca padrão funciona a partir do Python 3.3. Você pode obter um datetimeobjeto com reconhecimento de fuso horário local usando astimezone(como sugerido por johnchen902 ):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))
Mihai Capotă
fonte
2
A documentação é uma grande ajuda, encontrada aqui: docs.python.org/3.8/library/… . Essa parte incrivelmente básica da funcionalidade está escondida em um parágrafo obscuro da documentação, para que essa resposta do stackoverflow seja efetivamente o único lugar em toda a Internet com essas informações. Na documentação, também pode ser visto que, desde o Python 3.6, datetime.now()pode ser chamado sem argumentos e retornar o resultado local correto ( datetimepresume-se que ingênuos s estejam no fuso horário local).
atimholt
8

Se você estiver usando o Django , poderá definir datas sem reconhecimento de tz (somente UTC ).

Comente a seguinte linha em settings.py:

USE_TZ = True
Laffuste
fonte
8
onde você viu o django mencionado nesta pergunta?
vonPetrushev
1
Uma alma gentil excluiu meu pedido de desculpas anterior aqui, então novamente: vergonha para mim, resposta errada, pois a pergunta não é específica do Django. Deixei-o porque ele pode ajudar alguns usuários de qualquer maneira, mas vou excluí-lo quando a pontuação se aproximar de 0. Se essa resposta for inadequada, sinta-se à vontade para fazer um voto negativo.
laffuste 14/01
6

O pytz é uma biblioteca Python que permite cálculos precisos e de fuso horário entre plataformas usando o Python 2.3 ou superior.

Com o stdlib, isso não é possível.

Veja uma pergunta semelhante no SO .

user225312
fonte
6

Aqui está uma maneira de gerá-lo com o stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

date armazenará a data local e o deslocamento do UTC , não a data no fuso horário UTC, para que você possa usar esta solução se precisar identificar em qual fuso horário a data é gerada . Neste exemplo e no meu fuso horário local:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

A chave é adicionar a %zdiretiva à representação FORMAT, para indicar o deslocamento UTC da estrutura de tempo gerada. Outros formatos de representação podem ser consultados nos documentos do módulo datetime

Se você precisar da data no fuso horário UTC, poderá substituir time.localtime () por time.gmtime ()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Editar

Isso funciona apenas em python3 . A diretiva z não está disponível no código _strptime.py do python 2

jcazor
fonte
ValueError: 'z' é uma diretiva incorreta no formato '% Y-% m-% dT% H:% M:% S% z'
jno
Você está no python 2, certo? Infelizmente, parece que a directiva z não está disponível em python 2. código _strptime.py
jcazor
6

Use dateutil, conforme descrito em Python datetime.datetime.now (), que reconhece o fuso horário :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())
G. Führ
fonte
1
Veja esta resposta de JF Sebastian para uma situação em que isso resulta incorretamente.
Antony Hatchkins
2
Acho que o erro na outra postagem é relevante apenas em casos de uso específicos. A tzlocal()função ainda é uma das soluções mais simples e definitivamente deve ser mencionada aqui.
user8162
2

Obter uma data com reconhecimento de utcfuso horário no fuso horário é suficiente para que a subtração de data funcione.

Mas se você deseja uma data com reconhecimento de fuso horário no seu fuso horário atual, tzlocalé o caminho a seguir:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

O PS dateutiltem uma função semelhante ( dateutil.tz.tzlocal). Mas apesar de compartilhar o nome, ele tem uma base de código completamente diferente, que conforme observado por JF Sebastian pode dar resultados errados.

Antony Hatchkins
fonte
python é geralmente usado no servidor. O fuso horário local em um servidor geralmente não faz sentido e sempre deve ser definido como UTC. Definir datetime tzinfo dessa maneira falha em alguns casos. use melhor o UTC e localize no fuso horário desejado apenas na saída. qualquer cálculo de timedelta, por exemplo, não considera o horário de verão; portanto, isso deve ser feito no UTC e localizado.
MrE 13/09/17
@ MrE errado, offtopic, exemplos?
Antony Hatchkins
tente usar um objeto de data e hora localizado em um fuso horário que observe o horário de verão, adicione vários dias para alterar o estado do horário de verão e você verá que a operação em objetos de data e hora no fuso horário localizado falha e não respeita o horário de verão. Daí meu comentário de que você SEMPRE deve executar qualquer operação de data e hora no horário UTC.
MrE 15/09
o ponto é: não faça isso, faça suas operações no UTC e use datetime.astimezone (fuso horário) para converter para o fuso horário local na saída.
MrE 15/09
2

Aqui está uma solução usando um fuso horário legível e que funciona com today ():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

Você pode listar todos os fusos horários da seguinte maneira:

import pytz

pytz.all_timezones
pytz.common_timezones # or
JohnAndrews
fonte
1

Especialmente para fusos horários não UTC:

O único fuso horário que possui seu próprio método é timezone.utc, mas você pode alterar um fuso horário com qualquer deslocamento UTC, se precisar, usando timedelta& timezonee forçando-o usando .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Usar timezone(timedelta(hours=n))como fuso horário é a verdadeira bala de prata aqui, e possui muitos outros aplicativos úteis.

tmck-code
fonte
0

Se você obtiver data e hora atuais no python, importe a data e a hora, o pacote pytz no python depois de obter a data e a hora atuais como ..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))
jigar vagadiya
fonte
0

Outra alternativa, na minha opinião uma melhor, é usar em Pendulumvez de pytz. Considere o seguinte código simples:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

Para instalar o Pendulum e ver a documentação deles, clique aqui . Possui inúmeras opções (como suporte simples ao formato ISO8601, RFC3339 e muitos outros), melhor desempenho e tende a gerar código mais simples.

ng10
fonte
Não sei por que a votação aqui em baixo, este código está funcionando em vários programas que executam 7/24 para mim :). não que eu me importe com outra opinião, mas diga por que não está funcionando para você, para permitir que eu verifique. Agradecemos antecipadamente
ng10 16/03/19
0

Use o fuso horário, como mostrado abaixo, para uma data e hora com reconhecimento de fuso horário. O padrão é UTC:

from django.utils import timezone
today = timezone.now()
Anupama V Iyengar
fonte
0

Tyler, de 'howchoo', fez um ótimo artigo que me ajudou a ter uma idéia melhor dos Objetos do Datetime, link abaixo

Trabalhando com data e hora

essencialmente, acabei de adicionar o seguinte ao final dos meus objetos datetime

.replace(tzinfo=pytz.utc)

Exemplo:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)
Jose
fonte
0

tente pnp_datetime , todo o tempo usado e retornado está no fuso horário e não causará nenhum problema que seja ingênuo e sensível ao deslocamento.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)
cloudup
fonte
0

Deve-se enfatizar que, desde o Python 3.6, você só precisa da lib padrão para obter um objeto datetime com reconhecimento de fuso horário que represente a hora local (a configuração do seu sistema operacional). Usando astimezone ()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(veja o comentário de @ johnchen902).

MrFuppes
fonte