Dlaczego datetime.datetime.utcnow () nie zawiera informacji o strefie czasowej?

285
datetime.datetime.utcnow()

Dlaczego datetimenie ma żadnych informacji o strefie czasowej, skoro jest to wyraźnie UTC datetime?

Spodziewałbym się, że to zawiera tzinfo.

Witalij Babiy
źródło
Jak przekonwertować normalne pole daty formatu ISO, które jest typu string na format utc?
Navi

Odpowiedzi:

192

Oznacza to, że strefa czasowa jest naiwna, więc nie można jej używać datetime.astimezone

możesz nadać mu taką strefę czasową

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

teraz możesz zmienić strefy czasowe

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

Aby uzyskać aktualny czas w danej strefie czasowej, możesz przekazać tzinfo datetime.now()bezpośrednio:

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

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

Działa dla dowolnej strefy czasowej, w tym tych, które obserwują czas letni (DST), tj. Działa dla stref czasowych, które mogą mieć różne przesunięcia utc w różnych momentach (nie ustalone przesunięcie utc). Nie używaj tz.localize(datetime.now())- może się nie powieść podczas przejścia na koniec DST, gdy czas lokalny jest niejednoznaczny.

John La Rooy
źródło
216
Ale nie ma żadnego dobrego powodu, aby był on naiwny dla strefy czasowej - jest określony jako UTC. Dlaczego musisz wyszukać bibliotekę innej firmy, aby działała poprawnie?
Mark Ransom,
4
Zgadzam się; dla mnie „naiwne” czasy są całkowicie bezużyteczne. W tej chwili trwa dyskusja na liście python na temat dodawania pytza do stdlib; problemem nie jest licencjonowanie, ale fakt, że dane strefy czasowej są tak często aktualizowane (czym nie może być sam Python). Pytz również nie implementuje interfejsu tzinfo w oczekiwany sposób, więc możesz dostać błędy, jeśli spróbujesz użyć niektórych stref czasowych miasta astimezone. Datetime nie tylko nie ma natywnych stref czasowych, ale jedyna szeroko dostępna implementacja tzinfo jest niezgodna z domniemanym standardem.
bobince
5
@bobince Dlaczego pytz i standardowe biblioteki datetime nie działają dla Ciebie? Rdzeń Pythona i ewolucja Pytza jako niezależne projekty zmniejszają złożoność logistyczną dla głównego zespołu. Tak, zmniejszenie złożoności dla głównego zespołu Python zwiększa złożoność dla wszystkich użytkowników Pythona, którzy muszą radzić sobie ze strefami czasowymi, ale ufam, że podjęli tę decyzję nie bez powodu. Zasada „Biblioteka standardowa nie ma instancji tzinfo ...” jest świetna, ponieważ jest prosta, po co tutaj robić wyjątek?
Derek Litz,
15
Jak o właśnieu=datetime.now(pytz.utc)
Craig McQueen
4
@bain: nie używaj tz.localize(datetime.now()); użyj datetime.now(tz)zamiast tego.
jfs
142

Zauważ, że od wersji Python 3.2 datetimemoduł zawiera datetime.timezone. Dokumentacja datetime.utcnow()mówi:

Świadomy aktualny czas UTC można uzyskać, dzwoniąc .datetime.now(timezone.utc)

Możesz więc zrobić:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Craig McQueen
źródło
2
Który jest preferowany? datetime.now(timezone.utc)czy datetime.utcnow(timezone.utc)?
Jesse Webb,
8
datetime.utcnow()nie przyjmuje żadnych argumentów. Tak musiało być datetime.now(timezone.utc).
Craig McQueen,
1
datetime.now()zwróci czas maszyny, ale datetime.utcnow()zwróci rzeczywisty czas UTC.
Babu,
13
@Babu: datetime.utcnow()nie ustawia tzinfowskazania, że ​​jest to UTC. Ale datetime.now(datetime.timezone.utc)zwraca czas UTC z tzinfo ustawionym.
Craig McQueen,
@CraigMcQueen Więc jeśli przekażemy tzobiekt w konstruktorze teraz, zwróci on czas w tej strefie czasowej? Dobrze! Dzięki za wskazanie.
Babu
71

