2012-08-30 11 views
6

Piszę program, który zajmuje się strefami czasowymi i ich przekraczaniem. Dwie rzeczy, z którymi mam do czynienia najczęściej, tworzą obiekt datetime z "teraz", a następnie lokalizują naiwny obiekt datetime.Czas letni w języku Python

Aby utworzyć obiekt datetime od teraz w Pacyfiku strefy czasowej, jestem obecnie robi to (Python 2.7.2+)

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

czy to jest poprawne w odniesieniu do DST? Jeśli nie, przypuszczam, że powinienem:

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

Moje pytanie brzmi: dlaczego? Czy ktoś może mi pokazać przypadek, w którym pierwsza jest błędna, a sekundy są prawidłowe?

chodzi o moje sekund pytanie, załóżmy, że miałem naiwną datę i godzinę od jakiegoś użytkownika wejście 9/1/2012 o godzinie 8:00 w Los Angeles, CA. Jest to dobry sposób, żeby dokonać datetime takiego:

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

Jeśli nie, w jaki sposób należy budować te datetimes?

Odpowiedz

9

Z pytz documentation:

Preferowanym sposobem radzenia sobie z czasami jest zawsze działa w UTC, konwersja tylko do localtime podczas generowania wyjścia do odczytania przez człowieka.

Idealnie powinieneś używać utcnow zamiast now.

Zakładając, że z jakiegoś powodu twoje ręce są związane i musisz pracować z lokalnymi czasami, nadal możesz napotkać problem z próbą zlokalizowania bieżącego czasu, jeśli robisz to w oknie przejściowym oszczędzającym czas. Ten sam datetime może wystąpić dwa razy, raz w ciągu dnia i ponownie w standardowym czasie, a metoda localize nie wie, jak rozstrzygnąć konflikt, chyba że powiesz to wyraźnie parametrem is_dst.

Tak, aby uzyskać aktualny czas UTC:

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

I przekonwertować go do czasu lokalnego (ale tylko wtedy, gdy musi):

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

EDIT: Jak wskazano w uwagach przez @J.F. Sebastian, Twój pierwszy przykład z użyciem datetime.now(tz) będzie działał we wszystkich przypadkach. Twój drugi przykład kończy się niepowodzeniem podczas przejścia na jesień, jak to nakreśliłem powyżej. Wciąż opowiadam się za używaniem czasu UTC zamiast czasu lokalnego dla wszystkiego poza wyświetlaniem.

+1

Preferowanym sposobem uzyskania aktualnego czasu w danej strefie czasowej jest: 'datetime.now (tz)'. – jfs

+0

@ J.F.Sebastian Podejrzeń, że nie działa niezawodnie z timezones 'pytz', z tych samych powodów konstruktor' datetime' nie działa z nimi. –

+1

to działa: [kiedy 'datetime.now (pytz_timezone)' nie działa?] (Http://stackoverflow.com/q/31886808/4279) – jfs

0

The pytz website mówi:

Niestety używając argumentu tzinfo standardowego datetime konstruktorzy „” nie działa”” z pytz dla wielu stref czasowych.

Nie powinieneś więc używać datetime.now(la). Nie znam szczegółów, ale niektóre strefy czasowe działają na bardziej egzotycznych zasadach, do których jesteśmy przyzwyczajeni, a kod daty i czasu Pythona nie może ich obsłużyć. Używając kodu pytz, powinny one być obsługiwane poprawnie, ponieważ jest to celem pytoła. Może również mieć problemy z razy, które występują dwa razy dzięki skoki czasowe w świetle dziennym.

Co do drugiego pytania, dokładnie to pokazuje dokumentacja, więc powinieneś być dobry.

+0

'datetime.now (tz)' jest przypadkiem, to działa (nie powinieneś używać '.localize()' tutaj). – jfs

+0

Powinieneś użyć datetime.datetime.utcnow(). Astimezone (tz) - To pobiera czas w UTC, a następnie przesuwa go z UTC zgodnie z zasadami obowiązującymi w strefie czasowej tz. (Przekazanie strefy czasowej pytz do konstruktora da ci pewne przesunięcie, które nie jest parzystą liczbą godzin.) – jobermark

+0

@jobermark: użyj 'now (tz)' zamiast 'utcnow(). Astimezone (tz)'. [Działa) (http://stackoverflow.com/questions/12203676/daylight-savings-time-in-python/12204612#comment62276833_12204612) – jfs

5

Pierwsze rozwiązanie jest poprawne w odniesieniu do czasu letniego, a drugie rozwiązanie jest złe.

Podam przykład. Tu, w Europie, kiedy uruchomiony ten kod:

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)) 

uzyskać następujące:

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) tworzy datetime z bieżącego czasu w Los Angeles, a także strefy czasowej dla LA.

la.localize(datetime.now()) dodaje strefy czasowej do naiwnego datetime, ale nie bez konwersji strefy czasowej; po prostu zakłada, że ​​czas był już w tej strefie czasowej.

datetime.now() tworzy naiwne datetime (bez strefy czasowej) z czasem lokalnym.

Dopóki jesteś w LA, nie będzie widać różnicy, ale jeśli kod kiedykolwiek skończy gdzieś indziej, to prawdopodobnie nie będzie robić, co chciał.

Poza tym, jeśli kiedykolwiek potrzebne do czynienia z poważnie stref czasowych, to lepiej mieć wszystkie swoje czasy w UTC, oszczędzając sobie sporo kłopotów z DST.

+0

'now2' może zwrócić nieprawidłowy wynik (godzina wyłączona) na końcu Przejścia DST ("fall back"), nawet jeśli jesteś w LA. 'datetime.now (la)' działa nawet podczas przejść UTC. – jfs

0

To działa:

# 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 

To nie działa:

# 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 

Wniosek:

datetime.timedelta()NIE konto do oszczędzania czasu.

Czy twój czas dodać/odjąć w strefie czasowej UTC ZAWSZE. Przesyłaj do czasu lokalnego tylko dla wyjścia/wyświetlacza.