Konwertuj datetime na Unix timestamp i konwertuj je z powrotem w Pythonie

186

Mam dt = datetime(2013,9,1,11)i chciałbym uzyskać uniksowy znacznik czasu tego obiektu datetime.

Kiedy mam, (dt - datetime(1970,1,1)).total_seconds()mam sygnaturę czasową 1378033200.

Podczas konwersji z powrotem za pomocą datetime.fromtimestampotrzymałem datetime.datetime(2013, 9, 1, 6, 0).

Godzina się nie zgadza. Za czym tęskniłem?

Bridgewater
źródło
1
Na marginesie, jeśli używasz Pythona 3.3+, naprawdę chcesz użyć tej timestampmetody, zamiast próbować to zrobić samodzielnie. Po pierwsze, pozwala to całkowicie uniknąć możliwości odejmowania naiwnych czasów z różnych stref czasowych.
abarnert
1
Skąd się dtbierze? Czy to czas lokalny czy UTC?
jfs
2
@abarnert Chciałbym również złożyć prośbę o wyczyszczenie wszystkich stron kodowych i symboli Unicode, czyli zakazanie wszystkich języków innych niż ASCII i innych niż domyślne strony kodowe. Malowanie symboli jest nadal dozwolone.
Daniel F
6
@DanielF: Żądanie odrzucone. Nie jestem zainteresowany tworzeniem tutaj jednego języka światowego, a nawet gdybyśmy to zrobili, nie chciałbym uniemożliwiać życia lingwistom historii i badaczom Tolkiena. Unicode już rozwiązuje problem, z wyjątkiem tego, że niektóre organizacje i produkty (konsorcjum TRON, japońskie oprogramowanie, które używa Shift-JIS zamiast UTF-8, Microsoft nadal dostarcza system operacyjny, który domyślnie cp1252 dla plików tekstowych użytkownika i różne SDK udające, że UTF -16 to zestaw znaków o stałej szerokości i / lub to samo co Unicode) musi zostać ukarany, aby ustawić je w jednej linii.
abarnert

Odpowiedzi:

105

To, co przegapiłeś, to strefy czasowe.

Prawdopodobnie masz pięć godzin poza czasem UTC, więc 2013-09-01T11: 00: 00 lokalna i 2013-09-01T06: 00: 00Z to ten sam czas.

Musisz przeczytać górną część datetimedokumentacji, która wyjaśnia o strefach czasowych oraz obiektach „naiwnych” i „świadomych”.

Jeśli pierwotną naiwną datą i godziną był UTC, sposobem na jej odzyskanie jest użycie utcfromtimestampzamiast fromtimestamp.

Z drugiej strony, jeśli pierwotna naiwna data i godzina była lokalna, nie powinieneś odejmować od niej znacznika czasu UTC; użyj datetime.fromtimestamp(0)zamiast tego.

Lub, jeśli masz świadomy obiekt daty i godziny, musisz albo użyć lokalnej (świadomej) epoki po obu stronach, albo jawnie przekonwertować na iz UTC.

Jeśli masz lub możesz zaktualizować Python 3.3 lub nowszy, możesz uniknąć wszystkich tych problemów, używając po prostu tej timestampmetody, zamiast próbować samodzielnie wymyślić, jak to zrobić. A nawet jeśli tego nie zrobisz, możesz rozważyć wypożyczenie jego kodu źródłowego .

(A jeśli możesz poczekać na Pythona 3.4, wygląda na to, że PEP 341 prawdopodobnie trafi do ostatecznej wersji, co oznacza, że ​​wszystkie rzeczy, o których JF Sebastian i ja rozmawialiśmy w komentarzach, powinny być możliwe do wykonania za pomocą tylko standardowej biblioteki, a działa w ten sam sposób w systemach Unix i Windows.)