Standardowe biblioteki Pythona nie zawierają żadnych klas tzinfo (ale patrz pep 431 ). Mogę tylko zgadywać przyczyny. Osobiście uważam, że błędem było nie uwzględnienie klasy tzinfo dla UTC, ponieważ jest ona wystarczająco kontrowersyjna, aby mieć standardową implementację.

Edycja: Chociaż nie ma implementacji w bibliotece, istnieje jedna podana jako przykład w tzinfodokumentacji .

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

Aby go użyć, aby uzyskać bieżący czas jako świadomy obiekt datetime:

from datetime import datetime 

now = datetime.now(utc)

Jest datetime.timezone.utcw Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Mark Ransom
źródło
8
Zastanów się, dlaczego ta klasa nie była zapewniona w pierwszej kolejności (i, co ważniejsze, używana do datetimeobiektów stworzonych przez utcnow()) ...
André Caron
17
Obiekt strefy czasowej timezone.utczostał wreszcie dodany do Pythona 3.2. Dla kompatybilności wstecznej utcnow()nadal zwraca obiekt czasowy bez strefy czasowej, ale możesz uzyskać to, co chcesz, dzwoniąc now(timezone.utc).
mhsmith
4
@rgove, to rodzaj naprawiania krzywd, które miały być uczciwą grą dla Pythona 3. Nie powinni się martwić o kompatybilność wsteczną. Jest inny przykład, który przeczytałem w ciągu ostatnich kilku dni - structmoduł przeprowadzałby automatyczne konwersje z Unicode na bajtowanie, a ostateczną decyzją było złamanie zgodności z wcześniejszymi wersjami Pythona 3, aby zapobiec podjęciu złej decyzji.
Mark Ransom
2
Jestem zdumiony, że tzinfodokumentacja Pythona zawiera przykłady kodu do jego implementacji, ale nie zawierają one tej funkcji w samym czasie! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@LS tak, pytzto świetny zasób. Zanim zredagowałem swoją odpowiedź, aby wstawić przykładowy kod, ktoś już ją zasugerował i nie chciałem kraść grzmotu.
Mark Ransom,
20

pytzModuł jest jedną z opcji, a nie ma innegopython-dateutil , który chociaż jest również pakiet osoba trzecia, może już być dostępne w zależności od innych uzależnień i systemu operacyjnego.

Chciałem tylko dołączyć tę metodologię w celach informacyjnych - jeśli już zainstalowałeś python-dateutildo innych celów, możesz użyć jej tzinfozamiast duplikować zpytz

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

Zgadzam się, że wezwania do utcnowpowinny zawierać informacje o strefie czasowej UTC. Podejrzewam, że nie zostało to uwzględnione, ponieważ natywna biblioteka datetime domyślnie ustawia czas naiwnych dat danych w celu zachowania kompatybilności.

bbengfort
źródło
1
NameError: nazwa „dt” nie jest zdefiniowana
xApple
Korzystałem z wywołania datetime.datetime.utcfromtimestamp () i potrzebowałem dodać tzinfo. Drugie rozwiązanie zadziałało dla mnie: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee
1
Uwaga: w przeciwieństwie do datetime.now(pytz_tz)tego zawsze działa; datetime.now(dateutil.tz.tzlocal())może zawieść podczas przejścia DST . PEP 495 - Ujednoznacznienie czasu lokalnego może poprawić dateutilsytuację w przyszłości.
jfs
@IanLee: można użyć utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(uwaga: dateutilz nietrwałego przesunięcie UTC (takie jak dateutil.tz.tzlocal()) może się nie udać tutaj użyć do pytzopartych rozwiązanie zamiast ).
jfs
Ponieważ mój program został już importowania dateutildo dateutil.parserlubiłem to rozwiązanie najlepsze. To było tak proste, jak: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Altówka!!
LS
11

