105 votes

Obtenir un horodatage UTC en python avec datetime

Y a-t-il un moyen d'obtenir le timestamp UTC en spécifiant la date? Ce que j'attends:

datetime(2008, 1, 1, 0, 0, 0, 0)

devrait donner

1199145600

Créer un objet datetime naïf signifie qu'il n'y a pas d'informations de fuseau horaire. Si je regarde la documentation pour datetime.utcfromtimestamp, créer un timestamp UTC signifie omettre les informations de fuseau horaire. Donc je suppose que créer un objet datetime naïf (comme je l'ai fait) donnerait un timestamp UTC. Cependant:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

donne

2007-12-31 23:00:00

Y a-t-il encore des informations de fuseau horaire cachées dans l'objet datetime? Qu'est-ce que je fais de mal?

109voto

vaab Points 1639

Naïve datetime versus aware datetime

Les objets datetime par défaut sont dits "naïfs" : ils conservent les informations de temps sans les informations de fuseau horaire. Pensez à un datetime naïf comme un nombre relatif (par exemple : +4) sans origine claire (en fait, votre origine sera commune à toute la limite de votre système).

En revanche, pensez à un datetime conscient comme des nombres absolus (par exemple : 8) avec une origine commune pour le monde entier.

Sans les informations sur le fuseau horaire, vous ne pouvez pas convertir le datetime "naïf" vers une autre représentation non naïve du temps (où se situe +4 si nous ne savons pas d'où partir ?). C'est pourquoi vous ne pouvez pas avoir de méthode datetime.datetime.toutctimestamp(). (cf : http://bugs.python.org/issue1457227)

Pour vérifier si votre datetime dt est naïf, vérifiez dt.tzinfo, si c'est None, alors il est naïf :

datetime.now()        ## DANGER : retourne un datetime naïf pointant sur l'heure locale
datetime(1970, 1, 1)  ## retourne un datetime naïf pointant sur l'heure donnée par l'utilisateur

J'ai des datetimes naïfs, que puis-je faire ?

Vous devez faire une hypothèse en fonction de votre contexte particulier : La question que vous devez vous poser est la suivante : votre datetime était-il en UTC ? ou était-ce l'heure locale ?

  • Si vous utilisiez l'UTC (vous êtes hors de danger) :

    import calendar
    
    def dt2ts(dt):
        """Convertit un objet datetime en timestamp UTC
    
        les datetime naïfs seront considérés comme UTC.
    
        """
    
        return calendar.timegm(dt.utctimetuple())
  • Si vous n'utilisiez PAS l'UTC, bienvenue en enfer.

    Vous devez rendre vos datetimes non naïfs avant d'utiliser la fonction précédente, en leur redonnant leur fuseau horaire prévu.

    Vous aurez besoin du nom du fuseau horaire et de l'information sur si l'heure d'été était en vigueur lors de la création du datetime naïf cible (la dernière information sur l'heure d'été est nécessaire pour les cas particuliers) :

    import pytz     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Définissez votre fuseau horaire
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Définissez is_dst en conséquence

    Conséquences de ne pas fournir is_dst :

    Ne pas utiliser is_dst générera un horaire incorrect (et un timestamp UTC) si le datetime cible a été créé pendant un changement d'heure d'été (par exemple, en retirant une heure).

    Fournir un is_dst incorrect générera naturellement un horaire incorrect (et un timestamp UTC) uniquement en cas de chevauchement ou de trous dans l'heure d'été. Et, lorsque fournir également un horaire incorrect, se produisant dans des "trous" (heure qui n'a jamais existé en raison du changement d'heure de l'été), is_dst donnera une interprétation de comment considérer cet horaire erroné, et c'est le seul cas où .normalize(..) fera quelque chose, car il le traduira alors en un horaire réel (changeant le datetime ET l'objet DST si nécessaire). Notez que .normalize() n'est pas requis pour avoir un timestamp UTC correct à la fin, mais il est probablement recommandé si vous n'aimez pas l'idée d'avoir des horaires erronés dans vos variables, surtout si vous réutilisez cette variable ailleurs.

    et ÉVITEZ D'UTILISER CE QUI SUIT : (cf : Convertir le fuseau horaire du datetime avec pytz)

    dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))  ## MAUVAIS !!

    Pourquoi ? car .replace() remplace aveuglément le tzinfo sans prendre en compte l'heure cible et choisira un mauvais objet DST. Tandis que .localize() utilise l'heure cible et votre indice is_dst pour sélectionner le bon objet DST.

Ancienne réponse incorrecte (merci @J.F.Sebastien d'avoir soulevé cela) :

Heureusement, il est assez facile de deviner le fuseau horaire (votre origine locale) lorsque vous créez votre objet datetime naïf car il est lié à la configuration système que vous ne devriez probablement PAS changer entre la création de l'objet datetime naïf et le moment où vous voulez obtenir le timestamp UTC. Cette astuce peut être utilisée pour poser une question imparfaite.

En utilisant time.mktime nous pouvons créer un utc_mktime :

def utc_mktime(utc_tuple):
    """Retourne le nombre de secondes écoulées depuis l'époque

    Notez qu'aucun fuseau horaire n'est pris en compte.

    le tuple UTC doit être : (année, mois, jour, heure, minute, seconde)

    """

    if len(utc_tuple) == 6:
        utc_tuple += (0, 0, 0)
    return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))

def datetime_to_timestamp(dt):
    """Convertit un objet datetime en timestamp UTC"""

    return int(utc_mktime(dt.timetuple()))

Assurez-vous que votre objet datetime est créé dans le même fuseau horaire que celui qui a créé votre datetime.

Cette dernière solution est incorrecte car elle suppose que le décalage UTC actuel est le même que le décalage UTC à partir de l'ÉPOQUE. Ce qui n'est pas le cas pour de nombreux fuseaux horaires (à des moments spécifiques de l'année pour les décalages de l'heure d'été).

38voto

Chris Cogdon Points 108

Une autre possibilité est:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

Cela fonctionne car à la fois "d" et "epoch" sont des datetimes naifs, rendant l'opérateur "-" valide, et renvoyant un intervalle. total_seconds() transforme l'intervalle en secondes. Notez que total_seconds() renvoie un flottant, même si d.microsecond == 0

24voto

Mike Siomkin Points 102

Une solution simple sans utiliser de modules externes:

de datetime import datetime, fuseau horaire

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=fuseau horaire.utc).timestamp())

21voto

Mr. Mr. Points 987

Notez également la fonction calendar.timegm() telle que décrite dans cet article de blog :

import calendar
calendar.timegm(utc_timetuple)

La sortie devrait être conforme à la solution de vaab.

16voto

J.F. Sebastian Points 102961

Si l'objet datetime en entrée est en UTC :

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

Remarque : il renvoie un flottant, c'est-à-dire que les microsecondes sont représentées sous forme de fractions de seconde.

Si l'objet date en entrée est en UTC :

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

Voir plus de détails sur Converting datetime.date to UTC timestamp in Python.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X