Jak uzyskać wartość datetime.today () w Pythonie, która „rozpoznaje strefę czasową”?

310

Próbuję odjąć jedną wartość daty od wartości, datetime.today()aby obliczyć, jak dawno coś było. Ale narzeka:

TypeError: can't subtract offset-naive and offset-aware datetimes

Ta wartość datetime.today()nie wydaje się być „świadoma strefy czasowej”, podczas gdy moja inna wartość daty to. Jak uzyskać wartość tego, datetime.today()co świadczy o strefie czasowej?

W tej chwili daje mi czas w czasie lokalnym, którym jest PST, czyli UTC - 8 godzin. W najgorszym przypadku, czy istnieje sposób, aby ręcznie wprowadzić wartość strefy czasowej do datetimezwracanego obiektu datetime.today()i ustawić ją na UTC-8?

Oczywiście idealnym rozwiązaniem byłoby automatyczne rozpoznanie strefy czasowej.

złodziej
źródło
10
Wydaje się, że możemy używać datetime.now().astimezone()od Pythona 3.6
johnchen902

Odpowiedzi:

362

W standardowej bibliotece nie ma wieloplatformowego sposobu tworzenia świadomych stref czasowych bez tworzenia własnej klasy stref czasowych.

W systemie Windows jest win32timezone.utcnow(), ale jest to część pywin32. Wolałbym raczej użyć biblioteki pytz , która ma stale aktualizowaną bazę danych większości stref czasowych.

Praca z lokalnymi strefami czasowymi może być bardzo trudna (patrz łącza „Dalsze czytanie” poniżej), więc możesz raczej chcieć używać UTC w całej aplikacji, szczególnie do operacji arytmetycznych, takich jak obliczanie różnicy między dwoma punktami czasowymi.

Możesz uzyskać bieżącą datę / godzinę w następujący sposób:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Pamiętaj o tym datetime.today()i datetime.now()zwróć czas lokalny , a nie czas UTC, więc zastosowanie się .replace(tzinfo=pytz.utc)do nich byłoby nieprawidłowe.

Innym dobrym sposobem na to jest:

datetime.now(pytz.utc)

który jest nieco krótszy i robi to samo.


Dalsze czytanie / oglądanie, dlaczego preferować UTC w wielu przypadkach:

AndiDog
źródło
75
A może datetime.now(pytz.utc)zamiast datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro,
5
now(utc)nie wraca dzisiaj (chyba że jest północ do UTC), zwraca aktualny czas w UTC. Musisz także .replace(hour=0, minute=0, ...)uzyskać początek dnia (jak datetime.today())
jfs
1
W docs powiedzieć, że today()zwraca aktualny czas, a nie o północy. Jeśli istnieje przypadek użycia, w którym wymagana jest północ, tak, wymiana musi zostać wykonana odpowiednio. Ponieważ pierwotne pytanie dotyczyło różnicy czasu, nie sądzę, że północ jest wymagana.
AndiDog
1
@AndiDog: Mój komentarz sugeruje, że myślałem (niesłusznie), że datetime.today()jest combine(date.today(), time()). datetimema obie metody .now()i .today()metody, które (jak słusznie zauważyłeś) zwracają (prawie) to samo. Nie ma date.now()metody. datea datetimeobiekty nie są wymienne. Użycie obiektu datetime zamiast dateobiektu może powodować subtelne błędy; Nie widzę żadnego powodu, datetime.today()aby istnieć, jeśli jest to prawie duplikat datetime.now().
jfs
6
Dodając do tej odpowiedzi, jeśli używasz django, zawsze używaj timezone.now()zamiast, datetime.now()ponieważ użyje UTC automatycznie, jeśli USE_TZ = True. timezoneznajduje się w django.utils.timezone, dokumentacja: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan
107

