web-dev-qa-db-fra.com

Conversion de la date/heure sensible à un fuseau horaire en heure locale en Python

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?

36
kes

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)
55
unutbu

Dans les versions récentes de Django (au moins 1.4.1):

from Django.utils.timezone import localtime

result = localtime(some_time_object)
57
user9876

Une solution robuste portable devrait utiliser la base de données tz. Pour obtenir le fuseau horaire local sous la forme pytztzinfo 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.

2
jfs

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. 

0
ecerulm