L’importance des timezone avec le passage à l’heure d’hiver

Dans les programmes, on gère souvent le temps. De base, c’est un truc assez pénible à faire à la main. Heureusement, en Python, ça reste assez simple avec la bibliothèque datetime. Créer un instant (une certaine heure un certain jour) est très facile. Théoriquement, il faudrait ajouter la notion de fuseaux horaires (time zones), mais ça complique les choses et c’est assez cauchemardesque. Alors on s’en passe. Après tout, tant que l’on reste dans le même fuseau horaire, il n’y a pas de problème.

Oui mais ce week-end, nous passons à l’heure d’hiver. Et là, mine de rien, ça peut être la cata…

Nous allons donc voir aujourd’hui pourquoi il est important de gérer les timezone même si on travaille dans une seule zone géographique.

Précisions sur la notion de date

Dans cet article, je vais vous parler de dates. Communément, il s’agit de l’information du jour, du mois et de l’année. Mais en informatique, il s’agit de la notion d’instant, c’est à dire qu’il faut ajouter l’information d’heure, minute, seconde voir même de microseconde. Une date au sens humain (un jour d’un mois d’une année) possède simplement les autres infromations à 0, donc minuit.

En Python, on manipule cette notion avec un objet de type datetime de la bibliothèque datetime… Oui je sais…

Je ne ferai pas de rappel de la bibliothèque datetime. J’utiliserai plus bas les objets de type datetime créés à partir de leur constructeur et les objets timedelta représentant des durées créés soit par soustraction de dates soit à partir de leur constructeur. Référez-vous à la documentation pour plus d’informations sur l’usage de la bibliothèque.

Les timezone, qu’est-ce que c’est ?

Si je vous donne rendez-vous pour un meeting le lundi 29 mars 2021 à 9h00, nous avons de fortes chances de nous y retrouver. Mais si vous échangez avec un contact sur la Silicon Valley… Et bien avec le décalage horaire, il va manquer une information.

La notion évidente des timezone, c’est de prendre en considération le décalage entre deux dates. Dans l’objet de type datetime, la notion de timezone est uniquement une information de durée qui est la durée de décalage par rapport à une référence.

Des dates naives

Le paradoxe des dates c’est qu’elles ne peuvent réellement être comparées que si elles possèdent l’information de timezone. En Python, une date sans timezone est dite naïve.

En Python, vous pouvez évidemment comparer deux dates naïves et faire des calculs entre elles. Mais vous ne pouvez pas faire de comparaisons entre une date naïve et une date avec un timezone (date aware).

Ajouter un timezone

Ajouter un timezone consiste à ajouter un décalage par rapport à un instant de référence. Évidemment, si nous parlons temps, la référence est UTC.

Mais on ajoute combien ? C’est là que rentre en jeu la base de données IANA. Celle-ci indique en fonction de la localisation géographique le nombre d’heures à ajouter ou soustraire. Attention… Ce n’est pas forcément un nombre d’heures strict. L’Australie par exemple est sur 3 fuseaux horaires : UTC+10, UTC+9:30 et UTC+8:45… Oui, Perth et Alice Springs ont un décalage de 45 minutes…

Évidemment, gérer à la main, c’est fastidieux. De plus, si vous travaillez sur des dates historiques, il faut considérer des changements administratifs. Le changement d’heure avec les heures d’hiver et d’été est une chose mais il y a aussi des curiosités comme les îles Samoa qui n’ont pas eu de vendredi 30 décembre 2011.

La bibliothèque standard n’avait pas d’implémentation concrète pour prendre en charge cette base avant la version 3.9. Je vais donc utiliser une bibliothèque tierce, pytz.

Note : une autre bibliothèque intéressante pour la gestion des timezone est dateutil. Celle-ci fait beaucoup plus que la gestion des timezone.

Utilisation de pytz

Pytz permet de créer des objets timezone, ce que nous allons faire après avoir importé la dépendance.

import pytz

paris_tz = pytz.timezone('Europe/Paris')

Nous lui avons évidemment précisé le timezone que nous souhaitons utiliser. Pour vous faciliter la vie, la lib propose une constante qui liste toutes les timezone prises en charge : pytz.all_timezones.

Nous pouvons maintenant créer une date. Il y a deux manières de localiser une date naïve soit par une méthode de l’objet de type datetime soit avec une méthode de l’objet de type timezone. Vous avez les deux cas ci-dessous, n’utilisez évidemment qu’une seule des deux dernières lignes dans votre code.

from datetime import datetime, timedelta

naive_summer_time = datetime(2021, 10, 30)

# Localisation de date à partir de datetime
summer_time = naive_summer_time.astimezone(paris_tz)

# Localisation de date à partir de pytz
summer_time = paris_tz.localize(naive_summer_time)

À partir de là, nous avons des objets aware, c’est à dire qu’ils savent où ils sont dans le temps indépendamment de leur lieu de création.

Passage à l’heure d’hiver

