Por que datetime.datetime.utcnow () não contém informações de fuso horário?

285
datetime.datetime.utcnow()

Por que isso datetimenão possui nenhuma informação de fuso horário, pois é explicitamente um UTC datetime?

Eu esperaria que isso contivesse tzinfo.

Vitaly Babiy
fonte
Como converter um campo de data normal no formato iso, que é do tipo string para o formato utc?
Navi

Respostas:

192

Isso significa que é um fuso horário ingênuo, então você não pode usá-lo com datetime.astimezone

você pode atribuir um fuso horário como este

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

agora você pode mudar de fuso horário

print(u.astimezone(pytz.timezone("America/New_York")))

Para obter a hora atual em um determinado fuso horário, você pode passar o tzinfo para datetime.now()diretamente:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

Funciona para qualquer fuso horário, incluindo aqueles que observam o horário de verão (DST), ou seja, funciona para fusos horários que podem ter diferentes desvios de utc em momentos diferentes (deslocamento de utc não fixo). Não use tz.localize(datetime.now())- pode falhar durante a transição no final do horário de verão quando a hora local é ambígua.

John La Rooy
fonte
216
Mas não há uma boa razão para que o fuso horário seja ingênuo - ele é especificado como UTC. Por que você precisa procurar uma biblioteca de terceiros para fazê-la funcionar corretamente?
Mark Ransom
4
Concordo; para mim, os tempos 'ingênuos' são completamente inúteis. No momento, há uma discussão na lista python sobre como adicionar pytz ao stdlib; o problema não é o licenciamento, mas o fato de os dados do fuso horário serem atualizados com tanta frequência (o que o próprio Python não pode ser). O pytz também não implementa a interface tzinfo da maneira esperada, para que você possa obter erros se tentar usar alguns dos fusos horários da cidade astimezone. Portanto, datetime não apenas não possui fusos horários nativos, mas a única implementação amplamente disponível do tzinfo não é compatível com o suposto padrão.
24910
5
@bobince Por que o pytz e as bibliotecas de data e hora padrão não funcionam para você? O núcleo e o pytz do Python, evoluindo como projetos independentes, reduzem a complexidade logística da equipe principal. Sim, reduzir a complexidade da equipe principal do Python aumenta a complexidade de todos os usuários do Python que precisam lidar com fusos horários, mas confio que eles tenham tomado essa decisão por um bom motivo. A regra "A biblioteca padrão não possui instâncias tzinfo ..." é ótima porque é simples, por que fazer uma exceção aqui?
Derek Litz
15
Que tal apenasu=datetime.now(pytz.utc)
Craig McQueen
4
@ banho: não use tz.localize(datetime.now()); use em datetime.now(tz)vez disso.
JFS
142

Observe que, para o Python 3.2 em diante, o datetimemódulo contém datetime.timezone. A documentação para datetime.utcnow()diz:

Um datetime UTC atual consciente pode ser obtido chamando .datetime.now(timezone.utc)

Então você pode fazer:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Craig McQueen
fonte
2
Qual é o preferido? datetime.now(timezone.utc)ou datetime.utcnow(timezone.utc)?
Jesse Webb
8
datetime.utcnow()não aceita argumentos. Então teria que ser datetime.now(timezone.utc).
Craig McQueen
1
datetime.now()retornará a hora da máquina, mas datetime.utcnow()retornará a hora UTC real.
Babu
13
@BuBu: datetime.utcnow()não definido tzinfopara indicar que é UTC. Mas datetime.now(datetime.timezone.utc)retorna a hora UTC com tzinfo set.
Craig McQueen
@CraigMcQueen Então, se passarmos um tzobjeto no construtor now, ele retornará o tempo desse fuso horário? Está bem! Obrigado por apontar.
Babu
71

As bibliotecas padrão do Python não incluem nenhuma classe tzinfo (mas consulte pep 431 ). Só posso adivinhar as razões. Pessoalmente, acho que foi um erro não incluir uma classe tzinfo para o UTC, porque essa é incontroversa o suficiente para ter uma implementação padrão.

Editar: Embora não exista implementação na biblioteca, existe um exemplo na tzinfodocumentação .

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

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

    def dst(self, dt):
        return ZERO

utc = UTC()

Para usá-lo, para obter o horário atual como um objeto de data e hora consciente:

from datetime import datetime 

now = datetime.now(utc)

Existe datetime.timezone.utcno Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Mark Ransom
fonte
8
Vai entender por que motivo esta classe não foi fornecido em primeiro lugar (e, mais importante, usado para datetimeobjetos criados por utcnow()) ...
André Caron
17
O objeto de fuso horário timezone.utcfoi finalmente adicionado ao Python 3.2. Para compatibilidade com versões anteriores, utcnow()ainda retorna um objeto de tempo sem fuso horário, mas você pode obter o que deseja chamando now(timezone.utc).
mhsmith
4
@rgove, esse é o tipo de correção de erros que deveria ser um jogo justo para o Python 3. Eles não deveriam ter se preocupado com a compatibilidade com versões anteriores. Há outro exemplo que li nos últimos dias - o structmódulo faria conversões automáticas de Unicode para bytestring e a decisão final foi quebrar a compatibilidade com versões anteriores do Python 3 para impedir que uma decisão ruim seguisse adiante.
Mark Ransom
2
Estou impressionado que a tzinfodocumentação do Python inclua exemplos de código para implementá-lo, mas eles não incluem essa funcionalidade no próprio datetime! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@LS sim, pytzé um ótimo recurso. No momento em que editei minha resposta para inserir o código de exemplo, alguém já havia sugerido e eu não queria roubar seus trovões.
Mark Ransom
20

