2012-08-30 31 views
6

Sto scrivendo un programma che si occupa molto con fusi orari e che li attraversano. Le due cose che mi occupo di più sono la creazione di un oggetto datetime da "now" e quindi la localizzazione di un oggetto datetime ingenuo.ora legale in Python

Per creare un oggetto datetime da ora nel fuso orario del Pacifico, Attualmente sto facendo questo (python 2.7.2+)

from datetime import datetime 
import pytz 
la = pytz.timezone("America/Los_Angeles") 
now = datetime.now(la) 

È questo corretto per quanto riguarda l'ora legale? In caso contrario, suppongo di doverlo fare:

now2 = la.localize(datetime.now()) 

La mia domanda è: perché? Qualcuno può mostrarmi un caso in cui il primo è sbagliato e il secondo è giusto?

quanto riguarda la mia domanda secondi, supponiamo che ho avuto una data ingenua e ora da qualche input dell'utente per 9/1/2012 alle 8:00 am a Los Angeles, CA. È il modo giusto per fare il datetime in questo modo:

la.localize(datetime(2012, 9, 1, 8, 0)) 

Se no, come devo essere la costruzione di questi datetimes?

risposta

9

Dal pytz documentation:

Il modo preferito di trattare con tempi è di lavorare sempre in UTC, conversione in localtime solo quando la generazione di output per essere letto da esseri umani.

Quindi idealmente si dovrebbe usare utcnow anziché now.

Supponendo che per qualche motivo le tue mani siano legate e che tu debba lavorare con le ore locali, puoi ancora incontrare un problema nel cercare di localizzare l'ora corrente se lo stai facendo durante la finestra di transizione dell'ora legale. Lo stesso datetime potrebbe verificarsi due volte, una volta durante l'ora legale e di nuovo durante l'ora solare, e il metodo localize non sa come risolvere il conflitto a meno che non lo dici esplicitamente con il parametro is_dst.

Quindi, per ottenere il tempo corrente UTC:

utc = pytz.timezone('UTC') 
now = utc.localize(datetime.datetime.utcnow()) 

E per convertirlo in ora locale (ma solo quando è necessario):

la = pytz.timezone('America/Los_Angeles') 
local_time = now.astimezone(la) 

Edit: come ha sottolineato nei commenti per @J.F. Sebastian, il tuo primo esempio con datetime.now(tz) funzionerà in tutti i casi. Il tuo secondo esempio fallisce durante la transizione autunnale come descritto sopra. Sostengo ancora l'utilizzo dell'ora locale anziché dell'ora locale per tutto tranne la visualizzazione.

+1

il modo preferibile per ottenere l'ora corrente in un determinato fuso orario è: 'datetime.now (tz)'. – jfs

+0

@ J.F.Sebastian Sospetto che non funzioni in modo affidabile con i fusi orari 'pytz', per gli stessi motivi per cui il costruttore' datetime' non funziona con loro. –

+1

funziona: [quando fa 'datetime.now (pytz_timezone)' fallisce?] (Http://stackoverflow.com/q/31886808/4279) – jfs

0

Il pytz website dice:

Purtroppo utilizzando l'argomento tzinfo del datetime standard di costruttori ‘’ non funziona’’ con pytz per molti fusi orari.

Quindi non utilizzare datetime.now(la). Io non conosco le specifiche, ma alcuni fusi orari operare su regole più esotiche allora siamo abituati, e il codice di datetime python non li riesco a gestire. Usando il codice di pytz, dovrebbero essere gestiti correttamente poiché questo è lo scopo di pytz. Potrebbe anche avere problemi per i tempi che si verificano due volte grazie ai tempi di salto nell'ora legale.

Per quanto riguarda la seconda domanda, che è esattamente ciò che la documentazione mostra, così si dovrebbe essere buono.

+0

'datetime.now (tz)' è il caso quindi funziona (non si dovrebbe usare '.localize()' qui). – jfs

+0

Dovresti usare datetime.datetime.utcnow(). Astimezone (tz) - Questo ottiene il tempo in UTC e quindi lo compensa da UTC secondo le regole applicabili nel fuso orario tz. (Passare un fuso orario pytz nel costruttore ti porterà un po 'di offset che non è un numero pari di ore.) – jobermark

+0

@jobermark: usa 'now (tz)' invece di 'utcnow(). Astimezone (tz)'. [Funziona] (http://stackoverflow.com/questions/12203676/daylight-savings-time-in-python/12204612#comment62276833_12204612) – jfs

5

La prima soluzione è corretta per quanto riguarda DST, e la seconda soluzione è male.

Darò un esempio. Qui in Europa, durante l'esecuzione di questo codice:

from datetime import datetime 
import pytz # $ pip install pytz 

la = pytz.timezone("America/Los_Angeles") 
fmt = '%Y-%m-%d %H:%M:%S %Z%z' 
now = datetime.now(la) 
now2 = la.localize(datetime.now()) 
now3 = datetime.now() 
print(now.strftime(fmt)) 
print(now2.strftime(fmt)) 
print(now3.strftime(fmt)) 

ottengo il seguente:

2012-08-30 12:34:06 PDT-0700 
2012-08-30 21:34:06 PDT-0700 
2012-08-30 21:34:06 

datetime.now(la) crea un datetime con l'ora corrente a Los Angeles, oltre le informazioni fuso orario per LA.

la.localize(datetime.now()) aggiunge informazioni sul fuso orario al datetime ingenuo, ma non fa la conversione fuso orario; presume solo che il tempo fosse già in questo fuso orario.

datetime.now() crea un datetime naive (senza informazioni fuso orario) con l'ora locale.

Finché siete a Los Angeles, non vedrà la differenza, ma se il codice viene eseguito sempre da qualche altra parte, probabilmente non fare quello che si voleva.

A parte questo, se mai bisogno di affrontare seriamente i fusi orari, è meglio avere tutti i tempi in UTC, risparmiando un sacco di problemi con l'ora legale.

+0

'now2' può restituire un risultato errato (un'ora off) durante la fine- di- Transizioni DST ("fall back") anche se sei a LA. 'datetime.now (la)' funziona anche durante le transizioni UTC. – jfs

0

Questo funziona:

# naive datetime 
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC# UTC timezone 
pst = pytz.timezone('America/Los_Angeles') # LA timezone 

# Convert to UTC timezone aware datetime 
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>) 

# show as in LA time zone (not converting here) 
d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# we get Pacific Daylight Time: PDT 

# add 1 day to UTC date 
d = d + datetime.timedelta(days=1) 
>>> datetime.datetime(2016, 11, 6, 16, 43, 45, tzinfo=<UTC>) 

d.astimezone(pst) # now cast to LA time zone 
>>> datetime.datetime(2016, 11, 6, 8, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>) 
# Daylight saving is applied -> we get Pacific Standard Time PST 

Questo non funziona:

# naive datetime 
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC# UTC timezone 
pst = pytz.timezone('America/Los_Angeles') # LA timezone 

# convert to UTC timezone aware datetime 
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>) 

# convert to 'America/Los_Angeles' timezone: DON'T DO THIS 
d = d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# we are in Pacific Daylight Time PDT 

# add 1 day to LA local date: DON'T DO THAT 
d = d + datetime.timedelta(days=1) 
>>> datetime.datetime(2016, 11, 6, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# Daylight Saving is NOT respected, we are still in PDT time, not PST 

Conclusione:

datetime.timedelta()NON conto per l'ora legale.

Aumenta/sottragga tempo nel fuso orario UTC SEMPRE. Trasmissione all'ora locale solo per uscita/display.

Problemi correlati