Konwertowanie datetime.date na znacznik czasu UTC w Pythonie

295

Mam do czynienia z datami w Pythonie i muszę je przekonwertować na znaczniki czasu UTC, aby mogły być używane w JavaScript. Poniższy kod nie działa:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

Konwersja obiektu daty najpierw na datę i godzinę również nie pomaga. Próbowałem tego przykładu pod tym linkiem , ale:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

a teraz albo:

mktime(utc.localize(input_date).utctimetuple())

lub

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

działa.

Tak ogólne pytanie: jak mogę przekonwertować datę na sekundy od epoki według UTC?

Andreas Jung
źródło
29
Nie jestem pewien, czy zgodziłbym się na oznaczenie go jako duplikatu. Chociaż rozwiązania są podobne, pytania nie są. Jeden (ten) próbuje utworzyć znacznik czasu z datetime.date, drugi próbuje przekonwertować ciąg znaków z jednej strefy czasowej na drugą. Jako ktoś, kto szuka rozwiązania tego problemu, nie mogę stwierdzić, że ten drugi udzieli odpowiedzi, której szukam.
DRH
5
datetime (date.year, date.month, date.day) .timestamp ()
Julian

Odpowiedzi:

462

Jeśli d = date(2011, 1, 1)jest w UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

Jeśli dznajduje się w lokalnej strefie czasowej:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1i timestamp2może się różnić, jeśli północ w lokalnej strefie czasowej nie jest tą samą instancją czasu, co północ w UTC.

mktime()może zwrócić nieprawidłowy wynik, jeśli dodpowiada niejednoznacznemu czasowi lokalnemu (np. podczas przejścia DST) lub jeśli djest przeszłością (przyszłą) datą, w której przesunięcie utc mogło być inne, a C mktime()nie ma dostępu do bazy danych tz na danej platformie . Możesz użyć pytzmodułu (np. Via tzlocal.get_localzone()), aby uzyskać dostęp do bazy danych tz na wszystkich platformach . Ponadto utcfromtimestamp()może się nie powieść i mktime()może zwrócić znacznik czasu inny niż POSIX, jeśli "right"używana jest strefa czasowa .


Aby przekonwertować datetime.dateobiekt reprezentujący datę w UTC bez calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

Jak mogę przekonwertować datę na sekundy od epoki zgodnie z UTC?

