Python strptime () e fusi orari?


157

Ho un dumpfile CSV da un backup IPD di Blackberry, creato usando IPDDump. Le stringhe di data / ora qui assomigliano a questo (dove si ESTtrova un fuso orario australiano):

Tue Jun 22 07:46:22 EST 2010

Devo essere in grado di analizzare questa data in Python. Inizialmente, ho provato a usare la strptime()funzione da datettime.

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

Tuttavia, per qualche motivo, l' datetimeoggetto che ritorna non sembra esserne tzinfoassociato.

Ho letto su questa pagina che apparentemente datetime.strptimescarta in silenzio tzinfo, tuttavia, ho controllato la documentazione e non riesco a trovare nulla in tal senso documentato qui .

Sono stato in grado di ottenere la data analizzata utilizzando una libreria Python di terze parti, dateutil , tuttavia sono ancora curioso di sapere come stavo usando in-built in strptime()modo errato? C'è un modo strptime()per giocare bene con i fusi orari?


1
Non puoi semplicemente ... convertire tutte le date in GMT?
Robus,

2
@Robus: Hmm, speravo di farlo - ma stavo assumendo che strftime / datetime potesse in qualche modo farlo? In entrambi i casi, devo archiviare / analizzare il fatto che i tempi sono nel fuso orario EST, o qualunque sia il fuso orario che mi capita. Lo script deve essere in grado di analizzare periodi di tempo generici con informazioni sul fuso orario (ad es. ETC potrebbe essere qualsiasi altro fuso orario).
victorhooi,

3
EST è anche un'abbreviazione del fuso orario statunitense. (Allo stesso modo, BST è sia un abbreviato fuso orario britannico che brasiliano.) Tali abbreviazioni sono intrinsecamente ambigue. Utilizzare invece gli offset relativi a UTC / GMT. (Se devi supportare le abbreviazioni, devi rendere la mappatura dipendente dalla locale e questo è un buco di topo disordinato.)
Donal Fellows

Risposte:


58

La datetimedocumentazione del modulo dice:

Restituisce un datetime corrispondente a date_string, analizzato in base al formato. Questo equivale a datetime(*(time.strptime(date_string, format)[0:6])).

Vedi quello [0:6]? Questo ti prende (year, month, day, hour, minute, second). Nient'altro. Nessuna menzione di fusi orari.

È interessante notare che [Win XP SP2, Python 2.6, 2.7] passando l'esempio time.strptimenon funziona ma se si rimuove "% Z" e "EST" funziona. Anche usando "UTC" o "GMT" invece di "EST" funziona. "PST" e "MEZ" non funzionano. Sconcertante.

Vale la pena notare che questo è stato aggiornato dalla versione 3.2 e la stessa documentazione ora afferma anche quanto segue:

Quando la direttiva% z viene fornita al metodo strptime (), verrà prodotto un oggetto datetime consapevole. Il tzinfo del risultato verrà impostato su un'istanza del fuso orario.

Nota che questo non funziona con% Z, quindi il caso è importante. Vedi il seguente esempio:

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


354

Consiglio di usare python-dateutil . Il suo parser è stato in grado di analizzare ogni formato di data che ho lanciato finora.

>>> 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 così via. Non hai a che fare con strptime()assurdità del formato ... basta lanciarci una data e fa la cosa giusta.

Aggiornamento : Oops. Mi mancava la tua domanda originale che hai menzionato che hai usato dateutil, mi dispiace per quello. Ma spero che questa risposta sia ancora utile ad altre persone che si imbattono in questa domanda quando hanno una data di analisi delle domande e vedono l'utilità di quel modulo.


Dato che così tante persone tendono ad usare Python-Dateutil, vorrei indicarci un limite di quella 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 devi sostituire l'ultima virgola con punto. Quindiparser.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 il

7
@flyingfoxlee, sì, lo capisco. Voglio solo dire alla gente la limitazione di Python-Dateutil. Fa cose magiche, ma a volte non riesce a farlo. Quindi "basta fissare un appuntamento e fa la cosa giusta". non è vero al 100%.
wanghq,

4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")ritorna: datetime.datetime(2016, 10, 27, 9, 6)non riesce a capire il fuso orario ...
HaPsantran,

2
Dipende dal proprio obiettivo. dateutil parserpuò essere semplice da usare, ma strptime()è più veloce. Inoltre, i suoi formati sono abbastanza facili da imparare.
rapimento il

9

La stringa di ora è simile al formato dell'ora in rfc 2822 (formato data nell'e-mail, intestazioni http) . Puoi analizzarlo usando solo 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)

Scopri le soluzioni che producono oggetti datetime sensibili al fuso orario per varie versioni di Python: analisi della data con fuso orario da un'e-mail .

In questo formato, ESTè semanticamente equivalente a-0500 . Sebbene, in generale, un'abbreviazione del fuso orario non sia sufficiente, per identificare un fuso orario in modo univoco .


0

Mi sono imbattuto in questo esatto problema.

Cosa ho finito per fare:

# 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.