Comment rendre un fuseau horaire de datetime inconscient conscient en python
Ce que je dois faire
j'ai un objet datetime-unknown datetime, auquel je dois ajouter un fuseau horaire pour pouvoir le comparer avec d'autres objets datetime-aware datetime. Je ne veux pas convertir mon application entière à timezone ignorant pour ce cas un héritage.
Ce que j'ai Essayé
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
D'abord, j'ai essayé 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 vraiment surprenant que cela ait échoué, puisque c'est en train d'essayer de faire une conversion. Remplacer semblait être un meilleur choix (comme dans Python: Comment obtenir une valeur de datetime.aujourd'hui () "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 voir, replace semble définir le tzinfo, mais ne rend pas l'objet conscient. Je me prépare à revenir au doctorage de l'entrée chaîne de disposer d'un fuseau horaire avant l'analyse (je suis en utilisant dateutil pour l'analyse, si ce qui compte), mais qui semble incroyablement encombrants.
J'ai aussi essayé python 2.6 et python 2.7, avec les mêmes résultats.
Contexte
j'écris un analyseur pour certains fichiers de données. Il y a un vieux format que je dois supporter où la chaîne de date n'a pas d'indicateur de fuseau horaire. J'ai déjà corrigé les données source, mais je dois encore soutenir le format de données legacy. Une conversion unique des données héritées n'est pas une option pour diverses raisons de business BS. Bien qu'en général, je n'aime pas l'idée de coder un fuseau horaire par défaut, dans ce cas, cela semble être la meilleure option. Je sais avec une confiance raisonnable que toutes les données en question sont en UTC, donc je suis prêt à accepter le risque de défaut de paiement dans ce cas.
9 réponses
en général, pour rendre un fuseau horaire de datetime naïf-conscient, utilisez la méthode de localisation :
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'y a pas de calcul de l'heure d'été pour gérer:
now_aware = unaware.replace(tzinfo=pytz.UTC)
works. ( .replace
renvoie une nouvelle datetime; elle ne modifie pas unaware
.)
Tous ces exemples utilisent un module externe, mais vous pouvez obtenir le même résultat en utilisant simplement le module datetime.
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 aucun problème de pytz.
NOTE: Si vous souhaitez l'utiliser avec python3 et python2, vous pouvez l'utiliser aussi pour l'import de fuseau horaire (codé 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 eu l'utilisation de dt_aware à dt_unware
dt_unaware = dt_aware.replace(tzinfo=None)
et dt_unware to 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 déclaration dans Django pour convertir un temps inconnu en un temps conscient:
from django.utils import timezone
dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
cela codifie les réponses de @Sérgio et de @unutbu 151970920" . Il " fonctionnera simplement "avec un objet pytz.timezone
ou une chaîne IANA time Zone .
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 datetime.localize()
(ou .inform()
ou .awarify()
) devrait faire, accepter à la fois les chaînes et les objets timezone pour l'argument tz et par défaut à UTC si aucun fuseau horaire n'est spécifié.
je suis d'accord avec les réponses précédentes, et est très bien si vous êtes d'accord pour commencer dans UTC. Mais je pense que c'est aussi un scénario courant pour les gens de travailler avec une valeur de conscience tz qui a une datetime qui a un fuseau horaire local non UTC.
si vous deviez simplement choisir votre nom, on déduirait probablement que replace() sera applicable et produira le bon objet datetime aware. Ce n'est pas le cas.
remplacer( tzinfo=... ) semble être aléatoire, dans son comportement . Il est par conséquent inutile. Ne pas utiliser ce!
localize 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 de datetime de zone de temps consciente de l'heure locale actuelle:
datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
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éthodes 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 d'unutbu; j'ai fait un module d'utilité qui gère des choses comme ceci, 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')