Aller au contenu
VOZit

Blog · PHP

PHP et MySQL : le décalage d'heure qui fausse vos dates

Un lien d'activation qui expire trop tôt, un quota anti-abus qui se déclenche au mauvais moment, des statistiques décalées de deux heures. Le coupable est souvent le même : PHP et MySQL ne vivent pas dans le même fuseau horaire, et personne ne s'en est aperçu.

D'où vient l'écart

PHP utilise le fuseau défini par date.timezone dans sa configuration, et UTC lorsqu'il n'est pas défini. MySQL et MariaDB, eux, utilisent par défaut le fuseau du système (time_zone = SYSTEM), souvent Europe/Paris sur un serveur français.

Résultat en été : date('Y-m-d H:i:s') côté PHP et NOW() côté base renvoient deux heures différentes à la même seconde. Une heure d'écart en hiver.

Où cela casse

Tant que PHP et la base ne se comparent jamais, tout va bien. Le problème apparaît dès qu'une date calculée en PHP est comparée à une date produite par la base :

// Jeton valable 1 heure : l'expiration est calculée en PHP (UTC)…
$expire = date('Y-m-d H:i:s', time() + 3600);
$pdo->prepare('INSERT INTO jetons (jeton, expire_le) VALUES (?, ?)')
    ->execute([$jeton, $expire]);

// …et vérifiée par MySQL (heure de Paris) : en été, le jeton est
// déjà expiré depuis une heure au moment où on le crée.
SELECT * FROM jetons WHERE jeton = ? AND expire_le > NOW();

DATETIME ou TIMESTAMP : ils ne se comportent pas pareil

DATETIME stocke la valeur telle quelle, sans notion de fuseau. TIMESTAMP la convertit en UTC à l'écriture puis dans le fuseau de la session à la lecture. Deux connexions réglées différemment liront donc deux heures différentes pour la même ligne TIMESTAMP.

À savoir aussi : sous MySQL, et sous MariaDB jusqu'à ses versions 11.4, TIMESTAMP ne va pas au-delà du 19 janvier 2038. Pour une date d'échéance lointaine ou une date de naissance, préférez DATETIME ou DATE.

La correction

1. Fixer le fuseau de PHP, explicitement, au démarrage de l'application :

date_default_timezone_set('Europe/Paris');

2. Aligner la session MySQL sur PHP, à chaque connexion :

$pdo->exec("SET time_zone = '" . date('P') . "'");  // ex. +02:00

Le décalage numérique fonctionne partout. Un nom de fuseau comme 'Europe/Paris' est plus juste lors des changements d'heure, mais il exige que les tables de fuseaux soient chargées dans MySQL (mysql_tzinfo_to_sql), ce qui n'est pas toujours le cas.

3. Mieux encore : faire l'arithmétique de dates d'un seul côté. Si l'expiration est vérifiée par la base, calculez-la aussi dans la base :

INSERT INTO jetons (jeton, expire_le) VALUES (?, NOW() + INTERVAL 1 HOUR);

Une seule horloge, aucune conversion, aucun piège.

Comment vérifier votre serveur

php -r 'echo date_default_timezone_get(), " ", date("H:i"), PHP_EOL;'
mysql -e "SELECT @@global.time_zone, @@session.time_zone, NOW();"

Si les deux heures affichées diffèrent, chaque comparaison entre une date PHP et une date SQL de votre application mérite d'être relue.

Publié le 08/09/2026.

Parlons de votre projet

Un projet, une idée, une application à reprendre ?

Décrivez-le en quelques lignes. Nous revenons vers vous rapidement avec nos questions ou une proposition d'échange. L'étude est gratuite et sans engagement.

  • Étude gratuite, sans engagement
  • Un interlocuteur unique, du devis à la maintenance
  • Développé et hébergé en France
  • Basés près d'Angers, projets partout en France
Demander un devis gratuit