Comment convertir un objet datetime compatible avec un fuseau horaire en un autre objet datetime différent du fuseau horaire local?
Mon application particulière utilise Django (bien qu’il s’agisse en réalité d’une question générique en Python):
import iso8601
....
date_str="2010-10-30T17:21:12Z"
....
d = iso8601.parse_date(date_str)
foo = app.models.FooModel(the_date=d)
foo.save()
Cela provoque une erreur de la part de Django:
raise ValueError("MySQL backend does not support timezone-aware datetimes.")
Ce dont j'ai besoin c'est:
d = iso8601.parse_date(date_str)
local_d = SOME_FUNCTION(d)
foo = app.models.FooModel(the_date=local_d)
Que serait SOME_FUNCTION be?
En général, pour convertir une date/heure arbitraire prenant en compte un fuseau horaire en une date/heure naïve (locale), je voudrais utiliser le module pytz
et astimezone
pour convertir en heure locale et replace
pour rendre la date et l'heure naïves:
In [76]: import pytz
In [77]: est=pytz.timezone('US/Eastern')
In [78]: d.astimezone(est)
Out[78]: datetime.datetime(2010, 10, 30, 13, 21, 12, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
In [79]: d.astimezone(est).replace(tzinfo=None)
Out[79]: datetime.datetime(2010, 10, 30, 13, 21, 12)
Mais puisque votre date/heure semble être dans le fuseau horaire UTC, vous pouvez le faire à la place:
In [65]: d
Out[65]: datetime.datetime(2010, 10, 30, 17, 21, 12, tzinfo=tzutc())
In [66]: import datetime
In [67]: import calendar
In [68]: datetime.datetime.fromtimestamp(calendar.timegm(d.timetuple()))
Out[68]: datetime.datetime(2010, 10, 30, 13, 21, 12)
En passant, il vaudrait peut-être mieux stocker les dates/heures sous forme de dates/heures naïves au format UTC plutôt que locales. De cette façon, vos données sont agnostiques à l'heure locale et vous ne convertissez que si vous le souhaitez en heure locale ou en un autre fuseau horaire. Une sorte d'analogue à travailler en unicode autant que possible et à n'encoder que lorsque cela est nécessaire.
Donc, si vous acceptez que le meilleur moyen de stocker les dates/heures en UTC naïf soit défini, il vous suffit de définir:
local_d = d.replace(tzinfo=None)
Dans les versions récentes de Django (au moins 1.4.1):
from Django.utils.timezone import localtime
result = localtime(some_time_object)
Une solution robuste portable devrait utiliser la base de données tz. Pour obtenir le fuseau horaire local sous la forme pytz
tzinfo
object, utilisez le module tzlocal
:
#!/usr/bin/env python
import iso8601
import tzlocal # $ pip install tzlocal
local_timezone = tzlocal.get_localzone()
aware_dt = iso8601.parse_date("2010-10-30T17:21:12Z") # some aware datetime object
naive_local_dt = aware_dt.astimezone(local_timezone).replace(tzinfo=None)
Remarque: il peut être tentant d'utiliser quelque chose comme:
#!/usr/bin/env python3
# ...
naive_local_dt = aware_dt.astimezone().replace(tzinfo=None)
mais cela peut échouer si le fuseau horaire local a une variable utc offset mais que python n'utilise pas de base de données de fuseau horaire historique sur une plate-forme donnée.
En utilisant python-dateutil
, vous pouvez analyser la date au format iso-8561 avec dateutil.parsrser.parse()
, ce qui vous donnera une datetime
au courant dans le fuseau horaire UTC/Zulu.
À l’aide de .astimezone()
, vous pouvez le convertir en date et heure consciente dans un autre fuseau horaire.
Utiliser .replace(tzinfo=None)
convertira la date/heure consciente en une date/heure naïve.
from datetime import datetime
from dateutil import parser as datetime_parser
from dateutil.tz import tzutc,gettz
aware = datetime_parser.parse('2015-05-20T19:51:35.998931Z').astimezone(gettz("CET"))
naive = aware.replace(tzinfo=None)
En général, la meilleure idée est de convertir toutes les dates au format UTC et de les stocker de cette manière, puis de les reconvertir en locales, si nécessaire. J'utilise aware.astimezone(tzutc()).replace(tzinfo=None)
pour m'assurer d'être au format UTC et convertir en naïf.