Cours de Python / Manipuler dates et fuseaux horaires avec datetime et zoneinfo
Utilisez date pour un jour du calendrier et datetime avec un fuseau horaire lorsqu’un instant précis compte. Distinguez les valeurs sans fuseau des valeurs avec fuseau, puis fixez séparément la convention de stockage et celle d’affichage.
Choisir entre date, durée et instant
date représente un jour, datetime une date accompagnée d’une heure et timedelta une durée. Un datetime dit naive ne permet pas à lui seul de connaître le fuseau utilisé ; un datetime dit aware dispose des informations nécessaires au décalage horaire. Deux affichages indiquant 09:00 ne représentent pas nécessairement le même instant si l’un est à Séoul et l’autre en temps universel coordonné, ou UTC.
Une date de dépense peut désigner seulement le jour choisi par l’utilisateur. Lui inventer une heure de minuit puis la convertir entre fuseaux peut déplacer le jour affiché sans répondre au besoin initial. À l’inverse, pour l’instant d’une transaction, une valeur avec fuseau permet de comparer et de convertir la représentation sans perdre le repère temporel.
date.fromisoformat("2026-10-17") interprète ici une date au format année-mois-jour. Une date impossible comme 2026-02-30 est refusée avec ValueError. Ajouter sept jours utilise timedelta ; ajouter trente jours ne signifie pas automatiquement avancer d’un mois du calendrier. Définissez séparément la politique des échéances en fin de mois.
Convertir un instant UTC vers Séoul
Le module zoneinfo est disponible dans la bibliothèque standard à partir de Python 3.9. Il utilise des données de fuseaux IANA, pour Internet Assigned Numbers Authority. Avant l’exercice, assurez-vous que ces données sont disponibles : sur un système qui en manque, notamment certaines installations Windows, installez le paquet tzdata dans l’environnement du projet avec python -m pip install tzdata. Utilisez le même interpréteur pour l’installation et l’exécution. Sans données système ni paquet tzdata, ZoneInfo peut lever ZoneInfoNotFoundError ; une faute dans le nom du fuseau peut aussi provoquer cette erreur. La documentation des sources de données de zoneinfo explique leur recherche.
Enregistrez le programme complet en UTF-8 dans dates.py, dans un dossier d’exercice. Depuis ce dossier, lancez python dates.py avec l’environnement virtuel du projet sélectionné. Sous macOS ou Ubuntu hors environnement virtuel, utilisez python3 dates.py si python3 est votre commande ; adaptez également l’installation en python3 -m pip install tzdata si nécessaire.
from datetime import date, datetime, timedelta, timezone
from zoneinfo import ZoneInfo
day = date.fromisoformat("2026-10-17")
print(day + timedelta(days=7))
utc = datetime(2026, 10, 17, 0, 0, tzinfo=timezone.utc)
seoul = utc.astimezone(ZoneInfo("Asia/Seoul"))
print(utc.isoformat())
print(seoul.isoformat())
Résultat attendu :
2026-10-24
2026-10-17T00:00:00+00:00
2026-10-17T09:00:00+09:00
La première ligne ajoute sept jours au 17 octobre. Les deux suivantes montrent le même instant sous deux représentations : minuit UTC et 09:00 à Séoul pour cette date. isoformat conserve le décalage sous la forme +00:00 ou +09:00. La conversion change l’heure affichée, sans ajouter neuf heures à l’instant lui-même.
Le choix d’Asia/Seoul reprend l’exercice du cours. Pour un autre lieu, utilisez un identifiant IANA correspondant au lieu concerné plutôt qu’un décalage fixe choisi pour une seule date. Les règles peuvent varier selon la saison et évoluer ; les données de fuseaux du système déployé doivent donc être maintenues.
Comparer astimezone et replace
Enregistrez ce deuxième programme dans un fichier distinct, zones.py, puis lancez python zones.py depuis le même dossier, ou la commande python3 correspondante. Il utilise les mêmes données de fuseaux.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc = datetime(2026, 10, 17, 0, 0, tzinfo=timezone.utc)
print(utc.astimezone(ZoneInfo("Asia/Seoul")).isoformat())
print(utc.replace(tzinfo=ZoneInfo("Asia/Seoul")).isoformat())
try:
print(datetime(2026, 10, 17) - utc)
except TypeError:
print("naive and aware cannot be subtracted")
Résultat attendu :
2026-10-17T09:00:00+09:00
2026-10-17T00:00:00+09:00
naive and aware cannot be subtracted
La première ligne convertit l’instant avec astimezone. La seconde conserve les champs de l’horloge, 00:00, mais leur attribue l’interprétation de Séoul : elle désigne donc un autre instant. En la reconvertissant en UTC, vous obtiendriez 15:00 la veille, soit le 16 octobre. replace n’est pas un remplacement général d’astimezone.
Attribuer un fuseau à une valeur naive n’est légitime que si vous savez déjà ce que ses champs représentent. Ajouter arbitrairement UTC pour faire disparaître une erreur ne corrige pas une origine inconnue. La documentation de datetime et astimezone détaille la conservation de l’instant et le traitement du fuseau.
Dans le try, la soustraction mélange un datetime naive et une valeur aware : elle lève TypeError. Le gestionnaire affiche la phrase anglaise de l’exemple, qui signifie que ces deux sortes de valeurs ne peuvent pas être soustraites ainsi. Il faut d’abord établir leur convention temporelle, pas retirer des fuseaux au hasard.
Vérifier les changements de jour et le stockage
- Dans dates.py, remplacez seulement l’heure UTC 0 par 23 dans le constructeur datetime. Le jour calculé par date reste le 24 octobre, l’instant UTC devient
2026-10-17T23:00:00+00:00et Séoul affiche2026-10-18T08:00:00+09:00. Rétablissez ensuite l’heure 0. - Remplacez la chaîne de day par
"2026-02-30". Vérifiez ValueError avant les affichages, puis restaurez"2026-10-17". - Comparez la valeur UTC à sa conversion Séoul : leur différence vaut zéro. Comparez-la ensuite à la valeur créée par replace : cette différence ne vaut plus zéro. Gardez un ordre de soustraction explicite pour interpréter le signe.
Pour un instant à conserver, choisissez une représentation cohérente, par exemple UTC, puis convertissez pour l’affichage. Un décalage ISO enregistré représente l’offset de cet instant ; il ne conserve pas à lui seul l’identité d’un fuseau IANA ni toutes ses règles futures. Une application de rendez-vous futurs peut donc aussi avoir besoin de l’identifiant du lieu et d’une politique pour les heures ambiguës ou inexistantes lors des changements d’heure.
Pour un jour métier sans heure, conservez une date si c’est bien l’information demandée. Lors d’un échange JSON, sérialisez-la dans un format explicite puis vérifiez sa lecture : le cours sur les données JSON et leur validation explique pourquoi l’écriture d’une structure ne garantit pas la validité du contenu. Gardez la distinction entre jour et instant visible dans la saisie, le stockage et les rapports.




