Une durée, c’est un truc qui peut ressembler à une date, mais qui n’en est pas une, et qui pourrait être simple… si les systèmes d’unités utilisés pour les représenter n’étaient pas aussi délirants.
C’est ce qui va être détaillé dans cette partie, en terminant par les spécificités de l’usage des durées en informatique.
- Une durée n’est pas une date (ça parait évident, mais ça va mieux en le disant)
- Le grand bazar des unités de durée
- Usage des durées en informatique
Une durée n’est pas une date (ça parait évident, mais ça va mieux en le disant)
Une date et une durée, ça se ressemble par bien des aspects. Une date comme une durée peuvent avoir des jours, des mois, des minutes, des secondes…
Mais c’est un piège ! Les deux concepts sont fondamentalement différents. La date représente un point dans le temps ; la durée représente la distance entre deux points dans le temps.
Pour symboliser un point (dans l’espace ou dans le temps), il n’y a que deux solutions possibles :
- Énoncer une définition ad hoc – très pratique dans les conversations courantes, beaucoup moins dans un cadre formel donc en informatique ;
- Donner la distance de ce point par rapport à une référence bien connue.
La différence entre les mesures d’espace et de temps, c’est que pour l’espace, on utilise des choses comme la latitude et la longitude. Ces normes sont bien des distances par rapport à un référentiel (l’équateur et le méridien de Greenwich), mais les unités dans lesquelles on exprime ces intervalles ne sont pas les unités habituelles de mesure de distance de la vie courante.
Pour les dates, on donne là aussi la distance par rapport à un repère connu (la naissance théorique de Jésus pour les dates, et « minuit » pour les heures), mais avec les unités de mesure de temps de la vie courante. D’où une certaine confusion.
Et donc :
Si les concepts peuvent se ressembler, mélanger durées et dates va mener à des erreurs de calcul et des résultats qui n’ont pas de sens.
En particulier, on ne peut pas utiliser une date pour exprimer une durée.
Le grand bazar des unités de durée
Le plus grand problème de la gestion des durées, c’est que les unités utilisées sont très variées, sans lien cohérent entre elles, et pire : certaines ne correspondent même pas à une durée fixe.
Des unités très variées
Si le système décimal s’est imposé dans presque tous les domaines tant il est pratique, la mesure du temps a résisté à cette décimalisation.
Un calendrier décimal-mais-pas-trop a existé quelques années en France, mais comme l’année ne compte pas un nombre de jours multiple de 10, la décimalisation était partielle et le dispositif a fini par être abandonné.
Quant aux subdivisions décimales de la journée, elles ont été employées en Chine et sont toujours utilisées dans certaines applications précises1, mais la norme est restée sur un découpage en vingt-quatre heures de soixante minutes.
On a donc, en partant de la seconde, unité de base du System International d’Unités :
- Des subdivisions décimales – dixièmes ou centièmes de secondes, millisecondes, microsecondes, nanosecondes, etc. La tierce n’est plus réellement usitée.
- Une minute qui vaut 60 secondes ;
- Une heure qui vaut 60 minutes ;
- Un jour qui vaut 24 heures ;
- Une semaine qui vaut 7 jours.
Arrivés là on constate qu’on a plein de facteurs multiplicatifs différents selon le niveau de précision, et pire : que les multiples suivants n’utilisent pas des valeurs constantes !
Des unités qui ne représentent pas toujours la même chose
La longueur peut varier
On a un mois dont la valeur dépend… eh bien du mois que l’on considère, et donc du calendrier.
Avec un calendrier grégorien, un mois c’est : 28, 30 ou 31 jours, ou 29 mais ça n’est pas possible toutes les années. Avec d’autres calendriers, un mois peut prendre encore d’autres valeurs, et il peut exister des jours décomptés en dehors des mois.
Selon un calendrier grégorien, un an dure toujours 12 mois… mais 365 ou 366 jours, pour des raisons astronomiques. Mais cette durée peut être différente (pour d’autres raisons astronomiques !) avec d’autres calendriers.
Dans tous les cas, ni un mois ni une année n’ont un nombre entier ou constant de semaines.
Pour les durées plus longues qu’une année, on retombe souvent sur un système décimal, avec des décennies, siècles et millénaires (10, 100 et 1000 ans).
Ah, et dans certains cas on doit aussi prendre en compte l’imprévisible seconde intercalaire, qui vient casser la régularité de tous les multiples de la seconde.
La date de début peut varier aussi
Ceci concerne surtout l’année. L’année civile a une durée et un point de départ qui dépendent du calendrier utilisé. Mais il existe aussi d’autres types d’années, comme : l’année scolaire (qui ne dure pas une année civile, si on ne compte pas les grandes vacances), l’année fiscale dont les dates peuvent être spécifiques à une entreprise et qui peut durer jusqu’à 15 mois dans certains cas, l’année liturgique (ici dans la religion catholique, dont les dates de début et de fin – et donc la durée – sont variables), etc.
Manipuler une durée exprimée dans une unité plus grande qu’une semaine implique de savoir comment gérer les incohérences qui arrivent avec ce type d’unité !
Et ça n’est pas fini !
Il y a des unités qui sont encore plus floues, ce sont les unités associées à un contexte spécifique. L’exemple classique, c’est la conduite de projet, pour laquelle « une journée » ne vaudra pas 24 heures.
Non, parce que dans ce contexte, « une journée », c’est sans doute « le temps travaillé par un employé pendant une journée » (donc peut-être 7 heures, si vous êtes en France et aux 35 heures réelles, et autre chose dans tous les autres cas), et « une semaine », probablement « le temps travaillé par un salarié pendant une semaine »… ce qui est également très variable selon votre pays et votre entreprise. Ça peut même ne pas être un multiple entier de « une journée de travail » si l’organisation ne prévoit pas des journées identiques toute la semaine. Tout ça est peut-être paramétrable dans le logiciel, etc.
Il y a aussi toutes les situations où la législation s’en mêle, ainsi que les raisons historiques, et c’est l’occasion de vous montrer un exemple pratique.
Imaginons que vous ayez un système de paie à gérer, et que les employés sont rémunérés tous les mois. Un salarié a une ligne de paie qui ne doit pas être appliquée sur tout le mois, mais seulement sur une partie de celui-ci (disons qu’il est arrivé en cours du mois). La question est : comment calculer la part à laquelle il a le droit ? Eh bien, il existe au moins huit façons de calculer « une fraction de mois », toutes étant employables en France2 ! Ces huit façons de découper un mois sont détaillées sur ce site, avec des exemples chiffrés. Je vous laisse imaginer les prises de tête quand plusieurs de ces systèmes sont utilisés en parallèle.
- Principalement pour les calculs astronomiques, avec le jour julien – exploité aussi dans de vieilles bases de données, et qui n’a rien à voir avec le calendrier julien – et le système TLE de paramètres orbitaux à deux lignes. Microsoft Excel gère également les dates en tant que jours fractionnaires, mais normalement l’utilisateur n’a pas à le faire directement.↩
- Pour l’anecdote, en France, dans beaucoup d’entreprises, ça n’est pas le même système de décompte qui est utilisé pour 1. les heures travaillées, 2. les congés payés pris normalement et 3. les congés payés pris en avance par rapport à leur droit théorique d’obtention. Souvent, poser des congés payés en avance compense exactement les heures travaillées – c’est le même système de calcul –, mais prendre des congés une fois leur droit acquis utilise un autre système de décompte légèrement plus avantageux !↩
Usage des durées en informatique
Des durées ? Quelles durées ?
Les durées sont souvent le parent pauvre des bibliothèques temporelles, quel que soit le langage de programmation choisi. C’est sans doute pour ça qu’on voit couramment des constantes en dur dans le code pour les représenter et les manipuler, comme :
public static final int SECONDS_PER_HOUR = 3_600;
Ce genre de code est généralement signe d’une arithmétique sur les dates et durées faite « à la main »… ce qui devrait vous alerter sur de probables bugs dans le programme qui les utilise.
Soyez surs de ce que vous faites en manipulant des durées
On l’a vu un peu plus haut, l’une des particularités des durées, c’est d’avoir des « unités » dont la taille réelle varie selon le contexte. C’est donc délicat à gérer et susceptible d’entrainer des bugs.
Avant de faire de l’arithmétique sur les durées et les dates, il est très important de bien se renseigner selon deux axes :
1. Vérifier le besoin fonctionnel
Tout d’abord, qu’est-ce qu’on demande exactement ?
Dès que l’on a besoin d’un calcul qui fait intervenir une unité de taille floue, quel est le comportement attendu ?
Si on me demande d’ajouter un mois, est-ce que je dois ajouter un nombre fixe de jours ? Est-ce que je dois tomber sur le même jour dans le mois suivant ? Et que se passe-t-il si cette dernière définition n’est pas possible, comme « ajouter un mois au 31 mars » ?
Avez-vous bien pensé à tous les cas tordus ?
2. Vérifier les hypothèses de votre framework
Ensuite, vérifiez les hypothèses de votre framework, et surtout qu’elles sont cohérentes avec votre besoin fonctionnel – surtout sur les cas aux limites.
D’une manière générale, évitez d’utiliser les unités mal définies tant que ça n’est pas strictement indispensable et que vous n’êtes pas certains du comportement obtenu.
Ne mélangez pas les choux et les carottes – pardon, les dates et les durées
Je sais, je l’ai déjà dit, mais je le répète. Un code tel que celui-ci doit vous alerter immédiatement :
public long projectRemainingTime(long currentDate, long remainingTime, long projectEnd) {
return currentDate + remainingTime - projectEnd;
}
Les problèmes qui devraient vous faire reprendre ce code sont :
currentDate
n’est pas une date, mais un entier, donc probablement un timestamp, par conséquent est mal nommée (puisque ça n’est pas une date) voire pire (si c’est le mauvais type utilisé) ;- le calcul mélange allègrement des dates (les timestamp
currentDate
etprojectEnd
) et une durée. Dont on ne sait même pas s’ils sont exprimés dans la même unité ! - on n’a aucune idée de ce qu’on renvoie (une date sous forme de timestamp ou une durée ? En quelle unité ?) en lisant la méthode.
Une version plus propre de cette méthode serait quelque chose comme :
public Duration projectRemainingTime(LocalDateTime now, Duration remainingTime, LocalDateTime projectEnd) {
return Duration.between(now, projectEnd).plus(remainingTime);
}
Ce qui est plus sûr, plus clair à la lecture (même si plus verbeux), et nous permet ne nous rendre compte de quelque chose : ce code a l’air de mesurer des différences de durées calendaires (jours de 24 heures), mais parle de projet. Est-ce qu’on ne voudrait pas des « jours de projets » dans ce contexte ? D’ailleurs, combien d’heures dure « un jour de projet » ? C’est des questions qui méritent d’être posées à qui de droit et documentées.
Communication entre langages – norme ISO 8601
Déjà évoquée ici, la norme ISO 8601 permet de standardiser la représentation de durées. Seule cette norme devrait être utilisée pour communiquer des durées entre deux systèmes informatiques hétérogènes.
Par exemple, P18Y9M4DT11H9M8S
représente une durée de 18 ans, 9 mois, 4 jours, 11 heures, 9 minutes et 8 secondes. Les années et mois utilisés sont calendaires.
Une durée est la distance entre deux points dans le temps – elle n’est donc pas une date et ne peut pas être représentée comme telle. Sa représentation passe par la combinaison d’un point de repère temporel et d’une « distance temporelle » à ce point.
La principale difficulté dans la gestion des dates est le système d’unités associé, qui conjugue :
- Des unités diverses avec des facteurs multiplicatifs différents entre eux ;
- Des unités qui ont des durées imprécises et dont la valeur peut dépendre du contexte (calendrier, dates considérées, contraintes légales…).
Les représentations informatiques des durées sont souvent le parent pauvre des bibliothèques de manipulation d’informations temporelles, et il faut résister à la tentation de se lancer dans des calculs « à la main ». Au contraire, tout calcul impliquant des durées mérite d’être vérifié pour être certain que ce qui est réalisé est bien ce qui était voulu – en plus d’employer les fonctions disponibles dans les bibliothèques. Et comme pour les dates, utilisez la norme ISO 8601 pour transférer des durées entre systèmes informatiques.