Les bugs de fuseau horaire passent souvent les tests ordinaires, car la plupart des jours se comportent normalement. Puis arrive un changement d’heure, une réunion internationale ou une règle politique modifiée. Une heure locale peut ne pas exister, ou exister deux fois. Le problème vient rarement d’une bibliothèque incapable; il vient plutôt d’un modèle qui a perdu trop tôt la différence entre instant, heure locale et fuseau.
Offset et fuseau ne sont pas identiques
Un offset dit seulement la différence avec UTC à un instant donné, par exemple +02:00. Un fuseau IANA comme Europe/Paris contient des règles historiques et futures. Le même lieu peut avoir des offsets différents selon la saison et l’année.
Pour enregistrer un instant passé, l’offset peut suffire. Pour planifier un événement futur, le fuseau est nécessaire. “9 h à Paris” n’est pas “toujours UTC+1”.
Certaines heures locales n’existent pas
Lors du passage à l’heure d’été, une tranche horaire peut être sautée. Une réunion prévue à 02:30 peut être impossible ce jour-là. L’application doit décider: refuser, déplacer à la prochaine heure valide ou demander confirmation.
Ne pas gérer ce cas produit des comportements arbitraires. L’utilisateur voit une heure acceptée par l’interface, puis le système l’exécute ailleurs.
Certaines heures se répètent
Lors du retour à l’heure d’hiver, une heure locale peut se produire deux fois. Sans information supplémentaire, convertir 01:30 en instant est ambigu. Les calendriers sérieux doivent choisir une règle ou demander à l’utilisateur.
Pour les logs, stocker l’instant UTC évite l’ambiguïté. Pour les entrées futures, conserver le fuseau permet de résoudre correctement le cas.
Les règles changent
Les pays modifient parfois leurs règles d’heure d’été ou d’offset. Les systèmes dépendent d’une base de données de fuseaux à jour. Convertir des événements futurs trop tôt peut figer une règle qui sera modifiée avant la date.
Mettre à jour tzdata est donc une opération de maintenance. Les conteneurs et environnements isolés peuvent garder des règles anciennes si personne ne les met à jour.
Les dates locales ne sont pas des instants
Un anniversaire, une date d’échéance ou une journée de rapport sont souvent des dates de calendrier. Les convertir à minuit UTC peut les déplacer pour certains utilisateurs. Si le domaine parle de jour local, stockez une date locale.
De même, ajouter 24 heures n’est pas toujours ajouter un jour de calendrier dans un fuseau avec changement d’heure. Ces opérations répondent à des questions différentes.
L’interface doit montrer l’intention
Lorsqu’un utilisateur planifie un événement, l’interface devrait afficher le fuseau et, si nécessaire, l’effet pour les participants. Lorsqu’une série récurrente change, elle doit dire si l’heure locale change ou si l’instant change.
Une explication avant l’enregistrement coûte moins cher qu’un support après une réunion manquée. Les invitations mises à jour et l’audit des changements aident à résoudre les désaccords.
Les API doivent être sans ambiguïté
Une chaîne comme 2026-03-29 02:30 sans fuseau est insuffisante pour représenter un instant. Une API doit demander soit un instant avec offset, soit des champs séparés pour date locale, heure locale et fuseau.
Les erreurs devraient distinguer heure inexistante, heure ambiguë et fuseau manquant. “Date invalide” n’aide pas un client à corriger son payload.
Les tests doivent couvrir les transitions
Testez les jours de changement d’heure, les zones à demi-heure, les hémisphères opposés et les règles historiques. Fixez explicitement la timezone dans les tests; dépendre de la machine CI rend le résultat fragile.
Ajoutez aussi des tests après mise à jour de tzdata. Une règle correcte cette année peut changer l’année prochaine.
Stockez l’intention
Un système fiable distingue instant, date locale, heure locale, fuseau et durée. Une fois cette différence conservée, l’heure d’été devient une règle de conversion, pas une surprise de production.
Les calendriers externes doivent rester cohérents
Lorsqu’un événement est envoyé à un calendrier, à un e-mail ou à un partenaire, la même intention doit voyager. Une invitation qui contient seulement un instant ne suffit pas toujours pour une série récurrente. Il faut transmettre la zone, la règle de répétition et les exceptions lorsque le format le permet.
Les mises à jour doivent préciser ce qui a changé: l’heure locale, le fuseau, une occurrence ou toute la série. Sinon les participants peuvent voir des horaires différents selon leur client.
Les interfaces doivent gérer les cas ambigus
Quand une heure est inexistante ou répétée, l’utilisateur mérite un choix explicite ou une règle visible. Cacher le problème derrière une correction automatique donne l’impression que le système a déplacé l’événement. Une petite alerte dans le formulaire évite un incident plus tard.
Les messages doivent être localisés avec soin. Un utilisateur ne pense pas en offset technique; il pense en ville, date, heure et conséquence visible.
Les exports calendrier doivent conserver cette même intention. Si une application envoie seulement un instant alors que l’événement est récurrent, les clients externes risquent de recalculer différemment les futures occurrences.
Perdre le fuseau original ne se corrige pas par un meilleur formatage plus tard. La bonne information doit être stockée au moment où l’utilisateur exprime son intention.