Zend_Date me fait perdre mon temps avec l'heure d'été
Il n'y a pas si longtemps, j'expliquais comment Zend_Date provoquait des sauts étranges dans le temps. J'ai expliqué la nature du problème et comment le corriger. Toujours avec le même composant (Zend Framework, version 1.10.8) mais dans un autre contexte, un client s'est plaint que l'heure faisait un bond d'une heure lorsqu'il en sélectionnait une le 11 mars 2012. Mon intuition : un conflit avec l'heure d'été...
Après vérification, passage à l’heure d’été au Québec a lieu le deuxième dimanche du mois de mars, à 2 heures du matin (heure locale). Ça correspond effectivement au 11 mars.
Pour constater le problème, j'ai isolé le code comme ceci :
require_once('Zend/Loader/Autoloader.php');Pourtant...
$autoloader = Zend_Loader_Autoloader::getInstance();
$locale = new Zend_Locale('fr_ca');
Zend_Registry::set('Zend_Locale', $locale);
// OK
$date = new Zend_Date('2012-03-11 08:00:00');
echo $date->toString('yyyy-MM-dd HH:mm:ss');
// 2012-03-11 08:00:00
$date = new Zend_Date('2012-03-11');En définissant la date et l'heure séparément, l'impression indique 9h.
$date->setTime('08:00:00');
echo $date->toString('yyyy-MM-dd HH:mm:ss');
// 2012-03-11 09:00:00
J'ai écrit une boucle pour voir où ça se passe.
$date = new Zend_Date('2012-03-11');Si c'est un problème de DST (Daylight Saving Time), le retour à l'heure normale présenterait le même problème. Au Québec, le retour à l’heure d’hiver a lieu le premier dimanche du mois de novembre à 2 heures du matin (heure locale), soit le 4 novembre.
$date->setTime('00:00:00');
for($i=0 ; $i<=6 ; $i++){
echo $date->toString('yyyy-MM-dd HH:mm:ss');
$date->addHour(1);
}
/*
2012-03-11 00:00:00
2012-03-11 01:00:00
2012-03-11 03:00:00 // 2h AM est disparu
2012-03-11 04:00:00
2012-03-11 05:00:00
2012-03-11 06:00:00
2012-03-11 07:00:00
*/
Constatons cela.
$date = new Zend_Date('2012-11-04');Comment solutionner ce problème ? Malheureusement, c'est un bogue connu et documenté chez Zend et qui ne semble toujours pas réglé. Certaines personnes prétendent qu'il faut désactiver le DST en modifiant le timezone à GMT0 :
$date->setTime('00:00:00');
for($i=0 ; $i<=12 ; $i++){
echo $date->toString('yyyy-MM-dd HH:mm:ss');
$date->addHour(1);
}
/*
2012-11-04 00:00:00
2012-11-04 01:00:00
2012-11-04 01:00:00 // deux fois 1h AM
2012-11-04 02:00:00
2012-11-04 03:00:00
2012-11-04 04:00:00
...
*/
date_default_timezone_set('GMT0');Effectivement, tous les tests précédents ne montrent plus les sauts. Mais un simple affichage de date('Y-m-d h:i:s') montre aussi que le jour et l'heure ne représentent plus la situation du Québec (il est présentement environ 21h et ça indique le lendemain à 2h du matin). À ne pas retenir.
Sur StackOverflow, un utilisateur suggère un workaround en effectuant deux appels de suite à setTime() pour forcer la fonction à vérifier si le DST a changé. Je confirme que ça fonctionne bien et qu'à défaut d'avoir une solution plus élégante, c'est elle que j'ai mis en place dans le projet. Accompagnée d'un commentaire sympathique dans le code pour expliquer la situation au prochain programmeur qui passera par là.