Conversion de datetime.date en horodatage UTC en Python

295

Je traite des dates en Python et je dois les convertir en horodatages UTC à utiliser dans Javascript. Le code suivant ne fonctionne pas:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

La conversion de l'objet date d'abord en datetime n'aide pas non plus. J'ai essayé l'exemple sur ce lien , mais:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

et maintenant soit:

mktime(utc.localize(input_date).utctimetuple())

ou

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

fonctionne.

Question donc générale: comment puis-je obtenir une date convertie en secondes depuis l'époque selon UTC?

Andreas Jung
la source
doublon possible de Comment convertir l'heure locale en UTC en Python?
Piotr Dobrogost le
29
Je ne suis pas sûr d'être d'accord avec le marquer comme doublon. Bien que les solutions soient similaires, les questions ne le sont pas. L'un (celui-ci) tente de créer un horodatage à partir de a datetime.date, l'autre tente de convertir une représentation sous forme de chaîne d'un fuseau horaire à un autre. En tant que personne à la recherche d'une solution à ce problème, je ne peux pas conclure que ce dernier fournira la réponse que je recherche.
DRH
5
datetime (date.year, date.month, date.day) .timestamp ()
Julian
4
Duplicata possible de In Python, comment convertir un objet `datetime` en secondes?
Trevor Boyd Smith

Réponses:

462

Si d = date(2011, 1, 1)est en UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

Si dest dans le fuseau horaire local:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1et timestamp2peut différer si minuit dans le fuseau horaire local n'est pas la même instance de temps que minuit en UTC.

mktime()peut renvoyer un résultat erroné s'il dcorrespond à une heure locale ambiguë (par exemple, pendant la transition DST) ou s'il ds'agit d'une date passée (future) où le décalage utc pourrait avoir été différent et le C mktime()n'a pas accès à la base de données tz sur la plate-forme donnée . Vous pourriez utiliser un pytzmodule (par exemple via tzlocal.get_localzone()) pour accéder à la base de données tz sur toutes les plateformes . En outre, utcfromtimestamp()peut échouer et mktime()peut retourner l' horodatage non-POSIX si "right"fuseau horaire est utilisé .


Pour convertir un datetime.dateobjet qui représente la date en UTC sans calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

Comment puis-je obtenir une date convertie en secondes depuis l'époque selon UTC?

Pour convertir datetime.datetime(pas datetime.date) un objet qui représente déjà l'heure en UTC en l'horodatage POSIX correspondant (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Remarque: Il est nécessaire de fournir timezone.utc explicitement, sinon .timestamp()supposez que votre objet datetime naïf se trouve dans le fuseau horaire local.

Python 3 (<3.3)

De la documentation pour datetime.utcfromtimestamp() :

Il n'existe aucune méthode pour obtenir l'horodatage d'une instance datetime, mais l'horodatage POSIX correspondant à une instance datetime dt peut être facilement calculé comme suit. Pour un dt naïf:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

Et pour un dt averti:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Lecture intéressante: Epoch time vs time of day sur la différence entre Quelle heure est-il?et combien de secondes se sont écoulées?

Voir également: datetime a besoin d'une méthode "epoch"

Python 2

Pour adapter le code ci-dessus pour Python 2:

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

timedelta.total_seconds()est équivalent à (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6calculé avec la véritable division activée.

Exemple

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Méfiez - vous des problèmes de virgule flottante .

Production

2012-01-08 15:34:10.022403
1326036850.02

Comment convertir un datetimeobjet averti en horodatage POSIX

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

Sur Python 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

Sur Python 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()
jfs
la source
2
Pour le cas Python 2, pourquoi pas timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01
7
@ m01: datetime(1970, 1, 1)est plus explicite. fromtimestamp()est incorrect ici ( dtest en UTC et utcfromtimestamp()doit donc être utilisé à la place).
jfs
1
@fedorqui regarde la totimestamp()fonction dans la réponse.
jfs
14
autant que j'aime le python, sa lib date-heure est sérieusement cassée.
Realfun
3
@Realfun: les fuseaux horaires, les transitions DST, la base de données tz, les secondes intercalaires existent indépendamment de Python.
jfs
81

Pour les systèmes Unix uniquement :

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Remarque 1: dizzyf a observé que cela s'applique aux fuseaux horaires localisés . Ne pas utiliser en production.

Remarque 2: Jakub Narębski a noté que cela ignore les informations de fuseau horaire même pour les datetime compatibles avec l'offset (testé pour Python 2.7).

Ulf Aslak
la source
4
Une bonne réponse si vous savez sous quel système votre code fonctionnera, mais il est important de noter que la chaîne de format '% s' n'existe pas sur tous les OS, donc ce code n'est pas portable. Si vous voulez vérifier s'il est pris en charge, vous pouvez vérifier man strftimeles systèmes de type Unix.
Alice Heaton
5
Il convient de mentionner que cela supprime la partie des secondes fractionnaires (microsecondes ou autre).
Alfe
11
AVERTISSEMENT: cela s'applique aux fuseaux horaires localisés! Vous obtiendrez un comportement strftime différent pour des objets datetime identiques en fonction de l'heure de la machine
dizzyf
3
De plus, cela ignore les informations de fuseau horaire et suppose que datetime se trouve dans le fuseau horaire local, même pour les datetime sensibles à l'offset , avec tzinfo défini. Eh bien, au moins en Python 2.7.
Jakub Narębski
1
horodatage javascript = horodatage python * 1000
Trinh Hoang Nhu
38
  • Hypothèse 1: vous essayez de convertir une date en horodatage, mais comme une date couvre une période de 24 heures, aucun horodatage ne représente cette date. Je suppose que vous souhaitez représenter l'horodatage de cette date à minuit (00: 00: 00.000).

  • Hypothèse 2: la date que vous présentez n'est pas associée à un fuseau horaire particulier, mais vous souhaitez déterminer le décalage à partir d'un fuseau horaire particulier (UTC). Sans connaître le fuseau horaire de la date, il n'est pas possible de calculer un horodatage pour un fuseau horaire spécifique. Je suppose que vous souhaitez traiter la date comme si elle se trouvait dans le fuseau horaire du système local.

Tout d'abord, vous pouvez convertir l'instance de date en un tuple représentant les différentes composantes de l'heure à l'aide du timetuple()membre:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

Vous pouvez ensuite convertir cela en horodatage en utilisant time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Vous pouvez vérifier cette méthode en la testant avec l'heure de l'époque elle-même (1970-01-01), auquel cas la fonction doit renvoyer le décalage de fuseau horaire pour le fuseau horaire local à cette date:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 est de 8 heures, ce qui serait correct pour le fuseau horaire du Pacifique (où je suis).

DRH
la source
Je ne sais pas pourquoi cela a été rejeté. Il résout le problème de l'OP (le code de travail l'OP étiqueté comme "ne fonctionne pas"). Cela ne répond pas à ma question (convertir UTC datetime.datetime en horodatage), mais quand même… voter.
Thanatos
9
Faites attention. L'hypothèse que "vous voulez traiter la date comme si elle était dans le fuseau horaire du système local est la racine de tous les problèmes avec le fuseau horaire en python. À moins que vous ne soyez sûr à 100% de la localisation de la machine qui exécutera votre script et surtout si vous servez des clients qui peuvent venir de n'importe où dans le monde, TOUJOURS supposer que les dates sont en UTC et faire la conversion le plus tôt possible. Cela gardera les cheveux sur votre tête, croyez-moi.
Romain G
8

suivez le document python2.7, vous devez utiliser calendar.timegm () au lieu de time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)
Wang
la source
3
en quoi est-ce différent de ma réponse si dest en UTC?
jfs
8