Uzyskaj aktualny czas w określonej strefie czasowej:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))
philfreo
źródło
2
Zobacz to .
wim
1
NIE powinieneś używać zlokalizowanego czasu z wyjątkiem danych wyjściowych. Wiele rzeczy idzie nie tak podczas korzystania z datetime opartej na strefie czasowej: prosta timedelta nie bierze pod uwagę czasu letniego, chyba że jesteś na początku czasu UTC. Zawsze używaj strefy czasowej z uwzględnieniem UTC. w razie potrzeby konwertuj na lokalną strefę czasową na wyjściu.
MrE
4
Aby powtórzyć nieporozumienie z @MrE, które wcześniej wyraziłem w komentarzach do zaakceptowanej odpowiedzi: istnieją całkowicie uzasadnione powody do pracy ze zlokalizowanymi czasami danych, a „NIE należy używać zlokalizowanego czasu z wyjątkiem danych wyjściowych” to zbyt szeroka rada. Załóżmy, że dodajesz 1 dzień do godziny na kilka godzin przed granicą czasu letniego, przy której zegary cofają się o godzinę. Jaki chcesz wynik? Jeśli uważasz, że czas powinien być taki sam, użyj zlokalizowanych czasów danych. Jeśli uważasz, że powinna to być godzina wcześniej, użyj czasu UTC lub danych naiwnych dla strefy czasowej. Co ma sens, zależy od domeny.
Mark Amery
@ MarkAm bardzo, na ile mogę się zgodzić, że możesz DODAWAĆ lub SUBTRAKTOWAĆ na kilka dni lub godzin i nie przejmować się problemami strefy czasowej (jak twój przykład), ten komentarz dotyczy przekazywania klientowi skorygowanej strefy czasowej. Ponieważ Python jest używany głównie do procesów zaplecza, przekazuje czasy do klienta. Serwer powinien zawsze przekazywać datę / godzinę w UTC, a klient powinien przekonwertować ją na swoją lokalną datę / godzinę / strefę czasową, w przeciwnym razie zdarzają się złe rzeczy: wystarczy sprawdzić dane wyjściowe datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))i sprawdzić, czy tego się spodziewano
2018 r.
„Serwer powinien zawsze przekazywać datę / godzinę w UTC, a klient powinien przekonwertować ją na swoją lokalną datę / godzinę / strefę czasową” - nie, to nie jest uniwersalna prawda. Czasami korzystanie ze strefy czasowej klienta jest niewłaściwe i odpowiednia strefa czasowa musi zostać przesłana jako część danych. Jeśli jako londyńczyk przeglądam godziny spotkań klubu szachowego w San Francisco na ich stronie internetowej, powinienem je zobaczyć w czasie w San Francisco, a nie w Londynie.
Mark Amery
69

W Pythonie 3 standardowa biblioteka znacznie ułatwia określenie UTC jako strefy czasowej:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

Jeśli potrzebujesz rozwiązania, które korzysta tylko ze standardowej biblioteki i działa zarówno w Pythonie 2, jak i Pythonie 3, zobacz odpowiedź jfs .

Jeśli potrzebujesz lokalnej strefy czasowej, a nie UTC, zobacz odpowiedź Mihai Capotă

Flimm
źródło
19

Oto rozwiązanie stdlib, które działa zarówno na Pythonie 2, jak i 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

gdzie todayjest świadoma instancja daty i godziny reprezentująca początek dnia (północ) w UTC i utcjest obiektem tzinfo ( przykład z dokumentacji ):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Powiązane: porównanie wydajności kilku sposobów uzyskania północy (początek dnia) dla danego czasu UTC . Uwaga: bardziej skomplikowane jest uzyskanie północy dla strefy czasowej z nieokreślonym przesunięciem UTC .

jfs
źródło
16

Inna metoda konstruowania obiektu typu data-godzina uwzględniającego strefę czasową reprezentującego bieżący czas:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  
Dariusz Walczak
źródło
zauważ, że pytz.utci pytz.UTCoba są zdefiniowane (i są takie same)
drevicko
2
Ta odpowiedź jest lepsza niż zaakceptowana, ponieważ jest bardziej uniwersalna: replace()strefa czasowa jest na ogół podatna na błędy w większości innych zastosowań, natomiast localize()ing jest preferowanym sposobem przypisywania strefy czasowej do naiwnych znaczników czasu.
Antony Hatchkins
@AntonyHatchkins: .localize()metoda zawiedzie w przypadku niejednoznacznych czasów lokalnych (dane wejściowe inne niż utc). W takich przypadkach odpowiedź @ philfreo.now(pytz_timezone) nadal działa.
jfs
Jak podano w dokumentach Pythona, .now(pytz_timezone)robi dokładnie to samo co localize(utcnow)- najpierw generuje bieżący czas w UTC, a następnie przypisuje mu strefę czasową: „<...> W tym przypadku wynik jest równoważny z tz.fromutc(datetime.utcnow().replace(tzinfo=tz))”. Obie odpowiedzi są poprawne i działają zawsze.
Antony Hatchkins
1
Jedynym naiwnym (innym niż utc) czasem, który można bezpiecznie uświadomić strefie czasowej, jest teraz : system bazowy powinien znać wartość UTC, a pytzpoprzez OLSON db powinien wiedzieć, jak przekonwertować ją na dowolną strefę czasową na świecie. Uświadomienie sobie jakiejkolwiek innej naiwnej (innej niż utc) strefy czasowej jest trudne z powodu niejednoznaczności podczas zmian czasu letniego. To nie jest problem .localize(podanie is_dstwartości powoduje, że działa ona na dowolną datę). To nieodłączny problem praktyki oszczędzania światła dziennego.
Antony Hatchkins,
16

