Blog ENI : Toute la veille numérique !
💥 Un livre PAPIER acheté
= La version EN LIGNE offerte pendant 1 an !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Données temporelles et algorithmes appliqués

Gérer une date calendaire

1. Notion de date calendaire

Une date est simplement la combinaison d’un jour, d’un mois et d’une année. Les trois éléments sont obligatoires. Il n’y a pas de notion d’instant, de secondes, de minutes ou d’heures.

Pour gérer une date, rien de plus simple, il suffit de créer un objet datetime.date en y affectant l’année, le mois et le jour.

>>> import datetime 
>>> d=datetime.date(2009, 7, 22) 
>>> d 
datetime.date(2009, 7, 22) 

En cas d’erreur dans les paramètres, une exception claire est levée :

>>> d2=datetime.date(2009, 2, 30) 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
ValueError: day is out of range for month 

Un tel objet propose :

>>> dir(datetime.date) 
['__add__', '__class__', '__delattr__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', 
'__lt__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', 
'__repr__', '__rsub__', '__setattr__', '__sizeof__', '__str__', 
'__sub__', '__subclasshook__', 'ctime', 'day', 'fromordinal', 
'fromtimestamp', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'min', 
'month', 'replace', 'resolution', 'strftime', 'timetuple', 'today', 
'toordinal', 'weekday', 'year'] 

Un tel objet possède trois propriétés...

Gérer un horaire ou un moment d’une journée

1. Notion d’instant

La notion d’instant recouvre la notion de temps qui s’écoule dans une journée, selon les règles habituelles et avec une précision qui dépend du contexte. Par exemple pour savoir quelle heure il est, l’heure et la minute suffisent. Lorsque l’on souhaite être plus précis, on peut ajouter la notion de seconde et enfin, lorsque l’on souhaite mesurer avec le maximum de précision, on peut descendre jusqu’au millionième de seconde.

Il n’est pas possible de donner une meilleure précision, celle-ci étant donnée par le matériel (et la cadence de celui-ci) via le système. Ces notions de précision sont visibles dans la signature du constructeur et dans la représentation de l’objet :

>>> datetime.time() 
datetime.time(0, 0) 
>>> datetime.time(13, 56) 
datetime.time(13, 56) 
>>> datetime.time(13, 56, 12) 
datetime.time(13, 56, 12) 
>>> datetime.time(13, 56, 12, 54321) 
datetime.time(13, 56, 12, 54321) 

Cet objet est idéal pour travailler sur des moments de la journée, sans considération du jour auquel il s’applique. Par exemple, pour travailler sur un agenda, on utilise des objets datetime.date pour les colonnes et datetime.time pour les lignes.

Ainsi, on peut comparer des horaires et il est plus facile de gérer les tranches horaires d’une journée. Cela peut également servir à mesurer des temps d’exécution, pour les tests de performance.

Par rapport à un objet datetime.date, l’objet datetime.time présente des fonctionnalités différentes. Rien ne s’apparente à la notion de date :

>>> diff=list(set(dir(datetime.date))-set(dir(datetime.time))) ...

Gérer un instant absolu

1. Notion d’instant absolu

Il s’agit d’identifier un instant par l’utilisation conjointe d’une date et d’un instant de la journée, situant un événement avec une exactitude déterminée.

Par exemple, on sait exactement quand l’éclipse du 11 août 1999 a débuté (dans -l’Atlantique Nord) et quand elle s’est terminée (en Inde) :

>>> debut_eclipse=datetime.datetime(1999, 8, 11, 8, 26, 17, 600000) 
>>> debut_eclipse 
datetime.datetime(1999, 8, 11, 8, 26, 17, 600000) 
>>> fin_eclipse=datetime.datetime(1999, 8, 11, 13, 40, 8, 500000) 

Une fois que l’on a deux instants absolus, on peut récupérer une durée facilement :

>>> fin_eclipse-debut_eclipse 
datetime.timedelta(0, 18830, 900000) 

2. Rapport avec les notions précédentes

