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.