Strptime Python () e fusos horários?

157

Eu tenho um arquivo de despejo CSV de um backup do IPD do Blackberry, criado usando o IPDDump. As cadeias de data / hora aqui são mais ou menos assim (onde ESTé um fuso horário australiano):

Tue Jun 22 07:46:22 EST 2010

Eu preciso ser capaz de analisar esta data no Python. No começo, tentei usar a strptime()função do horário do datett.

>>> datetime.datetime.strptime('Tue Jun 22 12:10:20 2010 EST', '%a %b %d %H:%M:%S %Y %Z')

No entanto, por algum motivo, o datetimeobjeto que volta não parece ter nenhum tzinfoassociado.

Eu li nesta página que aparentemente datetime.strptimedescarta silenciosamente tzinfo, no entanto, verifiquei a documentação e não consigo encontrar nada nesse sentido documentado aqui .

Consegui obter a data analisada usando uma biblioteca Python de terceiros, dateutil , no entanto, ainda estou curioso para saber como estava usando o construído strptime()incorretamente? Existe alguma maneira strptime()de jogar bem com os fusos horários?

victorhooi
fonte
1
Você não pode simplesmente ... converter todas as datas para GMT?
Robus
2
@Robus: Hmm, eu esperava fazer isso - mas eu estava assumindo que strftime / datetime poderia de alguma forma fazer isso? De qualquer forma, eu preciso armazenar / analisar o fato de que as horas estão no fuso horário EST ou em qualquer fuso horário que elas acontecerem comigo. O script precisa ser capaz de analisar horários genéricos com informações de fuso horário (por exemplo, ETC pode ser qualquer outro fuso horário).
Victorhooi
3
EST também é uma abreviação de fuso horário nos EUA. (Da mesma forma, a BST é uma abreviação de fuso horário no Reino Unido e no Brasil.) Essas abreviações são apenas inerentemente ambíguas. Use deslocamentos relativos ao UTC / GMT. (Se você precisar de apoio abreviaturas, você precisa fazer o mapeamento dependente da localidade e isso é uma bagunça rat buracos.)
Donal Fellows

Respostas:

58

A datetimedocumentação do módulo diz:

Retorne um datetime correspondente a date_string, analisado de acordo com o formato. Isso é equivalente a datetime(*(time.strptime(date_string, format)[0:6])).

Está vendo isso [0:6]? Isso te pega (year, month, day, hour, minute, second). Nada mais. Nenhuma menção de fusos horários.

Curiosamente, [Win XP SP2, Python 2.6, 2.7] passando o seu exemplo para time.strptimenão funciona, mas se você retirar o "% Z" e o "EST", ele funcionará. Também usando "UTC" ou "GMT" em vez de "EST" funciona. "PST" e "MEZ" não funcionam. Intrigante.

Vale ressaltar que isso foi atualizado a partir da versão 3.2 e a mesma documentação agora também afirma o seguinte:

Quando a diretiva% z é fornecida ao método strptime (), um objeto de data / hora consciente será produzido. O tzinfo do resultado será definido como uma instância do fuso horário.

Observe que isso não funciona com% Z, portanto, o caso é importante. Veja o seguinte exemplo:

In [1]: from datetime import datetime

In [2]: start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z')

In [3]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: None

In [4]: start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z')

In [5]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: UTC+10:00
John Machin
fonte
13
Bug do Python relacionado: % Z no strptime não corresponde ao EST e outros
jfs
353

Eu recomendo usar python-dateutil . Seu analisador foi capaz de analisar todos os formatos de data que eu joguei até agora.

>>> from dateutil import parser
>>> parser.parse("Tue Jun 22 07:46:22 EST 2010")
datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzlocal())
>>> parser.parse("Fri, 11 Nov 2011 03:18:09 -0400")
datetime.datetime(2011, 11, 11, 3, 18, 9, tzinfo=tzoffset(None, -14400))
>>> parser.parse("Sun")
datetime.datetime(2011, 12, 18, 0, 0)
>>> parser.parse("10-11-08")
datetime.datetime(2008, 10, 11, 0, 0)

e assim por diante. Sem lidar com strptime()bobagens de formato ... basta lançar uma data para ela e ela faz a coisa certa.

Atualização : Opa. Eu perdi na sua pergunta original que você mencionou que usava dateutil, desculpe por isso. Mas espero que essa resposta ainda seja útil para outras pessoas que se deparam com essa pergunta quando têm perguntas de análise de datas e veem a utilidade desse módulo.

Joe Shaw
fonte
Dado que muitas pessoas tendem a usar python-dateutil, eu gostaria de apontar uma limitação dessa lib. >>> parser.parse("Thu, 25 Sep 2003 10:49:41,123 -0300") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 748, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 310, in parse res, skipped_tokens = self._parse(timestr, **kwargs) TypeError: 'NoneType' object is not iterable
wanghq
1
@wanghq, você precisa substituir a última vírgula por ponto. Entãoparser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300") returns: datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800))
flyingfoxlee
7
@flyingfoxlee, sim, eu entendo isso. Eu só quero dizer às pessoas a limitação do python-dateutil. Faz coisas mágicas, mas às vezes falha em fazer isso. Então, "basta marcar uma data e isso faz a coisa certa". não é 100% verdadeiro.
Wanghq
4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")retornos: datetime.datetime(2016, 10, 27, 9, 6)falha para descobrir o fuso horário ...
HaPsantran
2
Depende do objetivo. dateutil parserpode ser simples de usar, mas strptime()é mais rápido. Além disso, seus formatos são bastante fáceis de aprender.
arrebatamento
9

Sua sequência de horas é semelhante ao formato de hora na rfc 2822 (formato da data no email, cabeçalhos http) . Você pode analisá-lo usando apenas stdlib:

>>> from email.utils import parsedate_tz
>>> parsedate_tz('Tue Jun 22 07:46:22 EST 2010')
(2010, 6, 22, 7, 46, 22, 0, 1, -1, -18000)

Veja soluções que geram objetos de data e hora com reconhecimento de fuso horário para várias versões do Python: analisando a data com o fuso horário de um email .

Nesse formato, ESTé semanticamente equivalente a-0500 . Embora, em geral, uma abreviação de fuso horário não seja suficiente, para identificar um fuso horário exclusivamente .

jfs
fonte
0

Encontrei exatamente esse problema.

O que acabei fazendo:

# starting with date string
sdt = "20190901"
std_format = '%Y%m%d'

# create naive datetime object
from datetime import datetime
dt = datetime.strptime(sdt, sdt_format)

# extract the relevant date time items
dt_formatters = ['%Y','%m','%d']
dt_vals = tuple(map(lambda formatter: int(datetime.strftime(dt,formatter)), dt_formatters))

# set timezone
import pendulum
tz = pendulum.timezone('utc')

dt_tz = datetime(*dt_vals,tzinfo=tz)
Christopher
fonte