756 votes

Comment créer un objet datetime tenant compte du fuseau horaire en Python ?

Ce que je dois faire

J'ai un objet datetime sans fuseau horaire, auquel je dois ajouter un fuseau horaire afin de pouvoir le comparer avec d'autres objets datetime avec fuseau horaire. Je ne veux pas convertir l'ensemble de mon application en application sans fuseau horaire pour ce seul cas particulier.

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

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

Il n'est pas vraiment surprenant que cette opération échoue, puisqu'il s'agit en fait d'une tentative de conversion. Remplacer semblait être un meilleur choix (conformément à Python : Comment obtenir une valeur de datetime.today() qui soit "timezone aware" ? ) :

>>> 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 suis sur le point de revenir à la modification de la chaîne d'entrée pour avoir un fuseau horaire avant de l'analyser (j'utilise dateutil pour l'analyse, si cela a de l'importance), mais cela semble incroyablement maladroit.

De plus, j'ai essayé ceci à la fois 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 encore prendre en charge l'ancien format de données. Une conversion unique des anciennes données n'est pas envisageable pour diverses raisons liées à la BS. 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 confiance raisonnable que toutes les données anciennes en question sont en UTC, je suis donc prêt à accepter le risque d'utiliser ce fuseau par défaut dans ce cas.

2 votes

unaware.replace() rendrait None s'il modifiait unaware en place. Le REPL montre que .replace() renvoie un nouveau datetime objet ici.

3 votes

Ce dont j'avais besoin quand je suis venu ici : import datetime; datetime.datetime.now(datetime.timezone.utc)

4 votes

@MartinThoma J'utiliserais le nom de l'entreprise. tz arg pour être plus lisible : datetime.datetime.now(tz=datetime.timezone.utc)

1voto

ilmatte Points 31

Je suis assez novice en Python et j'ai rencontré le même problème. Je trouve cette solution assez simple et pour moi elle fonctionne bien (Python 3.6) :

unaware=parser.parse("2020-05-01 0:00:00")
aware=unaware.replace(tzinfo=tz.tzlocal()).astimezone(tz.tzlocal())

1voto

leenremm Points 82

Voici une solution simple pour minimiser les modifications de votre code :

from datetime import datetime
import pytz

start_utc = datetime.utcnow()
print ("Time (UTC): %s" % start_utc.strftime("%d-%m-%Y %H:%M:%S"))

Heure (UTC) : 09-01-2021 03:49:03

tz = pytz.timezone('Africa/Cairo')
start_tz = datetime.now().astimezone(tz)
print ("Time (RSA): %s" % start_tz.strftime("%d-%m-%Y %H:%M:%S"))

Heure (RSA) : 09-01-2021 05:49:03

0voto

Turtles Are Cute Points 1068

Dans le format de la réponse de unutbu ; j'ai fait un module utilitaire qui gère des choses comme ça, 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')

0voto

Shide Points 31

Changement de fuseau horaire

import pytz
from datetime import datetime

other_tz = pytz.timezone('Europe/Madrid')

# From random aware datetime...
aware_datetime = datetime.utcnow().astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# 1. Change aware datetime to UTC and remove tzinfo to obtain an unaware datetime
unaware_datetime = aware_datetime.astimezone(pytz.UTC).replace(tzinfo=None)
>> 2020-05-21 06:28:26.984948

# 2. Set tzinfo to UTC directly on an unaware datetime to obtain an utc aware datetime
aware_datetime_utc = unaware_datetime.replace(tzinfo=pytz.UTC)
>> 2020-05-21 06:28:26.984948+00:00

# 3. Convert the aware utc datetime into another timezone
reconverted_aware_datetime = aware_datetime_utc.astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# Initial Aware Datetime and Reconverted Aware Datetime are equal
print(aware_datetime1 == aware_datetime2)
>> True

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X