Cette année 2021, nous passons à l’heure d’hiver dans la nuit du 30 au 31 octobre. Pour rappel, l’heure d’hiver correspond à l’heure légale. Le passage à l’heure d’été ajoute une heure à l’heure légale.

Le 1er novembre, nous serons donc en heure d’hiver. Nous pouvons créer un objet de type datetime à cette date, le localiser et comparer nos deux dates localisées :

naive_winter_time = datetime(2021, 11, 1)
winter_time = naive_winter_time.astimezone(paris_tz)

print(winter_time - summer_time)

Ceci affichera : 2 days, 1:00:00 ce qui correspond bien à une différence de deux jours et d’une heure.

Mais si nous faisons la même comparaison avec naive_winter_time - naive_summer_time, alors nous aurons 2 days, 0:00:00 soit deux jours tout rond. Ce qui est faux.

Vous remarquez que les timezones ne se limitent pas à des besoins liés à deux endroits géographiques différents. Elles sont également indispensables pour gérer les différences entre deux instants en fonction des contraintes administratives tel que les changements d’heure.

La particularité du jour du changement d’heure

En 2021, le passage à l’heure d’hiver se fait la nuit du 30 au 31 octobre. Vous remarquerez que je n’ai pas utilisé la date du 31 octobre et pour cause. Le changement d’heure se faisant à 3 heure cette nuit là, le 31 octobre 2021, pour Python qui considère l’instant à minuit, nous sommes toujours en heure d’été.

Par contre, du 31 octobre (à minuit) au 31 octobre à 4 heure, il s’est bien écoulé 5 heures :

>>> summer_time = datetime(2021, 10, 31).astimezone(paris_tz)
>>> naive_winter_time = datetime(2021, 10, 31, 4).astimezone(paris_tz)

>>> print(winter_time - summer_time)
5:00:00

Et pour exactement 3 heure du matin, nous avons :

>>> summer_time = datetime(2021, 10, 31).astimezone(paris_tz)
>>> unknown_time = datetime(2021, 10, 31, 3).astimezone(paris_tz)

>>> print(unknown_time - summer_time)
4:00:00

Le timezone n’a pas conscience du changement d’heure

Dernière chose, le timezone pour un objet datetime est une information de décalage. C’est donc une constante fixée. Si vous calculez une nouvelle date (un nouvel instant) à partir d’une date aware, c’est pour produire une date avec le même timezone. Et c’est là que ça peut être confus car même timezone signifie même décalage par rapport au temps de référence.

Allez, imaginons que vous lanciez une impression 3D le 30 octobre à 20 heure. Cette impression va durer 16 heures. Quand va-t-elle se terminer ? De manière naïve, nous pourrons faire :

>>> start_print = datetime(2021, 10, 30, 20).astimezone(paris_tz)
>>> print_duration = timedelta(hours=16)
>>> end_print = start_print + print_duration

>>> print(f"Fin à {end_print}")
Fin à 2021-10-31 12:00:00+02:00

Raté… D’un point de vue informatique, l’instant est le bon mais d’un point de vue informatif (humain), l’information est erronée. L’objet datetime produit a conservé un timezone de +2 heures alors que nous basculons à une période où l’heure est exprimée en +1 heure. En gros, c’est comme si vous aviez oublié de basculer en heure d’hiver.

Il est donc indispensable de re-localiser l’heure :

>>> start_print = datetime(2021, 10, 30, 20).astimezone(paris_tz)
>>> print_duration = timedelta(hours=16)
>>> end_print = (start_print + print_duration).astimezone(paris_tz)

>>> print(f"Fin à {end_print}")
Fin à 2021-10-31 11:00:00+01:00

Et maintenant, nous avons la bonne information.

Vers des bonnes pratiques

Gérer les timezone relève du cauchemar. On essaye d’esquiver autant que possible. Mais vous pouvez voir qu’il n’y a pas vraiment d’échappatoire. Si vous la négligez, il arrivera un moment où l’information d’heure que vous déduirez sera fausse même si la valeur est vrai.

Il y a quelques bonnes pratiques de code et d’organisation qui permettent de limiter les problèmes. Mais je préfère dédier un article à ce sujet, je remplacerai cette annonce par le lien dès sa publication.

Si vous avez aimé ce post, n’hésitez pas à laisser un commentaire ci-dessous ou sur la page Facebook 😉

À propos de... Darko Stankovski

iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.

Vous aimerez aussi...

Laisser un commentaire

En naviguant sur Dad 3.0, vous acceptez l’utilisation de cookies pour une navigation optimale et nous permettre de réaliser des statistiques de visites. Plus d'informations

Le blog Dad 3.0 utilise les cookies pour vous permettre une navigation optimale et nous permettre de réaliser des statistiques de visite. Dad 3.0 affichant des publicités, celles-si utilisent également des cookies pour un ciblage publicitaire. En continuant la navigation sur Dad 3.0, vous acceptez le dépôt et la lecture de cookies.

Fermer