Ya un bug pascal, t’es pas à l’heure !

Après le bug de l’an 2000, et en attendant celui de 2038  …

source : http://www.torange-fr.com/Holidays/Easter/P%C3%A2ques-oeuf-8218.html

Nous avons récemment écrit une fonction qui cherche à savoir si un jour est un jour de congés ou non.
Bon aucun soucis pour le jour de l’an, le 1er mai, le 8 mai et autre jour ‘fixe’. Par contre certains jours fériés sont variables d’une année à l’autre tels que le lundi de pâques, ascension et pentecôte.
Bon, heureusement l’ascension et la pentecôte c’est un certain nombre de jours fixes après pâques, respectivement 39 et 50. Parfait, il n’y a plus qu’à écrire une fonction pour calculer le jour de pâques selon l’année passé en paramètre et çà sera bon.

Bon, une visite rapide sur le web, wikipedia, comment trouver la date de pâques ?
Alors, c’est le premier dimanche suivant la première plaine lune de printemps. Ouah, c’est quand même un peu plus compliqué que la détermination d’une année bisextile.
Euh … bon bah si on doit coder çà on est pas sorti de l’auberge :) Ah, deux recherches plus tard vous trouverez qu’il existe déjà une fonction qui le calcule !
En php la fonction easter_date  renvoit le timestamp à minuit du jour de pâques pour une année. Bon la fonction n’est valide que jusqu’en 2037, probablement à cause du bug de 2038 lié à l’encodage du timestamp unix en 32bits signés …

Enfin bref, d’ici 2037 notre application aura été réécrite au moins 3 fois ou sera abandonnée.

Allez, on se lance dans une fonction isHoliday( $ts ) prenant en paramètre le timestamp à minuit du jour à évaluer. Je trouve çà pratique de manipuler les timestamp en php car les fonctions de formatage travaillent avec çà.
Je vous passe tous les détails d’implémentation, j’en viens à calculer le lundi de pâques. Facile, c’est 24 heures après le minuit de pâques,
donc : easter_date( date(‘Y’, $ts) ) + 24 * 3600
On compare çà avec $ts et si c’est égal alors c’est le lundi de pâques, non ?

function isHoliday( $ts ) {
// …
$easter = easter_date( date(‘Y’, $ts) );
if ( $easter + 24 * 3600 == $ts ) return true;
// …

return false;
}

Un jeu d’enfant vous me direz, et çà marcherait nikel pour de nombreuses années … autre que 2013 :)

Mais pourquoi donc ??
Pourquoi diable est ce que cela ne marche pas avec le timestamp à minuit de ce lundi de paque 2013 ?!? Relecture de code, au moins 3 fois, affichage de date(‘d-m-Y’, $ts) et date(‘d-m-Y’, $easter) sont bien égaux pourtant …
Je continue de chercher, j’affiche les deux timestamps comparés et effectivement il y a 3600 de différence : tiens une heure pile. Bon, c’est probablement lié au décalage par rapport à l’heure UTC, non ?
Je ré-écris deux ou trois trucs avec des new DateTime(), en reprenant les timestamps 24 heures après pâques : même résultat !

Tiens, étrange.

Hey, mais au fait,
il se passe quoi dans la nuit de samedi à dimanche ce week-end de pâques ? Bah ouais tiens, le décalage horaire pour l’heure d’été ! et il a lieu quand ce décalage horaire exactement ? Et bien dimanche à 3h00 où il sera en fait 4h00… Intéressant, notre jour de pâque ne compte que 23h donc ! Voilà d’où vient mon décalage de 3600 secondes. CQFD.

Voilà un problème qui est fun.
Allez expliquez çà à votre client … Il râlera car le calcul de jour férié ne fonctionne pas cette année :
« Mais si, çà marche depuis des années, vous avez du toucher quelque chose, c’est pas magique ? – Mais c’est quoi çà un ta-me-stamp ? vous vous moquez de moi ? »

Ouais … Car en plus c’est assez rare que le dimanche de pâques tombe précisément le jour du décalage horaire !

Alors, pour revenir à notre problème, on fait comment ?
Bon, un moyen simple de pas trop s’ennuyer, en fait, c’est de comparer les chaines de caractères date(‘d-m’, $timestamp) des deux timestamps. Ou encore de ‘forcer’ la remise à minuit après le calcul du lendemain de pâques (ou 39 jours pour l’ascension, et 50 pour la pentecôte). Pour ce faire, un moyen simple c’est la fonction strtotime(‘today’, $timestamp)
Je n’ai pas comparé les perfs des deux alternatives, si jamais le coeur vous en dit, vous pouvez le faire et me dire ce qu’il en est en commentaire de cet article ;-)

Bon, et voilà donc la tête de ma fonction finale, si cela vous intéresse :

function isHoliday( $ts ) {
// Licence : Creative Commons (BY)
// By Webpulser - http://goo.gl/76t31
  $fixed_holidays = array( ’01-01′, ’01-05′, ’08-05′, ’14-07′, ’15-08′, ’11-11′, ’25-12′ );
  $format = ‘d-m’;

 $dm = date($format, $ts);
  if ( in_array($dm, $fixed_holidays) ) return true;

 $easter = easter_date( date(‘Y’, $ts) );
  if ( date($format, $easter +   86400) == $dm ) return true;
  if ( date($format, $easter + 3369600) == $dm ) return true;
  if ( date($format, $easter + 4320000) == $dm ) return true;

 return false;
}

— Article rédigé par Guillaume Dufrêne

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

About Guillaume

Responsable Technique chez Webpulser, j'organise la production des solutions web innovantes de nos clients en utilisant les méthodologies agiles.

Réponses à “Ya un bug pascal, t’es pas à l’heure !”

  1. Sinon, on lit un tableau tout fait :-) VOus nous autoriseriez à reprendre cette formule sur notre site des congés http://www.mes-conges.fr ?

Trackbacks / Pingbacks

  1. bug de paques dans le calcul des congés | Webpulser developers' blog | Agence Web de création de site internet Webpulser Lille | Scoop.it - 02/04/2013

    [...] bug de paques dans le calcul des congés, comment faire pour calculer convenablement le lundi de paques, l'ascension et la pentecote ?  [...]