Jednowarstwowy korzystający tylko ze standardowej biblioteki działa począwszy od Python 3.3. Możesz uzyskać lokalny datetimeobiekt rozpoznający strefę czasową, używając astimezone(zgodnie z sugestią johnchen902 ):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))
Mihai Capotă
źródło
2
Dokumentacja stanowi dużą pomoc, którą można znaleźć tutaj: docs.python.org/3.8/library/… . Ta niewiarygodnie podstawowa funkcjonalność jest ukryta głęboko w niejasnym akapicie w dokumentach, dzięki czemu ta odpowiedź na przepełnienie stosu jest faktycznie jedynym miejscem w całym Internecie z tymi informacjami. W dokumentacji widać również, że od Python 3.6 datetime.now()można wywoływać bez żadnych argumentów i zwracać poprawny wynik lokalny ( datetimezakłada się, że naiwni znajdują się w lokalnej strefie czasowej).
atimholt
8

Jeśli używasz Django , możesz ustawić daty nieobsługujące TZ (tylko UTC ).

Skomentuj następujący wiersz w pliku settings.py:

USE_TZ = True
Laffuste
źródło
8
gdzie widziałeś django wspomniane w tym pytaniu?
vonPetrushev
1
Jakaś miła dusza usunęła tutaj moje poprzednie przeprosiny za komentarze, więc znowu: wstyd mi, zła odpowiedź, ponieważ pytanie nie jest specyficzne dla Django. Zostawiłem to, ponieważ i tak może pomóc niektórym użytkownikom, ale usunę go, gdy wynik zbliży się do 0. Jeśli ta odpowiedź jest nieodpowiednia, możesz głosować negatywnie.
laffuste
6

pytz to biblioteka Python, która umożliwia dokładne obliczenia stref czasowych między platformami przy użyciu Python 2.3 lub nowszego.

Z stdlib nie jest to możliwe.

Zobacz podobne pytanie na temat SO .

użytkownik225312
źródło
6

Oto jeden ze sposobów na wygenerowanie go za pomocą stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

data będzie przechowywać lokalną datę i przesunięcie względem UTC , a nie datę w strefie czasowej UTC, więc możesz użyć tego rozwiązania, jeśli chcesz określić, w której strefie czasowej jest generowana data . W tym przykładzie i w mojej lokalnej strefie czasowej:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

Kluczem jest dodanie %zdyrektywy do reprezentacji FORMAT, aby wskazać przesunięcie UTC wygenerowanej struktury czasu. Inne formaty reprezentacji można znaleźć w dokumentach modułu datetime

Jeśli potrzebujesz datę w strefie czasowej UTC, można zastąpić time.localtime () z time.gmtime ()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Edytować

Działa to tylko na python3 . Dyrektywa z nie jest dostępna dla kodu _strptime.py w Pythonie 2

jcazor
źródło
ValueError: „z” to zła dyrektywa w formacie „% Y-% m-% dT% H:% M:% S% z”
2017
Jesteś na Pythonie 2, prawda? Niestety, wydaje się, że dyrektywa z nie jest dostępna w Pythonie 2. Kod
_strptime.py
6

Użyj dateutil zgodnie z opisem w Python datetime.datetime.now (), który jest świadomy strefy czasowej :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())
G. Führ
źródło
1
Zobacz tę odpowiedź JF Sebastiana w sytuacji, gdy daje to niepoprawny wynik.
Antony Hatchkins
2
Myślę, że błąd w drugim poście dotyczy tylko określonych przypadków użycia. Ta tzlocal()funkcja jest nadal jednym z najprostszych rozwiązań i zdecydowanie powinna zostać tutaj wymieniona.
user8162,
2

Uzyskiwanie daty uwzględniającej strefę czasową utc strefę czasową strefie czasowej wystarczy, aby odejmowanie dat działało.

