Naiwni datetime
kontra świadomidatetime
O datetime
obiektach domyślnych mówi się, że są „naiwne”: przechowują informacje o czasie bez informacji o strefie czasowej. Pomyśl o naiwności datetime
jako o liczbie względnej (tj .:) +4
bez wyraźnego pochodzenia (w rzeczywistości twoje pochodzenie będzie powszechne w granicach twojego systemu).
W przeciwieństwie do tego myśl o świadomości datetime
jako o liczbach bezwzględnych (tj .:8
o wspólnym pochodzeniu dla całego świata.
Bez informacji o strefie czasowej nie można przekonwertować „naiwnej” daty i godziny na jakąkolwiek nienaiwną reprezentację czasu (gdzie są +4
cele, jeśli nie wiemy, od czego zacząć?). Dlatego nie możesz mieć datetime.datetime.toutctimestamp()
metody. (por .: http://bugs.python.org/issue1457227 )
Aby sprawdzić, czy jesteś datetime
dt
naiwny, sprawdź dt.tzinfo
, czy None
, to jest naiwny:
datetime.now()
datetime(1970, 1, 1)
Mam naiwne daty, co mogę zrobić?
Musisz przyjąć założenie w zależności od konkretnego kontekstu: Pytanie, które musisz sobie zadać, brzmi: czy byłeś datetime
w UTC? czy był to czas lokalny?
Jeśli korzystałeś z UTC (nie masz kłopotów):
import calendar
def dt2ts(dt):
"""Converts a datetime object to UTC timestamp
naive datetime will be considered UTC.
"""
return calendar.timegm(dt.utctimetuple())
Jeśli NIE korzystałeś z UTC , witaj w piekle.
Musisz sprawić, że będziesz datetime
nie naiwny, zanim użyjesz poprzedniej funkcji, przywracając im zaplanowaną strefę czasową.
Będziesz potrzebować nazwy strefy czasowej i informacji o tym, czy czas letni obowiązywał podczas tworzenia docelowej naiwnej daty i godziny (ostatnia informacja o czasie letnim jest wymagana w przypadku przypadków narożnych):
import pytz
mytz = pytz.timezone('Europe/Amsterdam')
dt = mytz.normalize(mytz.localize(dt, is_dst=True))
Konsekwencje niepodaniais_dst
:
Nieużywanie is_dst
spowoduje wygenerowanie nieprawidłowego czasu (i sygnatury czasowej UTC), jeśli docelowa data i godzina została utworzona, gdy wprowadzono wsteczny czas letni (na przykład zmiana czasu czasu letniego przez usunięcie jednej godziny).
Podanie nieprawidłowego is_dst
spowoduje oczywiście wygenerowanie nieprawidłowego czasu (i znacznika czasu UTC) tylko w przypadku nakładania się czasu letniego lub dziur. Podając również niepoprawny czas, występujący w „dziurach” (czas, który nigdy nie istniał z powodu przesunięcia czasu letniego do przodu), is_dst
poda interpretację tego, jak traktować ten fałszywy czas i jest to jedyny przypadek, w którym
.normalize(..)
faktycznie coś tutaj zrobimy, ponieważ następnie przetłumaczy go jako rzeczywisty prawidłowy czas (zmieniając datę i godzinę ORAZ obiekt DST, jeśli jest to wymagane). Zwróć na to uwagę.normalize()
nie jest to wymagane do posiadania poprawnego znacznika czasu UTC na końcu, ale jest prawdopodobnie zalecane, jeśli nie podoba ci się pomysł posiadania fałszywych czasów w zmiennych, szczególnie jeśli ponownie używasz tej zmiennej w innym miejscu.
I UNIKAJ UŻYWANIA NASTĘPUJĄCYCH : (por: konwersja strefy czasowej daty i godziny przy użyciu pytz )
dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))
Czemu? ponieważ .replace()
zastępuje na ślepo tzinfo
bez uwzględnienia czasu docelowego i wybiera zły obiekt DST. Natomiast .localize()
wykorzystuje czas docelowy i is_dst
wskazówkę, aby wybrać właściwy obiekt DST.
STARA niepoprawna odpowiedź (dzięki @JFSebastien za poruszenie tego):
Miejmy nadzieję, że dość łatwo jest odgadnąć strefę czasową (swoje lokalne pochodzenie), kiedy tworzysz swój naiwny datetime
obiekt, ponieważ jest to związane z konfiguracją systemu, której, miejmy nadzieję, NIE zmieniłbyś między naiwnym tworzeniem obiektu daty i godziny a momentem, w którym chcesz uzyskać Znacznik czasu UTC. Ta sztuczka może być użyta do nadania niedoskonałości pytanie.
Używając time.mktime
możemy stworzyć utc_mktime
:
def utc_mktime(utc_tuple):
"""Returns number of seconds elapsed since epoch
Note that no timezone are taken into consideration.
utc tuple must be: (year, month, day, hour, minute, second)
"""
if len(utc_tuple) == 6:
utc_tuple += (0, 0, 0)
return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))
def datetime_to_timestamp(dt):
"""Converts a datetime object to UTC timestamp"""
return int(utc_mktime(dt.timetuple()))
Musisz upewnić się, że Twój datetime
obiekt został utworzony w tej samej strefie czasowej, co ta, która utworzyła Twój datetime
.
To ostatnie rozwiązanie jest niepoprawne, ponieważ zakłada, że przesunięcie UTC od teraz jest takie samo, jak przesunięcie UTC z EPOCH. Co nie ma miejsca w przypadku wielu stref czasowych (w określonym momencie roku w przypadku przesunięć czasu letniego (DST)).
then.strftime('%s')
tym, że oczekuje się czasu lokalnego, ale sygnaturadatetime(2008, 1, 1)
czasowa wskazuje, że jest to UTC.