pobierz znacznik czasu UTC w pythonie z datetime

83

Czy istnieje sposób, aby uzyskać znacznik czasu UTC, podając datę? Czego bym się spodziewał:

datetime(2008, 1, 1, 0, 0, 0, 0)

powinno skutkować

 1199145600

Utworzenie naiwnego obiektu daty i godziny oznacza, że ​​nie ma informacji o strefie czasowej. Jeśli spojrzę na dokumentację dotyczącą datetime.utcfromtimestamp, utworzenie znacznika czasu UTC oznacza pominięcie informacji o strefie czasowej. Więc domyślam się, że utworzenie naiwnego obiektu daty i godziny (tak jak ja) spowodowałoby otrzymanie znacznika czasu UTC. Jednak:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

prowadzi do

2007-12-31 23:00:00

Czy w obiekcie datetime nadal są jakieś ukryte informacje o strefie czasowej? Co ja robię źle?

poklepać
źródło
Problem polega na then.strftime('%s')tym, że oczekuje się czasu lokalnego, ale sygnatura datetime(2008, 1, 1)czasowa wskazuje, że jest to UTC.
jfs

Odpowiedzi:

91

Naiwni datetimekontra świadomidatetime

O datetimeobiektach domyślnych mówi się, że są „naiwne”: przechowują informacje o czasie bez informacji o strefie czasowej. Pomyśl o naiwności datetimejako o liczbie względnej (tj .:) +4bez 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 datetimejako 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ą +4cele, 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 dtnaiwny, sprawdź dt.tzinfo, czy None, to jest naiwny:

datetime.now()        ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1)  ## returns naïve datetime pointing on user given time

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ś datetimew 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 datetimenie 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     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Set your timezone
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Set is_dst accordingly
    

    Konsekwencje niepodaniais_dst :

    Nieużywanie is_dstspowoduje 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_dstspowoduje 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_dstpoda 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'))  ## BAD !!
    

    Czemu? ponieważ .replace()zastępuje na ślepo tzinfobez uwzględnienia czasu docelowego i wybiera zły obiekt DST. Natomiast .localize()wykorzystuje czas docelowy i is_dstwskazó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 datetimeobiekt, 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.mktimemoż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 datetimeobiekt 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)).

vaab
źródło
4
Naiwny obiekt datetime powinien zawsze reprezentować czas w UTC. Inne strefy czasowe powinny być używane tylko dla wejść / wyjść (wyświetlacza). W datetime.timestamp()Pythonie 3.3 jest metoda.
jfs
2
time.mktime()powinny być używane tylko dla czasu lokalnego. calendar.timegm()może być użyty do konwersji krotki czasu utc na znacznik czasu posix. Lub jeszcze lepiej używaj tylko metod datetime. Zobacz moją odpowiedź
jfs
4
-1. Twój kod zakłada, że ​​utc_offset (teraz) i utc_offset (epoch) są takie same w lokalnej strefie czasowej. Inaczej jest w 116 strefach czasowych (z 430 wspólnych stref czasowych).
jfs
1
nadal -1: nie używaj .replace()ze strefą czasową, która ma nie ustalone przesunięcie utc, takie jak 'Europe/Amsterdam'. Zobacz Konwersja strefy czasowej daty i godziny przy użyciu pytz .
jfs
1
1- Czy rozumiesz, dlaczego nie powinieneś używać .replace(tzinfo=get_localzone())? 2- timegm()wraca intjuż. Nie trzeba go owijać int. Ponadto .timetuple()spada o ułamki sekundy.
jfs
30

Inna możliwość to:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

Działa to, ponieważ zarówno „d”, jak i „epoch” są naiwnymi datami, dzięki czemu operator „-” jest ważny i zwraca interwał. total_seconds()zamienia interwał na sekundy. Zwróć uwagę, że total_seconds()zwraca wartość float, a nawetd.microsecond == 0

Chris Cogdon
źródło
12
No nie bardzo, pomysł jest ten sam, ale ten jest łatwiejszy do zrozumienia :)
Natim
3
Można by pomyśleć, że jest jedna metoda w bibliotece czasu lub coś ... szisz
dohewise
Aby uzyskać znacznik czasu, wykonaj datetime.datetime.utcnow (). Timestamp ()
Eugene
21

Zwróć także uwagę na funkcję calendar.timegm () opisaną w tym wpisie na blogu:

import calendar
calendar.timegm(utc_timetuple)

Wynik powinien zgadzać się z rozwiązaniem vaab.

Panie Mr.
źródło
13

Jeśli wejściowy obiekt datetime to UTC:

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

