Stocker des dates avec leur timezone, avec Doctrine

written by Romaric 21 mars 2019

Chez netinfluence nous réalisons une application déployée sur les caisses self-service des boutiques Nespresso aux quatre coins de la planète. Cette application remonte des évènements jusqu’aux sièges en Europe, et tantôt il est utile de connaître l’heure Suisse de l’évènement, tantôt l’heure locale dans la boutique. Il faut donc stocker ces dates avec un maximum d’information.
Doctrine propose un type datetimetz, mais celui-ci n’est pas supporté par MySQL (indiqué dans une page de la documentation pas évidente à trouver). Il faut trouver une autre solution.

L’approche serait de stocker un DateTime d’un côté, le timezone de l’autre. Avec Doctrine il est possible de faire un type personnalisé, mais un type n’est appliqué que sur une seule colonne de la base. Il faut donc trouver un autre moyen.
La première idée et la plus simple est d’utiliser les setters/getter de notre entité pour lire ou écrire simultanément un $datetime et une colonne $timezone:

On voit qu’il y a un problème: lorsque Doctrine remplit le champ Event::$datetime avec la valeur récupérée depuis la BDD, il utilise le timezone de PHP (du serveur, de php.ini). Et ce n’est pas quelque chose de facilement configurable (aucune extension possible dans le code de Doctrine…). On se retrouve donc avec une valeur « fausse » dans Event::$datetime, avec un mauvais décalage, à laquelle il ne faudra surtout pas accéder directement.
Une solution plus élégante est de créer un type Doctrine, qui va se charger de la conversion en UTC, on aura donc une valeur de Event::$datetime toujours consistante, ce qui rend notre code un petit plus robuste:

J’espère que cet article vous permettra d’éviter de vous faire piéger!

PS: je ne saurais trop vous recommander de toujours utiliser que des DateTimeImmutable, il est très facile de perdre le fil de ce que l’on fait avec des DateTime classiques.

You may also like

3 comments

Jacques 22 août 2019 at 11 h 35 min

Bonjour Romaric,

Merci beaucoup pour cet article qui répond tout à fait à mon besoin.
J’utilise Sonata Admin pour administrer le contenu de mes tables et notamment pour extraire des données en les filtrant par une date/heure de début et de fin. N’étant pas encore expert, comment puis-je faire en sorte que mon filtre prenne en compte la timezone stockée dans la table car actuellement il recherche uniquement les enregistrements correspondant à l’intervalle en UTC ?
Merci d’avance pour ton aide.

Reply
Paul 20 janvier 2020 at 13 h 06 min Reply

Leave a Comment

*