Julien Danjou napisał dobry artykuł wyjaśniający, dlaczego nigdy nie należy zajmować się strefami czasowymi . Fragment:

Rzeczywiście, interfejs API Python datetime zawsze zwraca nieświadome obiekty datetime, co jest bardzo niefortunne. Rzeczywiście, jak tylko dostaniesz jeden z tego obiektu, nie ma sposobu, aby dowiedzieć się, jaka jest strefa czasowa, dlatego te obiekty same w sobie są dość „bezużyteczne”.

Niestety, nawet jeśli możesz użyć utcnow(), nadal nie zobaczysz informacji o strefie czasowej, jak odkryłeś.

Rekomendacje:

  • Zawsze używaj świadomych datetimeobiektów, tj. Z informacjami o strefie czasowej. Dzięki temu możesz je porównać bezpośrednio (świadomy i nieświadomy)datetime obiekty nie są porównywalne) i zwróci je poprawnie użytkownikom. Wykorzystaj pytz, aby mieć obiekty strefy czasowej.

  • Posługiwać się ISO 8601 jako formatu ciągu wejściowego i wyjściowego. Służy datetime.datetime.isoformat()do zwracania znaczników czasu w postaci łańcucha sformatowanego przy użyciu tego formatu, który zawiera informacje o strefie czasowej.

  • Jeśli chcesz przeanalizować ciągi znaków zawierające sformatowane znaczniki czasu ISO 8601, możesz polegać na iso8601, który zwraca znaczniki czasu z poprawnymi informacjami o strefie czasowej. To sprawia, że ​​znaczniki czasu są bezpośrednio porównywalne.

Joe D'Andrea
źródło
1
Jest to nieco mylące zalecenie. Zasadą jest, aby nigdy nie zajmować się strefami czasowymi. Zawsze przechowuj i przesyłaj tz nieużywane obiekty utc (obiekty epoki). Strefę czasową należy obliczać tylko w momencie reprezentacji w interfejsie użytkownika
dniu
1
Wygląda na to, że całkiem dobrze pasuje do myśli Juliena. Które z jego konkretnych zaleceń (jak wspomniano powyżej) są mylące?
Joe D'Andrea,
10

Aby dodać timezoneinformacje w Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
Nordborn
źródło
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (domyślnie, 19 stycznia 2017 r., 14:48:08)
Marcin Owsiany
-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
źródło
-13

Daty UTC nie potrzebują żadnych informacji o strefie czasowej, ponieważ są UTC, co z definicji oznacza, że ​​nie mają przesunięcia.

Ignacio Vazquez-Abrams
źródło
10
O ile wiem z docs.python.org/library/datetime.html , data i godzina bez tzinfo to taka, w której strefa czasowa jest nieokreślona. Tutaj strefa czasowa nie została określona, więc logicznie powinien być obecny. Istnieje duża różnica między datą / godziną bez powiązanej strefy czasowej a tą, która jest zdecydowanie w UTC. (Idealnie powinny to być różne typy IMO, ale to już inna sprawa ...)
Jon Skeet
@JonSkeet Myślę, że brakuje ci punktu Ignacio, że UTC nie jest strefą czasową. Niesamowite, że ta odpowiedź ma wynik -9, gdy wpisuję to ...
CS
3
@CS: Cóż, Ignacio nigdy nie stwierdził, że ... i chociaż ściśle mówiąc UTC nie jest strefą czasową, zwykle jest traktowany jako taki, aby znacznie ułatwić życie (w tym w Pythonie, np. Z pytz.utc). Zauważ, że istnieje duża różnica między wartością, której przesunięcie względem UTC jest nieznane, a tą, w której wiadomo, że wynosi 0. To ostatnie utcnow() powinno zwrócić IMO. To by pasowało do „Świadomego obiektu używa się do przedstawienia określonego momentu w czasie, który nie jest otwarty na interpretację” zgodnie z dokumentacją.
Jon Skeet