Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Prochaines sessions inter-entreprises : 28-31 mars 2017 / 13-16 juin 2017
Sessions intra-entreprises sur demande.
Inscrivez-vous vite !

JavaScript Quiz #1

Un petit quiz JavaScript, pour changer. Voici une fonction de validation de dates, écrite en JavaScript :

  1. /**
  2. * Vérifie que la date passée sous la forme d'une chaîne de caractères
  3. * (exemple : "15/11/2008") est valide
  4. */
  5. function validateDate(dateAsString) {
  6. // Vérifie que la date est du format "JJ/MM/AAAA"
  7. var regex = new RegExp("^[0-9]{2}[/][0-9]{2}[/][0-9]{4}$", "g");
  8.  
  9. if (regex.test(dateAsString)) {
  10. // Vérifie en plus que les champs jour, mois et année sont
  11. // cohérents et correspondent à une date pas totalement farfelue.
  12. var fields = dateAsString.split("/");
  13. var day = parseInt(fields[0]);
  14. var month = parseInt(fields[1]);
  15. var year = parseInt(fields[2]);
  16.  
  17. if (day > 0 && day < 32 &&
  18. month > 0 && month < 13 &&
  19. year > 1970 && year < 2200) {
  20. return true;
  21. }
  22. }
  23. return false;
  24. }

Question : Certains jours de l'année 2009 ne passent pas la validation alors qu'ils le devraient. Lesquels, et pourquoi ?

Réponse : Le monstre de Loch Thal a encore frappé !

Je m'explique :

  • "07/01/2009" passe la validation, mais pas le jour suivant, "08/01/2008".
  • "31/07/2009" passe la validation, mais pas le jour suivant, "01/08/2009".
  • "10/01/2009" passe la validation, mais pas le jour précédent, "09/01/2009".

Vous y voyez plus clair ? Toujours pas ?

En fait, le problème vient du parseInt(day) et du parseInt(month), lorsque day, ou month (ou les deux) est égal à "08" ou à "09".

Pourquoi parseInt("08") et parseInt("09") poseraient-ils un problème ?

Parce que, comme l'indique la documentation JavaScript de la fonction parseInt :

Syntaxe : parseInt(chaîne, base)

Le paramètre base est utilisé pour spécifier la base numérique à utiliser : 10 pour décimal, 16 pour hexadécimal, 2 pour binaire, ... Si ce paramètre est omis, JavaScript suppose ce qui suit :

* Si la chaîne commence par "0x", la base est 16 (hexadécimal).

* Si la chaîne commence par "0", la base est 8 (octal). (Note : cette fonctionnalité est obsolète)

* Si la chaîne commence par tout autre signe, la base est 10 (décimal).

Ici, le split(dateAsString) fabrique toujours des chaînes à deux caractères pour le jour et le mois. Et comme, quand on appelle parseInt(), la base n'est pas spécifiée, JavaScript considère tous les nombres inférieurs à "10", comme "01" ou "06", comme des nombres en octal. Problème : en base 8, il n'y a que 8 chiffres différents, et il n'y a pas de chiffre 8 ou 9. Écrire 08 ou 09 en octal n'a aucun sens.

Résultat : si parseInt("07") renvoie bien 7, parseInt("08") renvoie... 0 ! Car JavaScript s'arrête au premier signe qu'il ne sait pas interpréter (ainsi parseInt("0259"), tout comme parseInt("025a"), renvoient tous deux le nombre octal 025, c'est-à-dire 21 en décimal). Cette valeur zéro est ensuite rejetée pour le jour, comme pour le mois, par le if qui suit.

Conclusion : la fonction de validation va systématiquement éliminer les 8ème et 9ème jour de chaque mois, ainsi que tous les jours des mois d'août et de septembre. Le monstre de l'octal a encore frappé, vous disais-je !


Commentaires

1. Le dimanche 18 janvier 2009, 11:20 par bhamp0

Héhé ... je vais essayer de ne pas donner un indice trop gros.
Je vais juste donner le nombre de dates en échec : 83 dates !

2. Le dimanche 18 janvier 2009, 15:46 par bhamp0

En fait, 81, je ne sais plus compter : 2 mois complets + 2 jours par mois.

3. Le dimanche 18 janvier 2009, 18:27 par HollyDays

Bien vu, bhamp0 !

4. Le vendredi 3 septembre 2010, 09:01 par Bludwarf

Est-ce que le plus simple ne serait pas d'utiliser la conversion automatique "chaine -> entier" de JS?. Ex:
nombre = 1*chaine

5. Le samedi 4 septembre 2010, 10:11 par HollyDays

Techniquement, cela fonctionne très bien. C'est donc une solution, sans aucun doute possible. Pour autant, je ne suis pas sûr que ce soit "plus simple".

D'une part, même si "1 * fields[0]" est plus concis à écrire que "parseInt(fields[0], 10)", c'est une expression qui dissimule particulièrement bien l'intention du programmeur (convertir la chaîne en entier). Elle est donc bien moins claire et lisible, sujette à bugs futurs : car à coup sûr, un programmeur, 6 mois ou 1 an plus tard, se dira que multiplier par un, ça ne sert à rien et il supprimera la multiplication, introduisant alors sans le savoir une régression. Pour éviter ça, il faut accompagner la multiplication d'un commentaire décrivant pourquoi ce code est astucieux, et du coup, on a perdu la concision qu'on pensait avoir trouvée.
J'ajoute pour terminer que cette solution est un peu moins efficace que le parseInt (puisqu'on fait une multiplication en plus).

Mais après tout, c'est une solution au quiz, et on est ici pour s'amuser, non ? ;-)

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.