81 votes

Comment développer des pratiques de codage conçues pour se protéger contre les bogues liés aux années bissextiles ?

Microsoft vient d'annoncer qu'une erreur logicielle dans le calcul des dates (sur l'année bissextile) a provoqué une panne majeure de Windows Azure la semaine dernière.

Était-ce vraiment une simple erreur de jugement en contournant DateTime.Now.AddYears(1) sur une année bissextile ?

Quelles pratiques de codage auraient pu empêcher cela ?

EDIT Comme l'a souligné dcstraw DateTime.Now.AddYears(1) sur une année bissextile renvoie en fait la date correcte dans .NET. Il ne s'agit donc pas d'un bug du framework, mais manifestement d'un bug dans le calcul des dates.

95voto

Jon Skeet Points 692016

Une publicité éhontée :

Utiliser une meilleure API pour la date et l'heure

Les bibliothèques intégrées de date et d'heure de .NET sont terriblement difficiles à utiliser correctement. Elles faire vous permet de faire tout ce dont vous avez besoin, mais vous ne pouvez pas express Vous vous exprimez clairement à travers le système de caractères. DateTime est un désordre , DateTimeOffset peut vous faire croire que vous conservez les informations sur le fuseau horaire alors que ce n'est pas le cas, et TimeZoneInfo ne vous oblige pas à penser à tout ce que vous devriez considérer.

Aucun d'entre eux ne permet de dire "juste une heure du jour" ou "juste une date", ni de faire une distinction claire entre "heure locale" et "heure dans un fuseau horaire particulier". Et si vous voulez utiliser un calendrier autre que le calendrier grégorien, vous devez passer par la section Calendar la classe tout le temps.

Tout ceci est la raison pour laquelle je construis Heure de Noda - une bibliothèque alternative de date et d'heure basée sur un portage de l'application Joda Time "mais avec une nouvelle API (et plus légère) par-dessus.

Vous pouvez réfléchir à certains points, qui sont faciles à manquer si vous n'en êtes pas conscient :

  • Faire correspondre une date et une heure locales à celles d'un fuseau horaire particulier n'est pas aussi simple qu'on pourrait le croire. Une date/heure locale spécifique peut apparaître une fois, deux fois (ambiguïté) ou zéro fois (elle est ignorée) en raison des transitions d'heure d'été.
  • Les fuseaux horaires varient historiquement - plus de TimeZoneInfo est généralement disposé à révéler, franchement. (Il ne supporte pas un fuseau horaire dont l'idée de "l'heure normale" change avec le temps, ou qui passe en permanence à l'heure d'été).
  • Même avec la base de données zoneinfo, les identifiants des fuseaux horaires ne sont pas nécessairement stables. (CLDR s'occupe de cela ; quelque chose que j'espère supporter dans Noda Time éventuellement).
  • Les représentations textuelles des dates et des heures sont un cauchemar, non seulement en termes d'ordonnancement, mais aussi de séparateurs de date, de séparateurs d'heure et de choses bizarres comme les noms de mois génitifs.
  • Le début de la journée n'est pas toujours à minuit - au Brésil, par exemple, le passage à l'heure d'été au printemps fait passer l'horloge murale de 23 h 59 min 59 s à 1 h du matin.
  • Dans certains cas (enfin, un cas que je connais), un fuseau horaire peut obliger à sauter une journée entière - le 30 décembre 2011 n'a pas eu lieu à Samoa ! Je pense que la plupart des développeurs peuvent ignorer ce problème, mais...
  • Si vous comptez utiliser un calendrier autre que le calendrier grégorien, soyez prudent et assurez-vous que vous savez vraiment comment il doit se comporter.

En ce qui concerne les pratiques de développement spécifiques :

  • Réfléchissez à ce que vous essayez vraiment de représenter. Je m'attends à ce que le principal avantage de Noda Time soit de forcer les développeurs à choisir entre différents types de représentation de leurs données. Si l'on réussit cela, tout le reste est plus simple.
  • Testez en unité tout ce à quoi vous pouvez penser. Cela dépendra de ce que fait exactement votre système, bien sûr, mais notamment tenir compte des différents fuseaux horaires, de ce qui se passe lors des passages à l'heure d'été et, bien sûr, des années bissextiles.
  • Je conseillerais d'injecter une "interface de type horloge" - un service permettant d'indiquer l'heure actuelle - plutôt que de faire explicitement appel à DateTime.Now o DateTime.UtcNow ; cela rend plus facile (faisable !) le test unitaire
  • Si vous effectuez plusieurs opérations avec "now", obtenez cette date/heure une fois et s'en souvenir, plutôt que de demander plusieurs fois "maintenant" - sinon la valeur pourrait changer de façon malencontreuse entre les appels.
  • "Faire tout en UTC n'est pas toujours la solution non plus - si je veux savoir "quand exactement se produit 'deux semaines à partir de maintenant' dans mon fuseau horaire local", alors je dois stocker l'adresse de l'utilisateur. local la date et l'heure ainsi que le fuseau horaire.

25voto

dcstraw Points 1684

Il convient de noter que le bogue n'était probablement pas dû à une ligne comme celle que vous avez postée :

DateTime.Now.AddYears(1)

Cela ne crée pas une date invalide. Si vous exécutez :

(new DateTime(2012, 2, 29)).AddYears(1)

que vous obtenez le 28 février 2013. Je ne sais pas en quoi l'agent invité d'Azure est écrit mais ce doit être un appel différent qui a échoué. Une mauvaise façon de faire ça en .NET aurait été :

new DateTime(today.Year + 1, today.Month, today.Day)

Cela déclenche une exception si today est un jour bissextile. Cependant, le blog de Microsoft sur le problème d'Azure indique qu'ils ont créé une date invalide du 29 février 2013, ce que je ne suis pas sûr qu'il soit possible de faire avec DateTime dans .NET.

Je ne dis pas que DateTime y DateTimeOffset ne sont pas sujettes à des erreurs, mais je ne pense pas qu'elles auraient causé ce problème particulier.

2voto

wal Points 7092

Comment développer des pratiques de codage conçues pour se protéger des bogues liés aux années bissextiles ? Quelles sont les pratiques de codage qui auraient pu éviter ce problème ?

Les tests unitaires à des dates spécifiques, comme l'a mentionné John, sont une pratique de code qui peut aider, mais rien ne vaut ce que je définis comme un "test d'intégration manuel".

Changez l'horloge de votre serveur de développement/testbed et observez ce qui se passe lorsque le temps passe.

Ne vous embarrassez pas de détails pour savoir s'il s'agit d'une "pratique de codage" - Il est évident que vous ne pouvez pas le faire pour toutes les dates du calendrier - choisissez les dates qui vous intéressent, qu'il s'agisse du 29 février, des dates de fin de mois ou des dates de passage à l'heure d'été.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X