J'ai défini mes deux fonctions

  • utc_time2datetime (utc_time, tz = None)
  • datetime2utc_time (datetime)

ici:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time
agittaire
la source
1
J'ai passé en revue votre exemple .. Façon de désordre et de complication pour une chose aussi simple .. Pas du tout pythonique.
Angry 84
@ Mayhem: Je l'ai nettoyé un peu et j'ai ajouté des commentaires. Si vous avez une solution meilleure et plus générique pour convertir du temps en datetime et être en mesure de définir le fuseau horaire - veuillez nous le faire savoir. Faites-moi également savoir à quoi devrait ressembler un exemple plus pythonique de mon code.
agittarius
3

la question est un peu confuse. les horodatages ne sont pas UTC - ils sont une chose Unix. la date pourrait être UTC? en supposant que ce soit le cas, et si vous utilisez Python 3.2+, simple-date rend cela trivial:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

si vous avez réellement l'année, le mois et le jour, vous n'avez pas besoin de créer date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

et si la date est dans un autre fuseau horaire (cela importe parce que nous supposons que minuit sans heure associée):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[l'idée derrière simple-date est de rassembler toutes les données de date et d'heure de python dans une classe cohérente, afin que vous puissiez effectuer n'importe quelle conversion. ainsi, par exemple, il ira également dans l'autre sens:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]

Andrew Cooke
la source
SimpleDate(date(2011,1,1), tz='America/New_York')déclenche NoTimezone, tandis que pytz.timezone('America/New_York')ne déclenche aucune exception ( simple-date==0.4.8, pytz==2014.7).
jfs
Je reçois une erreur pip install simple-date, on dirait que c'est python3 / pip3 uniquement.
NoBugs
3

Utilisation du package de flèche :

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)
Mathieu Longtin
la source
note: arrowutilise dateutil et dateutilconnaît depuis de nombreuses années des bugs liés à la gestion du fuseau horaire. Ne l'utilisez pas si vous avez besoin de résultats corrects pour les fuseaux horaires avec un décalage UTC non fixe (il est correct de l'utiliser pour le fuseau horaire UTC mais le stdlib gère également le cas UTC).
jfs
@JFSebastian Ce bogue ne se produit que pendant une transition, dans un temps ambigu.
Paul
On dirait que le bug a été corrigé le 28 mars
Mathieu Longtin
On dirait que c'est la meilleure solution et cross-python. Utilisez simplement la flèche . Merci;)
maxkoryukov
@MathieuLongtin dateutil peut avoir d' autres problèmes
jfs
1

Une chaîne temporelle complète contient:

  • Date
  • temps
  • utcoffset [+HHMM or -HHMM]

Par exemple:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

Remarque:

UNIX timestampest un nombre à virgule flottante exprimé en secondes depuis l'époque, en UTC .


Éditer:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600
kev
la source
6
Et comment cela répond-il à ma question?
Andreas Jung
1
@ user908088 Convertir 1970-01-01 06:00:00 +0500à 3600que vous avez demandé.
kev
1
@ user908088 Il n'y a pas timezoneà python2, vous pouvez faire le calcul ( 06:00:00- +0500= 01:00:00) manuellement.
kev
5
pourquoi cette réponse en haut même si elle a -1 voix, quelque chose de mal avec SO
Umair
1

Étant donné que vous avez un datetimeobjet appelé d, utilisez ce qui suit pour obtenir l'horodatage en UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

Et pour la direction opposée, utilisez ce qui suit:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Max
la source
0

je suis impressionné par la discussion approfondie.

mes 2 cents:

depuis importation datetime date importation datetime

l'horodatage dans utc est:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

ou,

timestamp = time.time()

si résulte maintenant de datetime.now (), dans le même DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
non dévoilé
la source