Contents
Continuons notre série Les petites choses marrantes en PHP, consacrées aux petites choses marrantes en PHP, avec quelques exemples de petites choses marrantes en matière de résolution de date par Datetime et strtotime.
Mais attention, que les choses soient bien claires: il ne s’agit pas de troll! Juste de faire gaffe…
Une date normale
Donc, la documentation de PHP nous indique que Datetime et strtotime interprètent de la même façon les formats de date.
1 | echo date_default_timezone_get(); //UTC |
1 2 | $date = strtotime('1970-01-01 00:00:00'); date('Y-m-d H:i:s', $date); //"1970-01-01 00:00:00" |
1 2 | $date = new DateTime('1970-01-01 00:00:00'); $date->format('Y-m-d H:i:s'); //"1970-01-01 00:00:00" |
Une date vide
Sauf que cela devient marrant si on passe une string vide: DateTime retourne la date et l’heure courante, tandis que strtotime retourne la date du 1er janvier 1970, chère au système UNIX.
1 2 | $date = strtotime(''); date('Y-m-d H:i:s', $date); //"1970-01-01 00:00:00" |
1 2 | $date = new DateTime(''); $date->format('Y-m-d H:i:s'); //"2014-03-10 12:29:58" |
Marrant non?
L’an zéro
Sauf que cela devient marrant si on passe l’an zéro comme on la trouve par défaut dans MySQL: strtotime retourne à nouveau la date du 1er janvier 1970, tandis que DateTime ne retrouve plus ses petits.
1 2 | $date = strtotime('0000-00-00 00:00:00'); date('Y-m-d H:i:s', $date); //"1970-01-01 00:00:00" |
1 2 | $date = new DateTime('0000-00-00 00:00:00'); $date->format('Y-m-d H:i:s'); //"-0001-11-30 00:00:00" |
Marrant non?
Le bug de 2038
Sauf que cela devient marrant si on passe une date qui dépasse 32 bits, soit le 19 janvier 1938 à 3h, 14 min et 7 sec: si DateTime s’en sort sans problème, strtotime retourne encore et toujours la date du 1er janvier 1970.
1 2 | $date = strtotime('2039-01-01 00:00:00'); date('Y-m-d h:i:s', $date); //"1970-01-01 00:00:00" |
1 2 | $date = new DateTime('2039-01-01 00:00:00'); $date->format('Y-m-d h:i:s'); //"2039-01-01 00:00:00" |
Marrant, non?
Timezone
Dernière considération, tous ces tests ont été fait avec un timezone neutre, soit l’UTC.
1 | echo date_default_timezone_get(); //UTC |
Mais que se passe-t-il si on modifie le timezone?
1 2 | date_default_timezone_set('Asia/Tokyo'); echo date_default_timezone_get(); //Asia/Tokyo (soit + 9 heures) |
Normalement, le timezone n’a pas d’impact sur le formatage de la date depuis une string, dans la mesure où l’on ne travaille pas avec des timestamp qui, eux, sont absolus et indépendants du fuseau horaire.
1 2 3 4 | $date = strtotime('1970-01-01 00:00:00'); //sous le timezone 'Asia/Tokyo', $date vaut -32400 et pas 0 //mais c'est bien minuit qui est affiché date('Y-m-d H:i:s', $date); //"1970-01-01 00:00:00" |
Le 1er janvier 1970 informatique est basé sur l’UTC. Il était donc 9 heures du matin au Japon. Logique.
1 2 | $date = 0; date('Y-m-d H:i:s', $date); //"1970-01-01 09:00:00" |
Dès lors, lorsque que strtotime() retourne la date du 1er janvier 1970 par défaut, il faut bien sûr y voir la date UTC, soit le timestamp 0. Le timestamp est ensuite formaté par date() selon le timezone défini, soit avec un décalage de 9 heures pour Tokyo.
1 2 | $date = strtotime(''); date('Y-m-d H:i:s', $date); //"1970-01-01 09:00:00" |
1 2 | $date = strtotime('0000-00-00 00:00:00'); date('Y-m-d H:i:s', $date); //"1970-01-01 09:00:00" |
1 2 | $date = strtotime('2039-01-01 00:00:00'); date('Y-m-d H:i:s', $date); //"1970-01-01 09:00:00" |
C’est donc le seul cas où nous avons un décalage dû au fuseau horaire, bien qu’on ait passé un format de date en string.
Marrant, non?
L’explication
En réalité, c’est date qui retourne la date du 1er janvier 1970. strtotime ne retourne pas 0 par défaut, mais bien false, lequel est casté en 0 par date.
1 2 | $date = strtotime(''); var_dump($date); //bool false |
Autrement dit, il est facile de filtrer la réponse de strtotime grâce à une comparaison stricte.
1 2 3 4 | $date = strtotime(''); if($date===false){ echo "la date n'a pas été correctement interprétée"; } |
DateTime propose une méthode assez semblable qui retourne également false en cas de problème: DateTime::createFromFormat.
Conclusion
En situation extrême, strtotime retourne false, qui peut être interprété comme la date du 1er janvier 1970 par date.
Pour DateTime, la réponse est plus plus variable: c’est souvent mieux, sauf pour l’an zéro, où c’est pire…
C’est marrant ca, merci Raph. le 30/11/-1 j’aime assez bien