Converti datetime in timestamp Unix e convertilo nuovamente in Python


186

Ho dt = datetime(2013,9,1,11), e vorrei ottenere un timestamp Unix di questo oggetto datetime.

Quando lo faccio (dt - datetime(1970,1,1)).total_seconds()ho ottenuto il timestamp 1378033200.

Quando lo riconvertisco usando datetime.fromtimestampho ottenuto datetime.datetime(2013, 9, 1, 6, 0).

L'ora non coincide. Cosa mi sono perso qui?



1
Come nota a margine, se stai usando Python 3.3+, vuoi davvero usare il timestampmetodo invece di provare a farlo da solo. Per prima cosa, ciò evita completamente la possibilità di sottrarre tempi ingenui da diversi fusi orari.
Abarnert,

1
Da dove dtviene È l'ora o l'ora locale in UTC?
jfs

2
@abarnert Vorrei anche inviare una richiesta per eliminare tutte le codepage e i simboli unicode, ovvero per mettere fuori legge tutti i linguaggi non ascii e non predefiniti. Sono ancora consentiti dipinti di simboli.
Daniel F,

6
@DanielF: richiesta negata. Non sono interessato a creare una lingua del mondo unico qui, e anche se lo facessimo non vorrei rendere la vita impossibile ai linguisti storici e agli studiosi di Tolkien. Unicode risolve già il problema, tranne per il fatto che alcune organizzazioni e prodotti (il consorzio TRON, il software giapponese che utilizza Shift-JIS su UTF-8, Microsoft fornisce ancora un sistema operativo predefinito per cp1252 per i file di testo degli utenti e vari SDK che fingono che UTF -16 è un set di caratteri a larghezza fissa e / o la stessa cosa di Unicode) deve essere punito per renderli in linea.
Abarnert,

Risposte:


105

Quello che ti sei perso qui sono i fusi orari.

Presumibilmente hai cinque ore di pausa UTC, quindi 2013-09-01T11: 00: 00 locali e 2013-09-01T06: 00: 00Z sono alla stessa ora.

Devi leggere la parte superiore dei datetimedocumenti, che spiegano i fusi orari e gli oggetti "ingenui" e "consapevoli".

Se il tuo datetime ingenuo originale era UTC, il modo per recuperarlo è utilizzare utcfromtimestampinvece di fromtimestamp.

D'altra parte, se il tuo datetime ingenuo originale era locale, non avresti dovuto sottrarre un timestamp UTC da esso in primo luogo; usa datetime.fromtimestamp(0)invece.

In alternativa, se si disponeva di un oggetto datetime consapevole, è necessario utilizzare un'epoca locale (consapevole) su entrambi i lati o convertire esplicitamente da e verso UTC.

Se hai, o riesci ad aggiornare a, Python 3.3 o versioni successive, puoi evitare tutti questi problemi semplicemente usando il timestampmetodo invece di cercare di capire come farlo da solo. E anche se non lo fai, ti consigliamo di prendere in prestito il suo codice sorgente .

(E se puoi aspettare Python 3.4, sembra che PEP 341 sia probabilmente arrivato alla versione finale, il che significa che tutte le cose di cui JF Sebastian e io stavamo parlando nei commenti dovrebbero essere fattibili solo con lo stdlib, e lavorando allo stesso modo su Unix e Windows.)


1
se si dttrova nel fuso orario locale, la formula nella domanda non è corretta datetime.fromtimestamp(0)(epoca nel fuso orario corrente) deve essere utilizzata al posto di datetime(1970, 1,1)(epoca unix in UTC).
jfs

@JFSebastian: Non volevo coprire tutte e tre le possibilità in dettaglio, ma credo che tu abbia ragione, dovrei.
Abarnert,

tra l'altro, fromtimestamp(0)potrebbe non funzionare se il sistema non memorizza informazioni storiche sul fuso orario, ad esempio su Windows. pytzpotrebbe essere usato in questo caso.
jfs

@JFSebastian: Ma pytznon aiuta se non sai già in quale fuso orario ti trovi; per farlo a livello di codice, è necessaria una libreria diversa che ottenga l'attuale fuso orario di Windows e lo converta in un pytzfuso orario e / o lo cerchi per nome.
Abarnert,

c'è un tzlocalmodulo che funziona anche su Windows. Ecco come è possibile convertire l'ora utc in ora locale .
jfs

141

la soluzione è

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