Uwaga: zwraca wartość typu float, tj. Mikrosekundy są reprezentowane jako ułamki sekundy.

Jeśli obiekt daty wejściowej jest w UTC:

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

Więcej informacji można znaleźć w sekcji Konwertowanie datetime.date na znacznik czasu UTC w języku Python .

jfs
źródło
8

Wydaje mi się, że główna odpowiedź nadal nie jest tak jasna i warto poświęcić trochę czasu na zrozumienie czasu i stref czasowych .

Najważniejszą rzeczą do zrozumienia, gdy mamy do czynienia z czasem, jest to, że czas jest względny !

  • 2017-08-30 13:23:00: (naiwna data i godzina ) reprezentuje czas lokalny w dowolnym miejscu na świecie, ale pamiętaj, że 2017-08-30 13:23:00w Londynie NIE JEST TEN SAM CZAS, co 2017-08-30 13:23:00w San Francisco.

Ponieważ ten sam ciąg czasu może być interpretowany jako różne punkty w czasie w zależności od tego, gdzie się znajdujesz na świecie, istnieje potrzeba bezwzględnego pojęcia czasu.

Znacznik czasu UTC to liczba w sekundach (lub milisekundach) z Epoki (zdefiniowana jako 1 January 1970 00:00:00oGMT czasowej +00: 00 przesunięcie).

Epoka jest zakotwiczona w strefie czasowej GMT i dlatego jest absolutnym punktem w czasie. Znacznik czasu UTC będący przesunięciem w stosunku do czasu bezwzględnego definiuje zatem absolutny punkt w czasie .

Dzięki temu możliwe jest uporządkowanie wydarzeń w czasie.

Bez informacji o strefie czasowej czas jest względny i nie można go przekształcić w absolutne pojęcie czasu bez podania pewnych wskazówek, w której strefie czasowej powinna być zakotwiczona naiwna data i godzina.

Jakie są rodzaje czasu używanego w systemie komputerowym?

  • naiwny datetime : zwykle do wyświetlania w czasie lokalnym (tj. w przeglądarce), gdzie system operacyjny może dostarczać programowi informacje o strefie czasowej.

  • Znaczniki czasu UTC : znacznik czasu UTC jest absolutnym punktem w czasie, jak wspomniano powyżej, ale jest zakotwiczony w danej strefie czasowej, więc znacznik czasu UTC można przekonwertować na datę i godzinę w dowolnej strefie czasowej, jednak nie zawiera informacji o strefie czasowej. Co to znaczy? Oznacza to, że 1504119325 odpowiada 2017-08-30T18:55:24Z, lub 2017-08-30T17:55:24-0100czy też 2017-08-30T10:55:24-0800. Nie informuje, skąd pochodzi zarejestrowana data i godzina. Zwykle jest używany po stronie serwera do rejestrowania zdarzeń (dzienników itp.) Lub służy do konwersji daty i godziny uwzględniającej strefę czasową na bezwzględny punkt w czasie i obliczania różnic czasowych .

  • Ciąg daty i godziny ISO-8601 : ISO-8601 to ustandaryzowany format do rejestrowania daty i godziny ze strefą czasową. (W rzeczywistości jest to kilka formatów, przeczytaj tutaj: https://en.wikipedia.org/wiki/ISO_8601 ) Jest używany do przekazywania informacji o datetime w strefie czasowej w sposób serializowalny między systemami.

Kiedy używać którego? a raczej kiedy należy dbać o strefy czasowe?

  • Jeśli chcesz w jakikolwiek sposób dbać o porę dnia , potrzebujesz informacji o strefie czasowej. Kalendarz lub alarm wymaga podania pory dnia, aby ustawić spotkanie o odpowiedniej porze dla dowolnego użytkownika na świecie. Jeśli te dane są zapisywane na serwerze, serwer musi wiedzieć, jakiej strefie czasowej odpowiada ta data i godzina.

  • Aby obliczyć różnice czasowe między zdarzeniami pochodzącymi z różnych miejsc na świecie, wystarczy znacznik czasu UTC, ale tracisz możliwość analizowania, o której porze dnia miały miejsce zdarzenia (np. W przypadku analityki internetowej możesz chcieć wiedzieć, kiedy użytkownicy przychodzą do Twojego witryna w ich czasie lokalnym : czy widzisz więcej użytkowników rano czy wieczorem? Nie możesz tego zrozumieć bez informacji o porze dnia.

Przesunięcie strefy czasowej w ciągu daty :

Kolejną ważną kwestią jest to, że przesunięcie strefy czasowej w ciągu daty nie jest stałe . Oznacza to, że ponieważ 2017-08-30T10:55:24-0800mówi offset-0800 że skoro lub 8 godzin wstecz, nie oznacza, że ​​zawsze będzie!

Latem może to być czas letni i tak będzie -0700

Oznacza to, że przesunięcie strefy czasowej (+0100) to nie to samo co nazwa strefy czasowej (Europa / Francja) ani nawet oznaczenie strefy czasowej (CET)

America/Los_Angeles strefa czasowa to miejsce na świecie , ale zmienia się wPSTnotację przesunięcia strefy czasowej (Pacific Standard Time) zimą iPDT(Pacific Daylight Time) latem.

Tak więc, oprócz uzyskania przesunięcia strefy czasowej z datestring, należy również uzyskać dokładną nazwę strefy czasowej.

Większość pakietów będzie w stanie samodzielnie konwertować liczbowe przesunięcia z czasu letniego na standardowy, ale niekoniecznie jest to trywialne, gdy wystarczy przesunięcie. Na przykład WATstrefa czasowa w Afryce Zachodniej to UTC + 0100, podobnie jak CETstrefa czasowa we Francji, ale Francja przestrzega czasu letniego, podczas gdy Afryka Zachodnia nie (ponieważ znajdują się blisko równika)

Krótko mówiąc, jest to skomplikowane. BARDZO skomplikowane i dlatego nie powinieneś tego robić samodzielnie, ale zaufaj pakietowi, który zrobi to za Ciebie i UTRZYMAJ JĄ NA BIEŻĄCO!

Pan
źródło
zobacz mój wpis na blogu o dacie i godzinie w Pythonie, aby zrozumieć pułapki różnych pakietów medium.com/@eleroy/ ...
MrE
3

Proste rozwiązanie bez użycia zewnętrznych modułów:

from datetime import datetime, timezone

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=timezone.utc).timestamp())
Mike Siomkin
źródło
1