L’objet datetime.datetime permet de gérer une date, exactement comme l’objet datetime.date.

>>> d=datetime.datetime(2009, 7, 22) 
>>> d 
datetime.datetime(2009, 7, 22, 0, 0) 

Cet objet a juste une précision plus importante :

>>> datetime.date.resolution 
datetime.timedelta(1) 
>>> datetime.datetime.resolution 
datetime.timedelta(0, 0, 1) 

Comme on le verra plus tard, le premier élément correspond à une granularité d’un jour, le second à une seconde et le troisième à une microseconde.

D’ailleurs, les deux objets datetime.datetime et datetime.date sont en relation :

>>> type.mro(datetime.datetime) 
[<class 'datetime.datetime'>, <class 'datetime.date'>, <class 'object'>] 

L’ensemble des notions exposées dans la section précédente sont également valables ici :...

Gérer une différence entre deux dates ou instants

1. Notion de différence et de résolution

Python permet de travailler sur une différence entre deux objets datetime.date, datetime.time ou datetime.datetime tout en respectant leur résolution, c’est-à-dire le plus petit intervalle en dessous duquel une différence signifie quand même une égalité (par exemple, deux objets datetime.date ayant une différence de deux heures restent égaux, car la résolution est d’un jour).

Voici la signature du constructeur :

datetime.timedelta([days [, seconds[, microseconds[, milliseconds[, 
minutes, [hours, [weeks]]]]]]]) 

Voici ce que cela donne lorsqu’on utilise chaque paramètre de manière unitaire :

>>> datetime.timedelta(1) 
datetime.timedelta(1) 
>>> datetime.timedelta(0, 1) 
datetime.timedelta(0, 1) 
>>> datetime.timedelta(0, 0, 1) 
datetime.timedelta(0, 0, 1) 
>>> datetime.timedelta(0, 0, 0, 1) 
datetime.timedelta(0, 0, 1000) 
>>> datetime.timedelta(0, 0, 0, 0, 1) 
datetime.timedelta(0, 60) 
>>> datetime.timedelta(0, 0, 0, 0, 0, 1) 
datetime.timedelta(0, 3600) 
>>> datetime.timedelta(0, 0, 0, 0, 0, 0, 1) 
datetime.timedelta(7) 

Ainsi, la représentation, quels que soient les paramètres utilisés dans le constructeur, repose uniquement sur la distinction entre les jours, secondes et microsecondes.

Ceci signifie que deux valeurs identiques construites de manières différentes ont la même représentation. De plus, il est possible d’utiliser les paramètres nommés :