unixtime = time.mktime(d.timetuple())

12
presuppone che si dtratti di un oggetto data / datetime ingenuo che rappresenta l'ora locale (potrebbe non riuscire per orari ambigui o per date passate / future se il sistema operativo non fornisce un valore storico tb db (l'offset UTC potrebbe essere stato diverso in passato nel locale fuso orario)). Altre opzioni .
jfs

6
Sembra che nessuno si preoccupi della soluzione in virgola mobile. È così ovvio per tutti?
Ivan Balashov,

4
Questo lascia cadere i microsecondi. Potrebbe essere interessante.
Alfe,

L'unico problema è che non si tiene conto del fuso orario.
Blairg23,

non dovrebbe. Se devi regolare TZ, devi prima farlo.
Marcin Orlowski,

69

Se vuoi convertire un datetime di Python in secondi dall'epoca, dovresti farlo esplicitamente:

>>> 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

In Python 3.3+ puoi timestamp()invece usare :

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0

5
Non tiene conto dei fusi orari.
Blairg23

3
Non si desidera utilizzare %s, poiché sarà localizzato sull'orologio del sistema su cui ci si trova attualmente. Si dovrebbe usare sempre e solo .timestamp()per ottenere l'ora Epoch / UNIX corretta.
Blairg23,

3
%snon funziona su sistemi Windows ( ValueError: Invalid format string)
chrki

1
@ amitnair92 appena messo 8o9
Francisco Costa

54

Anziché questa espressione per creare un timestamp POSIX da dt,

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

Usa questo:

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

Ottengo la risposta giusta nel tuo esempio usando il secondo metodo.

EDIT: Alcuni followup ... Dopo alcuni commenti (vedi sotto), ero curioso circa la mancanza di sostegno o di documentazione per %sa strftime. Ecco cosa ho trovato:

Nel sorgente Python per datetimee time, la stringa STRFTIME_FORMAT_CODESci dice:

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

Quindi ora se noi man strftime(su sistemi BSD come Mac OS X), troverai il supporto per %s:

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

Comunque, ecco perché %sfunziona sui sistemi che fa. Ma ci sono soluzioni migliori al problema di OP (che tengono conto dei fusi orari). Vedi la risposta accettata di @ abarnert qui.


4
Questo è un comportamento senza documenti (credo). Ad esempio, su Windows risulta "Stringa di formato non valida".
Crescent Fresh,

@CrescentFresh, interessante. Potresti avere ragione. Anche se non vedo strftime("%s")nella documentazione, ho appena confermato che funziona su Mac e Linux. Grazie.
Darren Stone,

1
apparentemente ignora il campo tzinfo.
Daniel F,

1
@DarrenStone: non è necessario leggere la fonte. Sia time.strftime()e datetime.strftimela documentazione delegato alle piattaforme strftime(3)funzionano per le direttive non supportati. %spotrebbe non riuscire anche su Mac OS X, ad es. datetime.strftime ('% s') dovrebbe rispettare tzinfo .
jfs

1
Non dovresti mai usare in %squanto utilizzerai solo l'ora di sistema localizzata del sistema in cui ti trovi. Si desidera utilizzare .timestamp()se si sta tentando di ottenere il vero timestamp UNIX.
Blairg23,

6

Per lavorare con i fusi orari UTC:

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

datetime.utcfromtimestamp(time_stamp)

Dopo un'ora di ricerche, questa è stata l'unica risposta che ha funzionato: ')
Qurashi,

4

Hai perso le informazioni sul fuso orario (già risposto, concordato)

arrowil pacchetto consente di evitare questa tortura con i tempi; È già scritto, testato, pubblicato su pypi, cross-python (2.6 - 3.xx).

Tutto ciò di cui hai bisogno: pip install arrow(o aggiungi alle dipendenze)

Soluzione per il tuo caso

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'

3

Se l'oggetto datetime rappresenta l'ora UTC, non utilizzare time.mktime, poiché si presume che la tupla sia nel fuso orario locale. Invece, usa calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60


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)

Se si desidera il timestamp UTC: time.mktimesolo per dt locale. L'usocalendar.timegm è sicuro ma dt deve essere la zona utc quindi cambiare la zona in utc. Se dt in UTC basta usare calendar.timegm.


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

0

Questa classe coprirà le tue esigenze, puoi passare la variabile in ConvertUnixToDatetime e chiamare su quale funzione vuoi che funzioni in base.

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())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.