Myślę, że właściwym sposobem sformułowania pytania jest to Is there a way to get the timestamp by specifying the date in UTC?, że sygnatura czasowa to tylko liczba, która jest bezwzględna, a nie względna. Elementem względnym (lub świadomym strefy czasowej) jest data.

Uważam, że pandy są bardzo wygodne w przypadku znaczników czasu, więc:

import pandas as pd
dt1 = datetime(2008, 1, 1, 0, 0, 0, 0)
ts1 = pd.Timestamp(dt1, tz='utc').timestamp()
# make sure you get back dt1
datetime.utcfromtimestamp(ts1)  
Itamar Katz
źródło
Używanie pand jest właściwym podejściem IMHO, dla obecnego czasu istnieje również t = Timestamp.utcnow (), aby bezpośrednio uzyskać właściwy czas :)
ntg
0

Przyjęta odpowiedź wydaje mi się nie działać. Moje rozwiązanie:

import time
utc_0 = int(time.mktime(datetime(1970, 01, 01).timetuple()))
def datetime2ts(dt):
    """Converts a datetime object to UTC timestamp"""
    return int(time.mktime(dt.utctimetuple())) - utc_0
dreamingcloud
źródło
kończy się niepowodzeniem, jeśli bieżące ( dt) przesunięcie czasu UTC w lokalnej strefie czasowej i w 1970 r. się różni. mktime()oczekuje czasu lokalnego.
jfs
0

Najprostszy sposób:

>>> from datetime import datetime
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> dt.strftime("%s")
'1199163600'

Edycja: @Daniel jest poprawne, to zamieniłoby to na strefę czasową maszyny. Oto poprawiona odpowiedź:

>>> from datetime import datetime, timezone
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> int((dt-epoch).total_seconds())
'1199145600'

W rzeczywistości nie jest nawet konieczne określanie timezone.utc, ponieważ różnica czasu jest taka sama, o ile obie datetimemają tę samą strefę czasową (lub nie mają strefy czasowej).

>>> from datetime import datetime
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> int((dt-epoch).total_seconds())
1199145600
Mike Furlender
źródło
ta metoda nie zwraca utc, ale raczej dostosowuje datę i godzinę do bieżącej strefy czasowej. to znaczy. jeśli teraz jest godzina 12 w tz +3, zwróci wartość 9 rano w epoce.
Daniel Dubovski
Ach, masz rację. Moja strefa czasowa to UTC - i dlatego zadziałała.
Mike Furlender
Jeśli masz zamiar użyć (Python 3.2 i nowsze) timezone.utcprzedmiotu, a potem po prostu użyć tego z .timestamp(): datetime(2008, 1, 1, tzinfo=timezone.utc).timestamp(). Nie ma potrzeby tworzenia obiektu epoki i odejmowania ..
Martijn Pieters