Ale jeśli chcesz mieć datę uwzględniającą strefę czasową w bieżącej strefie czasowej, tzlocalmożesz wybrać:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutilma podobną funkcję ( dateutil.tz.tzlocal). Ale pomimo udostępnienia nazwy ma ona zupełnie inną bazę kodu, co, jak zauważył JF Sebastian, może dawać błędne wyniki.

Antony Hatchkins
źródło
Python jest zwykle używany na serwerze. Lokalna strefa czasowa na serwerze jest zwykle bezcelowa i zawsze powinna być ustawiona na UTC. Ustawienie datz tzinfo w ten sposób kończy się niepowodzeniem w niektórych przypadkach. lepiej użyć UTC, a następnie zlokalizować żądaną strefę czasową tylko na wyjściu. każde obliczenie timedelta na przykład nie uwzględnia czasu letniego, więc należy to zrobić w UTC, a następnie zlokalizować.
MrE
@MrE Nieprawidłowy, offtopic, przykłady?
Antony Hatchkins,
spróbuj użyć obiektu datetime zlokalizowanego w strefie czasowej, która obserwuje czas letni, dodaj liczbę dni, aby zmienić stan czasu letniego, a zobaczysz, że działanie na obiektach datetime w zlokalizowanej strefie czasowej kończy się niepowodzeniem i nie będzie szanowało czasu letniego. Stąd mój komentarz, że ZAWSZE powinieneś wykonywać dowolną operację datetime w czasie UTC.
MrE
chodzi o to: nie rób tego, wykonuj operacje w UTC, a następnie użyj datetime.astimezone (strefa czasowa), aby przekonwertować na strefę czasu lokalnego na wyjściu.
MrE
2

Oto rozwiązanie wykorzystujące czytelną strefę czasową, które działa z Today ():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

Możesz wyświetlić wszystkie strefy czasowe w następujący sposób:

import pytz

pytz.all_timezones
pytz.common_timezones # or
JohnAndrews
źródło
1

Szczególnie w strefach czasowych poza UTC:

Jedyną strefą czasową, która ma własną metodę, jest timezone.utcstrefa czasowa z dowolnym przesunięciem UTC, jeśli jest to konieczne, używając timedelta& timezonei wymuszając użycie .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Używanie timezone(timedelta(hours=n))jako strefy czasowej jest tutaj prawdziwą srebrną kulą i ma wiele innych przydatnych aplikacji.

kod tmck
źródło
0

Jeśli otrzymujesz aktualną godzinę i datę w Pythonie, a następnie zaimportuj datę i godzinę, spakuj pakiet Pytz w Pythonie po tym, jak otrzymasz aktualną datę i godzinę, jak ..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))
Jigar Vagadiya
źródło
0

Inną, moim zdaniem lepszą, alternatywą jest użycie Pendulumzamiastpytz . Rozważ następujący prosty kod:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

Aby zainstalować Wahadło i przejrzeć ich dokumentację, przejdź tutaj . Ma mnóstwo opcji (takich jak prosta obsługa formatu ISO8601, RFC3339 i wiele innych formatów), lepszą wydajność i zwykle daje prostszy kod.

ng10
źródło
nie jestem pewien, dlaczego głosowanie tutaj, ten kod działa w wielu programach, które uruchamiają dla mnie 7/24 :). nie, że mam coś przeciwko innej opinii, ale proszę powiedzieć, dlaczego to nie działa, aby pozwolić mi to sprawdzić. Z góry
dziękuję
0

Użyj strefy czasowej, jak pokazano poniżej, aby ustawić datę i godzinę uwzględniającą strefę czasową. Wartość domyślna to UTC:

from django.utils import timezone
today = timezone.now()
Anupama V Iyengar
źródło
0

Tyler z „howchoo” napisał naprawdę świetny artykuł, który pomógł mi lepiej zrozumieć obiekty Datetime, link poniżej

Praca z Datetime

Zasadniczo właśnie dodałem następujący tekst na końcu obu moich obiektów datetime

.replace(tzinfo=pytz.utc)

Przykład:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)
Jose
źródło
0

wypróbuj pnp_datetime , cały czas używany i zwracany jest w strefie czasowej i nie spowoduje żadnych problemów naiwnych i świadomych przesunięcia.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)
Cloudup
źródło
0

Należy podkreślić, że od Python 3.6 potrzebujesz tylko standardowej biblioteki lib, aby uzyskać obiekt datetime rozpoznający strefę czasową, który reprezentuje czas lokalny (ustawienie twojego systemu operacyjnego). Korzystanie z astimezonu ()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(patrz komentarz @ johnchen902).

MrFuppes
źródło