Convertissez datetime en horodatage Unix et reconvertissez-le en python

186

J'ai dt = datetime(2013,9,1,11), et j'aimerais obtenir un horodatage Unix de cet objet datetime.

Quand je le fais, (dt - datetime(1970,1,1)).total_seconds()je reçois l'horodatage 1378033200.

Lors de la reconversion en utilisant datetime.fromtimestampj'ai obtenu datetime.datetime(2013, 9, 1, 6, 0).

L'heure ne correspond pas. Qu'est-ce que j'ai manqué ici?

Bridgewater
la source
2
Related
1
En remarque, si vous utilisez Python 3.3+, vous voulez vraiment, vraiment utiliser la timestampméthode au lieu d'essayer de le faire vous-même. D'une part, cela évite complètement la possibilité de soustraire les temps naïfs de différents fuseaux horaires.
abarnert
1
D'où dtvient-il? Est-ce une heure locale ou une heure UTC?
jfs
2
@abarnert J'aimerais également soumettre une demande de purge de toutes les pages de codes et symboles Unicode, c'est-à-dire d'interdire tous les langages non-ascii et non-par défaut. Les peintures de symboles sont toujours autorisées.
Daniel F
6
@DanielF: Demande refusée. Je ne suis pas intéressé à créer une langue mondiale ici, et même si nous le faisions, je ne voudrais pas rendre la vie impossible aux linguistes historiques et aux érudits de Tolkien. Unicode résout déjà le problème, sauf que certaines organisations et certains produits (le consortium TRON, les logiciels japonais qui utilisent Shift-JIS sur UTF-8, Microsoft livrent toujours un système d'exploitation par défaut cp1252 pour les fichiers texte des utilisateurs et divers SDK qui prétendent que UTF -16 est un jeu de caractères de largeur fixe et / ou la même chose que Unicode) doivent être punis pour les mettre en ligne.
abarnert

Réponses:

105

Ce que vous avez manqué ici, ce sont les fuseaux horaires.

Vous avez probablement cinq heures de congé UTC, donc 2013-09-01T11: 00: 00 local et 2013-09-01T06: 00: 00Z sont la même heure.

Vous devez lire le haut de la datetimedocumentation, qui explique les fuseaux horaires et les objets «naïfs» et «conscients».

Si votre datetime naïf d'origine était UTC, le moyen de le récupérer est d'utiliser à la utcfromtimestampplace de fromtimestamp.

D'un autre côté, si votre datetime naïve d'origine était locale, vous n'auriez pas dû en soustraire un horodatage UTC en premier lieu; utiliser à la datetime.fromtimestamp(0)place.

Ou, si vous aviez un objet datetime conscient, vous devez soit utiliser une époque locale (consciente) des deux côtés, soit convertir explicitement vers et depuis UTC.

Si vous avez ou pouvez mettre à niveau vers Python 3.3 ou version ultérieure, vous pouvez éviter tous ces problèmes en utilisant simplement la timestampméthode au lieu d'essayer de comprendre comment le faire vous-même. Et même si vous ne le faites pas, vous pouvez envisager d' emprunter son code source .

(Et si vous pouvez attendre Python 3.4, il semble que PEP 341 est susceptible de faire partie de la version finale, ce qui signifie que tout ce dont JF Sebastian et moi parlions dans les commentaires devrait être faisable avec juste la bibliothèque stdlib, et fonctionnant de la même manière sous Unix et Windows.)

Abarnert
la source
1
si dtest dans le fuseau horaire local, alors la formule de la question est incorrecte datetime.fromtimestamp(0)(époque dans le fuseau horaire actuel) doit être utilisée à la place de datetime(1970, 1,1)(époque unix en UTC).
jfs
@JFSebastian: Je ne voulais pas couvrir toutes les trois possibilités en détail, mais je suppose que vous avez raison, je devrais.
abarnert
btw, fromtimestamp(0)peut échouer si le système ne stocke pas les informations de fuseau horaire historique, par exemple sous Windows. pytzpourrait être utilisé dans ce cas.
jfs
@JFSebastian: Mais cela pytzn'aide pas à moins que vous ne sachiez déjà dans quel fuseau horaire vous vous trouvez; Pour ce faire par programme, vous avez besoin d'une bibliothèque différente qui obtient le fuseau horaire Windows actuel et le convertit en pytzfuseau horaire et / ou le recherche par nom.
abarnert
il existe un tzlocalmodule qui fonctionne également sous Windows. Voici comment vous pouvez convertir l'heure UTC en heure locale .
jfs
141

la solution est

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

unixtime = time.mktime(d.timetuple())
DmitrySemenov
la source
12
il suppose qu'il ds'agit d'un objet date / date / heure naïf qui représente l'heure locale (il peut échouer pour des heures ambiguës ou pour des dates passées / futures si le système d'exploitation ne fournit pas d'historique tz db (le décalage UTC peut avoir été différent dans le passé dans le fuseau horaire)). Plus d'options .
jfs
6
Personne ne se soucie de la solution en virgule flottante. Est-ce évident pour tout le monde?
Ivan Balashov le
4
Cela laisse tomber les microsecondes. Ça pourrait être intéressant.
Alfe
Le seul problème est que vous ne prenez pas en compte le fuseau horaire.
Blairg23
ça ne devrait pas. Si vous devez régler TZ, vous devez d'abord le faire.
Marcin Orlowski
69

Si vous souhaitez convertir une date / heure python en secondes depuis l'époque, vous devez le faire explicitement:

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

Dans Python 3.3+, vous pouvez utiliser à la timestamp()place:

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Francisco Costa
la source
5
Ne prend pas en compte les fuseaux horaires.
Blairg23
3
Vous ne souhaitez pas l'utiliser %s, car il sera localisé sur l'horloge du système sur lequel vous vous trouvez actuellement. Vous ne devriez jamais l'utiliser que .timestamp()pour obtenir l'heure Epoch / UNIX correcte.
Blairg23
3
%sne fonctionne pas sur les systèmes Windows ( ValueError: Invalid format string)
chrki
1
@ amitnair92 vient de mettre 8ou9
Francisco Costa
54

Plutôt que cette expression pour créer un horodatage POSIX dt,

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

Utilisez ceci:

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

J'obtiens la bonne réponse dans votre exemple en utilisant la deuxième méthode.

EDIT: Quelques suivis ... Après quelques commentaires (voir ci-dessous), j'étais curieux de savoir le manque de support ou de documentation pour %sin strftime. Voici ce que j'ai trouvé:

Dans la source Python pour datetimeet time, la chaîne STRFTIME_FORMAT_CODESnous dit:

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

Alors maintenant, si nous man strftime(sur les systèmes BSD tels que Mac OS X), vous trouverez un support pour %s:

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

Quoi qu'il en soit, c'est pourquoi il %sfonctionne sur les systèmes qu'il utilise. Mais il existe de meilleures solutions au problème d'OP (qui prennent en compte les fuseaux horaires). Voir la réponse acceptée de @ abarnert ici.

Pierre Darren
la source
4
C'est un comportement non documenté (je crois). Par exemple, sur Windows, il en résulte "Chaîne de format non valide".
Crescent Fresh
@CrescentFresh, intéressant. Vous pouvez avoir raison. Bien que je ne vois pas strftime("%s")dans la documentation, je viens de confirmer que cela fonctionne sur Mac et Linux. Merci.
Darren Stone du
1
apparemment, il ignore le champ tzinfo.
Daniel F
1
@DarrenStone: vous n'avez pas besoin de lire la source. Les deux time.strftime()et la datetime.strftimedocumentation déléguent à la strftime(3)fonction de plates-formes pour les directives non prises en charge. %speut échouer même sous Mac OS X, par exemple, datetime.strftime ('% s') doit respecter tzinfo .
jfs
1
Vous ne devez jamais utiliser %scar vous n'utiliserez que l'heure système localisée du système sur lequel vous êtes. Vous souhaitez utiliser .timestamp()si vous essayez d'obtenir le véritable horodatage UNIX.
Blairg23
6

Pour travailler avec les fuseaux horaires UTC:

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

datetime.utcfromtimestamp(time_stamp)
Emin Temiz
la source
Après une heure de recherche, c'était la seule réponse qui fonctionnait: ')
Qurashi
4

Vous avez manqué les informations sur le fuseau horaire (déjà répondu, accepté)

arrowpackage permet d'éviter cette torture avec les datetimes; Il est déjà écrit, testé, publié pypi, cross-python (2.6 - 3.xx).

Tout ce dont vous avez besoin: pip install arrow(ou ajouter aux dépendances)

Solution pour votre cas

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
la source
3

Si votre objet datetime représente l'heure UTC, n'utilisez pas time.mktime, car il suppose que le tuple est dans votre fuseau horaire local. Au lieu de cela, utilisez calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Matt Kramer
la source
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)

Si vous voulez un horodatage UTC: time.mktimejuste pour le dt local .Use calendar.timegmest sûr mais dt doit la zone utc alors changez la zone en utc. Si dt ​​en UTC, utilisez simplement calendar.timegm.

wyx
la source
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
la source
0

Cette classe couvrira vos besoins, vous pouvez passer la variable dans ConvertUnixToDatetime et appeler la fonction sur laquelle vous voulez qu'elle opère.

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())
cryptopotluck
la source