Suite à une mise à jour de mon aggrégateur en ligne, il y a quelques-uns des fils auxquels je suis abonné qui sont marqués comme mis à jour lors de chaque rafraîchissement de l'aggrégateur. Et cela même si le flux rss n'a pas été le moins du monde modifié depuis la fois précédente !

Avec l'aide d'un développeur de gregarius, nous avons finalement réussi à trouver l'origine du problème : les fils en question sont des flux au format RSS 2.0. Or, la spécification RSS 2.0 dit que la date d'un billet (champ PubDate) doit être formatée conformément à la norme RFC822. D'après cette norme, il n'est pas obligatoire d'indiquer le nombre de secondes dans la date. Résultat, certains fils écrivent la date comme suit : « Wed, 04 Jan 2006 19:37 +0200 »[1]. Mais bon, tout le monde le sait, dans le monde informatique il est plus facile de travailler avec des dates numériques qu'écrites en anglais. L'idée de base est donc d'utiliser la fonction strtotime de PHP pour convertir cette date en un timestamp...

Et c'est là que le bât blesse. Si les secondes ne sont pas spécifiées dans la date, PHP4 semble prendre les secondes courantes pour compléter. Résultat : deux appels subséquents à cette fonction avec exactement les mêmes paramètres donnent deux résultats différents. Le petit script ci-dessous met en évidence ce problème :

<?php
 $date = 'Wed, 04 Jan 2006 19:37 +0200' ;
 $time = strtotime($date);
 echo $time . ' - ' . date('Y-m-d H:i:s', $time) . "\n";
 sleep(5);
 $time = strtotime($date);
 echo $time . ' - ' . date('Y-m-d H:i:s', $time) . "\n";
 ?>

Bien entendu, on s'attend à ce que les deux affichages donnent exactement le même résultat... Eh bien non ! Il y a un écart de cinq secondes entre la première et la deuxième ligne comme on le voit ci-dessous :

1136396271 - 2006-01-04 18:37:51
1136396276 - 2006-01-04 18:37:56

Cela fait toujours plaisir de faire de l'informatique non déterministe, n'est-ce pas ? Ce comportement a été confirmé par mes tests sur PHP 4.3.10 et PHP 4.4.0, et semble avoir été corrigé dans les versions 5 de PHP.

Heureusement, j'ai trouvé un « workaround[2] » relativement simple que l'on peut appliquer. La page de manuel de strtotime nous apprend qu'il y a un deuxième paramètre optionnel qui est utilisé comme point de départ pour calculer des dates relatives (par exemple pour connaître la date dans neuf mois). En fixant ce deuxième paramètre à une valeur connue (et toujours identique), on obtient alors à nouveau un résultat déterministe lors des appels à cette fonction. Par exemple, le script de test précédent modifié comme suit (seule différence : ajout d'un 0 comme deuxième paramètre lors des appels à strtotime) :

<?php
 $date = 'Wed, 04 Jan 2006 19:37 +0200' ;
 $time = strtotime($date, 0);
 echo $time . ' - ' . date('Y-m-d H:i:s', $time) . "\n";
 sleep(5);
 $time = strtotime($date, 0);
 echo $time . ' - ' . date('Y-m-d H:i:s', $time) . "\n";
 ?>

nous donne le résultat suivant :

1136396220 - 2006-01-04 18:37:00
1136396220 - 2006-01-04 18:37:00

qui est celui que l'on attend depuis le début. Ouf ![3]

Bizarrement, je n'ai réussi à trouver aucune mention de ce bug lors de mes recherches pour essayer de comprendre ce qui se passait...

Notes

[1] Ce qui est tout à fait valide d'après les différentes spécifications.

[2] Quelqu'un a-t-il un bel équivalent français à me proposer pour ce terme ?

[3] Dans le cas de gregarius, ce workaround a été appliqué dans le changeset 1156.