Comment stocker une date en base de données
Stocker une date est l'opération la plus subtile en base de données. Le bon pattern : TIMESTAMPTZ UTC + conversion à l'affichage.
Stocker une date en base de données semble trivial mais cumule les pièges : fuseaux, DST, format, comparaisons. Voici le pattern à jour en 2026 pour PostgreSQL, MySQL et MongoDB.
Ce qu'il ne faut JAMAIS faire
- Stocker en VARCHAR — impossible à comparer, à trier, à indexer correctement.
- Stocker en heure locale serveur — si le serveur déménage de fuseau, tout est faussé.
- Stocker sans fuseau (
TIMESTAMP WITHOUT TIME ZONE) — ambiguïté garantie en équipe distribuée. - Stocker en int sur 32-bit — bug de l'an 2038.
Le bon pattern : TIMESTAMPTZ UTC
- Stocker en type
TIMESTAMP WITH TIME ZONE(PostgreSQL :TIMESTAMPTZ, MySQL ≥ 8 :TIMESTAMP). - Normaliser en UTC à l'insertion.
- Convertir en heure locale au moment de l'affichage, selon le fuseau de l'utilisateur.
PostgreSQL
CREATE TABLE events (
id SERIAL PRIMARY KEY,
occurred TIMESTAMPTZ NOT NULL DEFAULT now(),
user_tz TEXT NOT NULL DEFAULT 'Europe/Paris'
);
-- Insertion (UTC stocké, indépendant du fuseau client)
INSERT INTO events (occurred) VALUES ('2026-02-01T14:30:00Z');
-- Lecture avec conversion en fuseau utilisateur
SELECT occurred AT TIME ZONE 'Asia/Tokyo' FROM events;
MySQL
CREATE TABLE events (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
occurred TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Force la session à UTC pour éviter les surprises
SET time_zone = '+00:00';
INSERT INTO events (occurred) VALUES ('2026-02-01 14:30:00');
-- Lecture en fuseau Tokyo
SET time_zone = 'Asia/Tokyo';
SELECT occurred FROM events;
MongoDB
MongoDB stocke nativement les Date en BSON Date — 64-bit, millisecondes depuis epoch, UTC. Pas de notion de fuseau côté serveur.
db.events.insertOne({
occurred: new Date('2026-02-01T14:30:00Z')
});
Le piège des dates « naïves »
Une « date naïve » est une date sans information de fuseau (ex : 2026-02-01 14:30:00). Sa valeur dépend de l'interprétation : 14h30 à Paris ou à New York ? Toujours stocker des dates aware (avec fuseau), ou normaliser explicitement en UTC.
Indexation et performances
- Un index B-tree sur un
TIMESTAMPTZest très efficace pour les requêtes par plage (WHERE occurred BETWEEN ... AND ...). - Pour les bases time-series massives : envisager TimescaleDB ou ClickHouse, optimisés pour les insertions ordonnées.
- Préférer comparer en UTC : éviter
occurred AT TIME ZONE 'Europe/Paris' > '2026-02-01 14:30'qui empêche l'index de fonctionner.
Stockage de la date de naissance
Une date de naissance n'a pas de fuseau : on utilise le type DATE (PostgreSQL/MySQL) ou un YYYY-MM-DD sans heure. Ne jamais utiliser TIMESTAMPTZ — cela introduit des décalages d'un jour selon le fuseau de lecture.
Récapitulatif
| Donnée | Type recommandé |
|---|---|
| Date d'événement (ponctuel) | TIMESTAMPTZ UTC |
| Date de naissance | DATE |
| Durée | INTERVAL ou int (secondes) |
| Horaire récurrent (08:00 chaque jour) | TIME + fuseau de référence |
| Année seule | SMALLINT |