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.

340
demandé sur Community 2011-08-15 16:55:44

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 .)

407
répondu unutbu 2014-04-08 02:47:03

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()
66
répondu kang 2017-12-10 13:18:31

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.

61
répondu Sérgio 2014-05-15 21:00:12

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())
34
répondu Googol 2016-10-19 10:10:01

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é.

8
répondu hobs 2017-05-23 12:18:25

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>)
4
répondu paolov 2017-11-02 21:29:06

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())
2
répondu Ahmet 2017-07-22 00:15:14

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,

2
répondu Doom 2017-08-22 02:46:44

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')
0
répondu Turtles Are Cute 2016-09-25 23:31:51