abarnert
źródło
1
jeśli dtznajduje się w lokalnej strefie czasowej, wówczas formuła w pytaniu jest niepoprawna datetime.fromtimestamp(0)(epoka w bieżącej strefie czasowej) powinna zostać użyta zamiast datetime(1970, 1,1)(epoka unix w UTC).
jfs
@JFSebastian: Nie chciałem szczegółowo omawiać wszystkich trzech możliwości, ale myślę, że masz rację, powinienem.
abarnert
btw, fromtimestamp(0)może się nie powieść, jeśli system nie przechowuje historycznych informacji o strefie czasowej, np. w systemie Windows. pytzmoże być użyty w tym przypadku.
jfs
@JFSebastian: Ale pytzto nie pomaga, chyba że już wiesz, w której strefie czasowej się znajdujesz; aby zrobić to programowo, potrzebujesz innej biblioteki, która pobiera aktualną strefę czasową Windows i konwertuje ją na pytzstrefę czasową i / lub wyszukuje ją po nazwie.
abarnert
jest tzlocalmoduł, który działa również w systemie Windows. Oto jak można przekonwertować czas utc na czas lokalny .
jfs
141

rozwiązaniem jest

import time
import datetime
d = datetime.date(2015,1,5)

unixtime = time.mktime(d.timetuple())
DmitrySemenov
źródło
12
zakłada, że djest to naiwny obiekt daty / daty i godziny, który reprezentuje czas lokalny (może się nie powieść w przypadku niejednoznacznych czasów lub przeszłych / przyszłych dat, jeśli system operacyjny nie dostarcza historycznej wartości tz db (przesunięcie UTC mogło być inne w przeszłości w lokalnym strefa czasowa)). Więcej opcji .
jfs
6
Wygląda na to, że nikomu nie przeszkadza rozwiązanie zmiennoprzecinkowe. Czy to jest oczywiste dla każdego?
Ivan Balashov
4
To skraca mikrosekundy. Może być interesujące.
Alfe
Jedynym problemem jest to, że nie bierzesz pod uwagę strefy czasowej.
Blairg23
nie powinno. Jeśli chcesz dostosować TZ, musisz to zrobić najpierw.
Marcin Orłowski
69

Jeśli chcesz przekonwertować datetime w Pythonie na sekundy od epoki, powinieneś to zrobić jawnie:

>>> import datetime
>>> datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'
>>> (datetime.datetime(2012,04,01,0,0) - datetime.datetime(1970,1,1)).total_seconds()
1333238400.0

W Pythonie 3.3+ możesz timestamp()zamiast tego użyć :

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Francisco Costa
źródło
5
Nie uwzględnia stref czasowych.
Blairg 23
3
Nie chcesz go używać %s, ponieważ zostanie on zlokalizowany zgodnie z zegarem systemu, w którym aktualnie jesteś. Powinieneś używać tylko .timestamp()do uzyskania prawidłowego czasu Epoki / UNIX.
Blairg23
3
%snie działa w systemach Windows ( ValueError: Invalid format string)
chrki
1
@ amitnair92 po prostu wstaw 8lub9
Francisco Costa
54

Zamiast tego wyrażenia do tworzenia sygnatury czasowej POSIX z dt,

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

Użyj tego:

int(dt.strftime("%s"))

Uzyskuję właściwą odpowiedź w twoim przykładzie, używając drugiej metody.

EDYCJA: Trochę dalszych informacji ... Po kilku komentarzach (patrz poniżej) byłem zaciekawiony brakiem wsparcia lub dokumentacji dla %sin strftime. Oto co znalazłem:

W źródle Pythona dla datetimei time, ciąg STRFTIME_FORMAT_CODESmówi nam:

"Other codes may be available on your platform.
 See documentation for the C library strftime function."

Więc teraz, jeśli my man strftime(w systemach BSD, takich jak Mac OS X), znajdziesz wsparcie dla %s:

"%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3))."

W każdym razie, dlatego %sdziała na systemach, które robi. Ale są lepsze rozwiązania problemu OP (uwzględniające strefy czasowe). Zobacz zaakceptowaną odpowiedź @ abarnert tutaj.

Darren Stone
źródło
4
To jest nieudokumentowane zachowanie (uważam). Na przykład w systemie Windows powoduje to „Nieprawidłowy ciąg formatu”.
Crescent Fresh
@CrescentFresh, interesujące. Możesz mieć rację. Chociaż nie widzę strftime("%s")w dokumentacji, właśnie potwierdziłem, że działa na komputerach Mac i Linux. Dzięki.
Darren Stone
1
najwyraźniej ignoruje pole tzinfo.
Daniel F
1
@DarrenStone: nie musisz czytać źródła. Zarówno, jak time.strftime()i datetime.strftimedelegacja dokumentacji do platformy strftime(3)działają w przypadku nieobsługiwanych dyrektyw. %smoże się nie powieść nawet w systemie Mac OS X, np. datetime.strftime ('% s') powinien uwzględniać tzinfo .
jfs
1
Nigdy nie powinieneś używać, %sponieważ będziesz używać tylko zlokalizowanego czasu systemowego systemu, na którym jesteś. Chcesz użyć, .timestamp()jeśli próbujesz uzyskać prawdziwy znacznik czasu UNIX.
Blairg23
6

