Ce que je dois faire
J'ai un objet datetime non conscient du fuseau horaire auquel je dois ajouter un fuseau horaire pour pouvoir le comparer à d'autres objets datetime sensibles à un fuseau horaire. Je ne veux pas convertir l'intégralité de mon application en fuseau horaire qui ne soit pas au courant de ce cas.
Ce que j'ai essayé
Tout d'abord, pour démontrer le problème:
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
J'ai d'abord essayé l'astimezone:
>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>
Ce n'est pas très surprenant que cela ait échoué, car il s'agit en fait d'une conversion. Remplacer semblait être un meilleur choix (selon Python: Comment obtenir une valeur de datetime.today () qui soit "consciente du fuseau horaire"? ):
>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>>
Mais comme vous pouvez le constater, remplacer semble définir le paramètre tzinfo, mais ne rend pas l'objet conscient. Je suis prêt à redéfinir la chaîne d'entrée afin de créer un fuseau horaire avant de l'analyser (j'utilise dateutil pour l'analyse, si cela compte), mais cela semble incroyablement simpliste.
De plus, j'ai essayé ceci dans python 2.6 et python 2.7, avec les mêmes résultats.
Contexte
J'écris un analyseur syntaxique pour certains fichiers de données. Je dois prendre en charge un ancien format dans lequel la chaîne de date ne comporte pas d'indicateur de fuseau horaire. J'ai déjà corrigé la source de données, mais je dois toujours prendre en charge le format de données hérité. Une conversion unique des données existantes n'est pas une option pour diverses raisons liées aux services commerciaux. Bien qu'en général, je n'aime pas l'idée de coder en dur un fuseau horaire par défaut, dans ce cas, cela semble être la meilleure option. Je sais avec une certitude raisonnable que toutes les données existantes en question sont en UTC, je suis donc prêt à accepter le risque de ne pas respecter cette valeur dans ce cas.
En général, pour créer une date et heure naïve prenant en compte le fuseau horaire, utilisez la méthode localize :
import datetime
import pytz
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)
now_aware = pytz.utc.localize(unaware)
assert aware == now_aware
Pour le fuseau horaire UTC, il n’est pas vraiment nécessaire d’utiliser localize
car il n’ya pas de calcul de l’heure avancée à traiter:
now_aware = unaware.replace(tzinfo=pytz.UTC)
travaux. (.replace
renvoie une nouvelle date et heure; elle ne modifie pas unaware
.)
Tous ces exemples utilisent un module externe, mais vous pouvez obtenir le même résultat en utilisant uniquement le module datetime, comme indiqué également dans this SO answer :
from datetime import datetime
from datetime import timezone
dt = datetime.now()
dt.replace(tzinfo=timezone.utc)
print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'
Moins de dépendances et pas de problèmes de pytz.
REMARQUE: Si vous souhaitez utiliser cela avec python3 et python2, vous pouvez également l'utiliser pour l'importation de fuseau horaire (codé en dur pour UTC):
try:
from datetime import timezone
utc = timezone.utc
except ImportError:
#Hi there python2 user
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return timedelta(0)
utc = UTC()
J'ai utilisé de dt_aware à dt_unaware
dt_unaware = dt_aware.replace(tzinfo=None)
et dt_unware à dt_aware
from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
mais répondre avant est aussi une bonne solution.
J'utilise cette instruction dans Django pour convertir une heure non consciente en heure consciente:
from Django.utils import timezone
dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
Je suis d’accord avec les réponses précédentes, et c’est bien si vous pouvez commencer en UTC. Mais je pense que c’est également un scénario courant pour les personnes de travailler avec une valeur prise en compte tz qui a un date/heure qui a un fuseau horaire local non UTC.
Si vous n'utilisiez que le nom, on pourrait probablement en déduire que replace () sera applicable et produira le bon objet prenant en compte la date et l'heure. Ce n'est pas le cas.
le remplacement (tzinfo = ...) semble être aléatoire dans son comportement. C'est donc inutile. Ne l'utilisez pas!
localiser est la fonction correcte à utiliser. Exemple:
localdatetime_aware = tz.localize(datetime_nonaware)
Ou un exemple plus complet:
import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())
me donne une valeur date/heure sensible à un fuseau horaire de l'heure locale actuelle:
datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
Ceci codifie @ (Sérgio) et @ unutbu réponses . Cela fonctionnera simplement avec un objet pytz.timezone
ou une chaîne fuseau horaire IANA .
def make_tz_aware(dt, tz='UTC', is_dst=None):
"""Add timezone information to a datetime object, only if it is naive."""
tz = dt.tzinfo or tz
try:
tz = pytz.timezone(tz)
except AttributeError:
pass
return tz.localize(dt, is_dst=is_dst)
Cela ressemble à ce que devrait faire datetime.localize()
(ou .inform()
ou .awarify()
): accepter les chaînes et les objets de fuseau horaire pour l'argument tz et utiliser la valeur UTC par défaut si aucun fuseau horaire n'est spécifié.
Afin d'ajouter le fuseau horaire local; (en utilisant dateutil)
from dateutil import tz
import datetime
dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)
dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())
deux méthode que je connais purement
from datetime import datetime
import pytz
naive = datetime.now()
aware = naive.replace(tzinfo=pytz.UTC)
ou
aware = pytz.UTC.localize(naive)
bien sûr, vous pouvez utiliser n’importe quel fuseau horaire au lieu de UTC,
Dans le format de la réponse de unutbu; J'ai créé un module utilitaire qui gère de telles choses, avec une syntaxe plus intuitive. Peut être installé avec pip.
import datetime
import saturn
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)
now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')