O pytzmódulo é uma opção e existe outra python-dateutil, que, embora também seja um pacote de terceiros, pode já estar disponível dependendo de suas outras dependências e sistema operacional.

Eu só queria incluir essa metodologia para referência - se você já instalou python-dateutilpara outros fins, pode usá-la em tzinfovez de duplicar compytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

Costumo concordar que as chamadas para utcnowdevem incluir as informações do fuso horário UTC. Suspeito que isso não esteja incluído porque a biblioteca de data / hora nativa é padronizada como data / hora ingênua para compatibilidade cruzada.

bbengfort
fonte
1
NameError: o nome 'dt' não está definido
xApple 23/07
Eu estava usando a chamada datetime.datetime.utcfromtimestamp () e precisando adicionar o tzinfo. A segunda solução funcionou para mim: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee
1
nota: diferente do datetime.now(pytz_tz)que sempre funciona; datetime.now(dateutil.tz.tzlocal())pode falhar durante transições de horário de verão . PEP 495 - A desambiguação na hora local pode melhorar a dateutilsituação no futuro.
JFS
@IanLee: você pode usar utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(nota: dateutilcom um deslocamento utc não fixo (como dateutil.tz.tzlocal()) pode falhar aqui , use uma pytzsolução baseada em vez ).
jfs
Desde o meu programa já estava importando dateutilpara dateutil.parser, eu gostei esta solução melhor. Era tão simples como: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Viola!!
LS
11

Julien Danjou escreveu um bom artigo explicando por que você nunca deve lidar com fusos horários . Um trecho:

De fato, a API de data e hora do Python sempre retorna objetos de data e hora desconhecidos, o que é muito lamentável. De fato, assim que você obtém um desses objetos, não há como saber qual é o fuso horário; portanto, esses objetos são "inúteis" por si próprios.

Infelizmente, mesmo que você possa usá- utcnow()lo, ainda não verá as informações do fuso horário, como descobriu.

Recomendações:

  • Sempre use datetimeobjetos conscientes , ou seja, com informações de fuso horário. Isso garante que você possa compará-los diretamente ( datetime objetos conscientes e inconscientes não são comparáveis) e os retornará corretamente aos usuários. Aproveite o pytz para ter objetos de fuso horário.

  • Use ISO 8601 como formato de sequência de entrada e saída. Use datetime.datetime.isoformat()para retornar os carimbos de data e hora como string formatada usando esse formato, que inclui as informações do fuso horário.

  • Se você precisar analisar seqüências de caracteres contendo carimbos de data e hora no formato ISO 8601, poderá confiar nele iso8601, que retornará carimbos de data e hora com informações corretas do fuso horário. Isso torna os timestamps diretamente comparáveis.

Joe D'Andrea
fonte
1
Essa é uma recomendação um pouco enganadora. A regra geral é, nunca lide com fusos horários. Sempre armazene e transmita tz objetos unc utc (objetos de época). Fuso horário só deve ser calculada no momento da representação na UI
Nehem
1
Parece que já combina com os pensamentos de Julien muito bem. Quais de suas recomendações específicas (como mencionado acima) são enganosas?
31517 Joe D'Andrea
10

Para adicionar timezoneinformações no Python 3.2 ou superior

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
nordborn
fonte
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (padrão, 19 de janeiro de 2017, 14:48:08)
Marcin Owsiany 28/11
-6
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Mrudula Athuluri
fonte
-13

As datas UTC não precisam de nenhuma informação de fuso horário, pois são UTC, o que, por definição, significa que elas não têm deslocamento.

Ignacio Vazquez-Abrams
fonte
10
Até onde eu sei , em docs.python.org/library/datetime.html , um datetime sem um tzinfo é aquele em que o fuso horário não é especificado. Aqui o fuso horário tem sido especificado, então logicamente que deve estar presente. Há uma grande diferença entre uma data / hora sem um fuso horário associado e outra que esteja definitivamente no UTC. (Idealmente, devem ser diferentes tipos da OMI, mas isso é outro assunto ...)
Jon Skeet
@ JonSkeet Acho que você está perdendo o ponto de Ignacio de que o UTC não é um fuso horário. Incrível que esta resposta tem -9 pontuação como eu escreva isso ...
CS
3
@ CS: Bem, Ignacio nunca afirmou que ... e, embora estritamente falando, o UTC não seja um fuso horário, geralmente é tratado como um para tornar a vida consideravelmente mais simples (inclusive em Python, por exemplo, com pytz.utc). Observe que há uma grande diferença entre um valor cujo deslocamento em relação ao UTC é desconhecido e um em que se sabe que é 0. O último é o que utcnow() deve retornar, IMO. Isso se encaixaria com "Um objeto consciente é usado para representar um momento específico no tempo que não está aberto à interpretação", conforme a documentação.
precisa saber é o seguinte