Do pracy ze strefami czasowymi UTC:

time_stamp = calendar.timegm(dt.timetuple())

datetime.utcfromtimestamp(time_stamp)
Emin Temiz
źródło
Po godzinie poszukiwań była to jedyna odpowiedź, która zadziałała: ')
Qurashi
4

Przegapiłeś informacje o strefie czasowej (już odpowiedział, zgodziłeś się)

arrowpakiet pozwala uniknąć tej tortury z datami; Jest już napisany, przetestowany, opublikowany w pypi, cross-python (2.6 - 3.xx).

Wszystko, czego potrzebujesz: pip install arrow(lub dodaj do zależności)

Rozwiązanie dla Twojego przypadku

dt = datetime(2013,9,1,11)
arrow.get(dt).timestamp
# >>> 1378033200

bc = arrow.get(1378033200).datetime
print(bc)
# >>> datetime.datetime(2013, 9, 1, 11, 0, tzinfo=tzutc())
print(bc.isoformat())
# >>> '2013-09-01T11:00:00+00:00'
maxkoryukov
źródło
3

Jeśli obiekt datetime reprezentuje czas UTC, nie używaj time.mktime, ponieważ zakłada się, że krotka znajduje się w lokalnej strefie czasowej. Zamiast tego użyj calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Matt Kramer
źródło
1
def dt2ts(dt, utc=False):
    if utc:
        return calendar.timegm(dt.timetuple())
    if dt.tzinfo is None:
        return int(time.mktime(dt.timetuple()))
    utc_dt = dt.astimezone(tz.tzutc()).timetuple()
    return calendar.timegm(utc_dt)

Jeśli chcesz mieć znacznik czasu UTC: time.mktimetylko dla lokalnego dt. Użyj calendar.timegmjest bezpieczne, ale dt musi mieć strefę utc, więc zmień strefę na utc. Jeśli dt w UTC, po prostu użyj calendar.timegm.

wyx
źródło
1
def datetime_to_epoch(d1):

    # create 1,1,1970 in same timezone as d1
    d2 = datetime(1970, 1, 1, tzinfo=d1.tzinfo)
    time_delta = d1 - d2
    ts = int(time_delta.total_seconds())
    return ts


def epoch_to_datetime_string(ts, tz_name="UTC"):
    x_timezone = timezone(tz_name)
    d1 = datetime.fromtimestamp(ts, x_timezone)
    x = d1.strftime("%d %B %Y %H:%M:%S")
    return x
rjha94
źródło
0

Ta klasa zaspokoi Twoje potrzeby, możesz przekazać zmienną do ConvertUnixToDatetime i wywołać funkcję, na której ma działać.

from datetime import datetime
import time

class ConvertUnixToDatetime:
    def __init__(self, date):
        self.date = date

    # Convert unix to date object
    def convert_unix(self):
        unix = self.date

        # Check if unix is a string or int & proceeds with correct conversion
        if type(unix).__name__ == 'str':
            unix = int(unix[0:10])
        else:
            unix = int(str(unix)[0:10])

        date = datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S')

        return date

    # Convert date to unix object
    def convert_date(self):
        date = self.date

        # Check if datetime object or raise ValueError
        if type(date).__name__ == 'datetime':
            unixtime = int(time.mktime(date.timetuple()))
        else:
            raise ValueError('You are trying to pass a None Datetime object')
        return type(unixtime).__name__, unixtime


if __name__ == '__main__':

    # Test Date
    date_test = ConvertUnixToDatetime(datetime.today())
    date_test = date_test.convert_date()
    print(date_test)

    # Test Unix
    unix_test = ConvertUnixToDatetime(date_test[1])
    print(unix_test.convert_unix())
kryptopotluck
źródło