>>> datetime.timedelta(weeks=2) 
datetime.timedelta(14) 
>>> datetime.timedelta(days=14) 
datetime.timedelta(14) 
>>> datetime.timedelta(milliseconds=950...

Spécificités des fuseaux horaires

Il est possible de gérer le fuseau horaire de référence très simplement :

>>> tz=datetime.timezone.utc 

Les autres se construisent en connaissant leurs décalages par rapport à ce fuseau de référence.

>>> tz=datetime.timezone(datetime.timedelta(hours=6)) 
>>> tz.tzname(datetime.datetime.now()) 
'UTC+06:00' 
>>> tz.utcoffset(datetime.datetime.now()) 
datetime.timedelta(0, 21600) 
>>> 21600/3600 
6.0 

Voici quelles sont les bornes :

>>> datetime.timezone.min 
datetime.timezone(datetime.timedelta(-1, 60)) 
>>> datetime.timezone.min.tzname(datetime.datetime.now()) 
'UTC-23:59' 
>>> datetime.timezone.max 
datetime.timezone(datetime.timedelta(0, 86340)) 
>>> datetime.timezone.max.tzname(datetime.datetime.now()) 
'UTC+23:59' 

Si l’on veut personnaliser un fuseau horaire, en particulier en lui donnant un nom précis ou un décalage non standard, il faut utiliser la classe datetime.tzinfo et la surcharger, comme ceci :

>>> class MyOffset(datetime.tzinfo): 
...     """My Offset""" 
...     def __init__(self, offset, name): 
...         self.__offset = datetime.timedelta(minutes=offset) 
...         self.__name = name 
...     def utcoffset(self, dt): 
...         return self.__offset 
...     def tzname(self, dt): 
...         return self.__name 
...     def dst(self, dt): 
...         return datetime.timedelta(0) 
...  
>>> myoffset=MyOffset(6, 'East 6') 

Il est à noter que l’implémentation...

Problématiques de bas niveau

1. Timestamp et struct_time

Python possède plusieurs manières de gérer les dates qu’elles aient pour résolution une journée ou une microseconde. Elles sont exposées plus haut.

Python possède également une structure struct_time qui se rapproche de la structure C :

Index

Clé

Minimum

Maximum

0

tm_year

1900

1

tm_month

1

12

2

tm_day

1

31

3

tm_hour

0

23

4

tm_min

0

60

5

tm_sec

0

60

6

tm_wday

0 (lundi)

6 (dimanche)

7

tm_yday

1

366

8

tm_isdst

-1

1

Les années peuvent être stockées sous forme de nombres de trois ou quatre chiffres. Les valeurs 69 à 99 incluses représentent les années 1969 à 1999, les valeurs 0 à 68 incluses représentent les années 2000 à 2068 et les valeurs 100 à 1899 incluses sont interdites, conformément à ce qui se fait en C. Par contre, les mois vont de 1 à 12 en Python au lieu de 0 à 11 en C.

L’attribut tm_isdst peut prendre les valeurs -1, 0 et 1 et permet de gérer le fait que la date soit locale ou UTC (DST : daylight saving time).

Il existe des ponts entre un timestamp et de la structure struct_time :

Fonction

DST

Origine

Destination

time.gmtime()

UTC

timestamp

struc_time

calendar.timegm()

UTC

struc_time

timestamp

time.localtime()

local

timestamp

struc_time

time.mktime()

local

struc_time

timestamp

Ces problématiques restent de bas niveaux et le module time est rarement utilisé, datetime répondant plus naturellement à la plupart des problématiques.

2. Mesures de performances

Pour mesurer le temps mis par un algorithme pour réaliser une opération, il y a deux manières de procéder. Soit on mesure le temps effectif entre le début de l’algorithme et sa fin, soit on ne mesure que le temps que le processeur a réellement octroyé...

Utilisation du calendrier

1. Présentation du module calendar

Python fournit un module calendar offrant toutes les fonctionnalités nécessaires pour gérer un calendrier ou une date dans un calendrier :

>>> import calendar 
>>> dir(calendar) 
['Calendar', 'EPOCH', 'FRIDAY', 'February', 'HTMLCalendar', 
'IllegalMonthError', 'IllegalWeekdayError', 'January', 
'LocaleHTMLCalendar', 'LocaleTextCalendar', 'MONDAY', 
'SATURDAY', 'SUNDAY', 
'THURSDAY', 'TUESDAY', 'TextCalendar', 'WEDNESDAY', '_EPOCH_ORD', 
'__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__ 
name__', 
'__package__', '_colwidth', '_locale', '_localized_day', 
'_localized_month', '_spacing', 'c', 'calendar', 'datetime', 'day_abbr', 
'day_name', 'different_locale', 'error', 'firstweekday', 'format', 
'formatstring', 'isleap', 'leapdays', 'main', 'mdays', 'month', 
'month_abbr', 'month_name', 'monthcalendar', 'monthrange', 'prcal', 
'prmonth', 'prweek', 'setfirstweekday', 'sys', 'timegm', 'week', 
'weekday', 'weekheader'] 

Commençons par les constantes. EPOCH renvoie à l’année d’origine du timestamp UNIX. Ainsi, un timestamp 0 correspond au premier janvier 1970, à minuit.

>>> calendar.EPOCH 
1970 

Puis, viennent les jours de la semaine :

>>> calendar.MONDAY ...