Aby przekonwertować datetime.datetime(nie datetime.date) obiekt, który już reprezentuje czas w UTC, na odpowiedni znacznik czasu POSIX (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Uwaga: Konieczne jest timezone.utcjawne podanie w innym przypadku .timestamp()założenia, że ​​naiwny obiekt typu data-godzina znajduje się w lokalnej strefie czasowej.

Python 3 (<3.3)

Z dokumentów dotyczących datetime.utcfromtimestamp():

Nie ma metody uzyskiwania znacznika czasu z instancji daty i godziny, ale znacznik czasu POSIX odpowiadający instancji daty i godziny dt można łatwo obliczyć w następujący sposób. Naiwny dt:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

I dla świadomego dt:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Ciekawa lektura: Czas epoki a pora dnia na podstawie różnicy między Którą to jest godziną? i ile sekund upłynęło?

Zobacz także: datetime potrzebuje metody „epoki”

Python 2

Aby dostosować powyższy kod dla Python 2:

timestamp = (dt - datetime(1970, 1, 1)).total_seconds()

gdzie timedelta.total_seconds()jest równoważne do (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6obliczonego z włączonym prawdziwym dzieleniem.

Przykład

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Uważaj na problemy zmiennoprzecinkowe .

Wynik

2012-01-08 15:34:10.022403
1326036850.02

Jak przekonwertować świadomy datetimeobiekt na znacznik czasu POSIX

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

W Pythonie 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

W Pythonie 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()
jfs
źródło
2
W przypadku Python 2 dlaczego nie timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01
7
@ m01: datetime(1970, 1, 1)jest bardziej wyraźny. fromtimestamp()jest tutaj niepoprawny ( dtjest w UTC, więc utcfromtimestamp()należy go użyć).
jfs,
1
@fedorqui spójrz na totimestamp()funkcję w odpowiedzi.
jfs
14
o ile uwielbiam Pythona, jego biblioteka czasu i daty jest poważnie zepsuta.
Realfun,
3
@ Realfun: strefy czasowe, przejścia DST, baza danych tz, sekundy przestępne istnieją niezależnie od Pythona.
jfs
81

Tylko dla systemów unix :

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Uwaga 1: dizzyf zauważył, że dotyczy to zlokalizowanych stref czasowych . Nie używaj w produkcji.

Uwaga 2: Jakub Narębski zauważył, że ignoruje to informacje o strefie czasowej nawet w przypadku datetime z uwzględnieniem przesunięcia (testowane dla Python 2.7).

Ulf Aslak
źródło
4
Dobra odpowiedź, jeśli wiesz, w jakim systemie będzie działał Twój kod, ale ważne jest, aby pamiętać, że ciąg formatu „% s” nie istnieje we wszystkich systemach operacyjnych, więc ten kod nie jest przenośny. Jeśli chcesz sprawdzić, czy jest obsługiwany, możesz sprawdzić man strftimew systemach uniksopodobnych.
Alice Heaton,
5
Należy wspomnieć, że powoduje to spadek części ułamkowej sekundy (mikrosekundy lub cokolwiek innego).
Alfe
11
OSTRZEŻENIE: Dotyczy to zlokalizowanych stref czasowych! Otrzymasz różne zachowanie strftime dla identycznych obiektów datetime w zależności od czasu komputera
dizzyf
3
Ponadto ignoruje to informacje o strefie czasowej i zakłada, że data-godzina znajduje się w lokalnej strefie czasowej, nawet dla daty-godziny uwzględniającej przesunięcie , z ustawionym tzinfo. Cóż, przynajmniej w Pythonie 2.7.
Jakub Narębski
1
javascript timestamp = python timestamp * 1000
Trinh Hoang Nhu
38
  • Założenie 1: Próbujesz przekonwertować datę na znacznik czasu, jednak ponieważ data obejmuje 24 godziny, nie ma jednego znacznika czasu reprezentującego tę datę. Zakładam, że chcesz reprezentować znacznik czasu tej daty o północy (00: 00: 00.000).

  • Założenie 2: Data, którą prezentujesz, nie jest powiązana z określoną strefą czasową, jednak chcesz ustalić przesunięcie względem określonej strefy czasowej (UTC). Bez znajomości strefy czasowej, w której jest data, nie można obliczyć znacznika czasu dla określonej strefy czasowej. Zakładam, że chcesz traktować datę tak, jakby była w lokalnej strefie czasowej systemu.

Po pierwsze, możesz przekonwertować instancję daty na krotkę reprezentującą różne składniki czasu za pomocą elementu timetuple()członkowskiego:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

Następnie możesz przekonwertować to na znacznik czasu, używając time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Możesz zweryfikować tę metodę, testując ją z samym czasem epoki (1970-01-01), w którym to przypadku funkcja powinna zwrócić przesunięcie strefy czasowej dla lokalnej strefy czasowej w tym dniu:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 to 8 godzin, co byłoby poprawne dla strefy czasowej Pacyfiku (gdzie jestem).

DRH
źródło
Nie jestem pewien, dlaczego zostało to odrzucone. Rozwiązuje problem PO (działający kod PO oznaczony jako „nie działa”). Nie odpowiada na moje pytanie (konwertuje UTC datetime.datetime na znacznik czasu), ale nadal… upvoting.
Thanatos,
9
Bądź ostrożny. Założenie, że „chcesz traktować datę tak, jakby była w lokalnej strefie czasowej systemu, jest źródłem wszystkich problemów ze strefą czasową w pythonie. Chyba że masz 100% pewności co do lokalizacji komputera, na którym zostanie uruchomiony skrypt, a zwłaszcza jeśli obsługujemy klientów, którzy mogą pochodzić z dowolnego miejsca na świecie, ZAWSZE zakładaj, że daty są w UTC, i dokonaj konwersji tak wcześnie, jak to możliwe. Utrzyma włosy na twojej głowie, zaufaj mi
Romain G
8

postępuj zgodnie z dokumentem python2.7, musisz użyć calendar.timegm () zamiast time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)
Wang
źródło
3
czym różni się od mojej odpowiedzi, jeśli djest w UTC?
jfs
8

Zdefiniowałem własne dwie funkcje

  • utc_time2datetime (utc_time, tz = Brak)
  • datetime2utc_time (datetime)

tutaj:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time
agittarius
źródło
1
Przeszedłem prosto obok twojego przykładu… Zbyt skomplikowany i skomplikowany dla tak prostej rzeczy… W ogóle nie pytoniczny.
Zły 84
@Mayhem: Posprzątałem trochę i dodałem komentarze. Jeśli masz lepsze i bardziej ogólne rozwiązanie do konwersji z godziny na datę i godzinę i możesz ustawić strefę czasową - daj nam znać. Daj mi również znać, jak powinien wyglądać bardziej pythonowy przykład mojego kodu.
agittarius
3

pytanie jest trochę zdezorientowane. znaczniki czasu nie są UTC - są uniksowe. data może być UTC? zakładając, że tak jest, a jeśli używasz języka Python 3.2+, prosta data sprawia, że ​​jest to banalne:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

jeśli faktycznie masz rok, miesiąc i dzień, nie musisz tworzyć date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

a jeśli data jest w innej strefie czasowej (ma to znaczenie, ponieważ zakładamy północ bez powiązanego czasu):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[idea simple-date polega na zebraniu wszystkich dat i godzin Pythona w jednej spójnej klasie, aby można było przeprowadzić dowolną konwersję. na przykład pójdzie w drugą stronę:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]

Andrzej Cooke
źródło
SimpleDate(date(2011,1,1), tz='America/New_York')podnosi NoTimezone, a pytz.timezone('America/New_York')nie podnosi żadnych wyjątków ( simple-date==0.4.8, pytz==2014.7).
jfs
pip install simple-datePojawia się błąd , wygląda na to , że jest to tylko python3 / pip3.
NoBugs,
3

Za pomocą pakietu strzałek :

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)
Mathieu Longtin
źródło
Uwaga: arrowużywa dateutil i dateutilzna od wielu lat błędy związane z obsługą stref czasowych. Nie używaj go, jeśli potrzebujesz poprawnych wyników dla stref czasowych z nieokreślonym przesunięciem UTC (dobrze jest użyć go dla strefy czasowej UTC, ale stdlib obsługuje również przypadek UTC).
jfs
@JFSebastian Ten błąd występuje tylko podczas przejścia, w niejednoznacznym czasie.
Paul
Wygląda na to, że błąd został naprawiony 28 marca
Mathieu Longtin
Wygląda na to, że jest to najlepsze rozwiązanie typu cross-python. Wystarczy użyć strzałki . Dzięki;)
maxkoryukov
@MathieuLongtin dateutil może mieć inne problemy
jfs
1

Pełny ciąg czasu zawiera:

  • data
  • czas
  • utcoffset [+HHMM or -HHMM]

Na przykład:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

Uwaga:

UNIX timestampjest liczbą zmiennoprzecinkową wyrażoną w sekundach od epoki, w UTC .


Edytować:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600
kev
źródło
6
Jak to odpowiada na moje pytanie?
Andreas Jung
1
@ user908088 Konwertuj 1970-01-01 06:00:00 +0500na, 3600o co prosiłeś.
kev
1
@ user908088 Nie ma timezonew python2, można wykonać obliczenia ( 06:00:00- +0500= 01:00:00) ręcznie.
kev
5
dlaczego ta odpowiedź na górze ma nawet -1 głosów, coś jest nie tak z SO
Umair,
1

Biorąc pod uwagę, że masz datetimeobiekt o nazwie d, użyj następującego polecenia, aby uzyskać znacznik czasu w UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

I w przeciwnym kierunku użyj następujących poleceń:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Max
źródło
0

Jestem pod wrażeniem głębokiej dyskusji.

moje 2 centy:

od daty importu do godziny importu

znacznik czasu w utc to:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

lub,

timestamp = time.time()

jeśli teraz wynika z datetime.now (), w tym samym DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
nieujawnione
źródło