Il semble que vous souhaitiez stocker une heure locale par rapport à un certain fuseau horaire. Dans ce cas, stockez un timestamp
(sans fuseau horaire) et le timezone
dans une colonne séparée.
Par exemple, supposons que vous souhaitiez enregistrer un événement qui se produira à 10 h 00 le 26 février 2030 à Chicago et qu'il doit avoir lieu à 10 h 00 heure locale quelle que soit la règle de fuseau horaire en vigueur à cette date.
Si la base de données stocke l'horodatage sans fuseau horaire :
unutbu=# select '2030-02-26 10:00:00'::timestamp as localtime, 'America/Chicago' AS tzone;
+---------------------+-----------------+
| localtime | tzone |
+---------------------+-----------------+
| 2030-02-26 10:00:00 | America/Chicago |
+---------------------+-----------------+
Puis plus tard, vous pouvez trouver la date et l'heure UTC de l'événement en utilisant
unutbu=# select '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 16:00:00 |
+---------------------+
La requête renvoie la date et l'heure UTC, 2030-02-26 16:00:00
, ce qui correspond à 2030-02-26 10:00:00
heure locale à Chicago.
Utiliser AT TIME ZONE
retarde l'application des règles de fuseau horaire au moment où la requête est faite au lieu du moment où le timestamptz
a été inséré.
Utiliser AT TIME ZONE
sur un timestamp
localise la date et l'heure dans le fuseau horaire donné, mais rapports la date et l'heure dans le fuseau horaire de l'utilisateur .En utilisant AT TIME ZONE
sur un timestamptz
convertit la date/heure dans le fuseau horaire donné, puis supprime le décalage, renvoyant ainsi un timestamp
.Ci-dessus, AT TIME ZONE
est utilisé deux fois :d'abord pour localiser un timestamp
et ensuite pour convertir le timestamptz
renvoyé vers un nouveau fuseau horaire (UTC). Le résultat est un timestamp
en UTC.
Voici un exemple, démontrant AT TIME ZONE
Comportement de sur timestamp
s :
unutbu=# SET timezone = 'America/Chicago';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 10:00:00-06 |
+------------------------+
unutbu=# SET timezone = 'America/Los_Angeles';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 08:00:00-08 |
+------------------------+
2030-02-26 10:00:00-06
et 2030-02-26 08:00:00-08
sont les mêmes datetimes mais signalés dans différents fuseaux horaires d'utilisateur. Cela montre que 10 h 00 à Chicago correspond à 8 h 00 à Los Angeles (selon les définitions de fuseau horaire actuelles) :
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Une alternative à l'utilisation de AT TIME ZONE
deux fois est de définir le fuseau horaire de l'utilisateur
à UTC
. Ensuite, vous pouvez utiliser
select localtime AT TIME ZONE tzone
Notez que lorsque cela est fait de cette façon, un timestamptz
est renvoyé à la place d'un timestamp
.
Attention, le stockage des heures locales peut être problématique car il peut y avoir des heures inexistantes et des heures ambiguës.Par exemple, 2018-03-11 02:30:00
est une heure locale inexistante en America/Chicago
. Postgresql normalise les heures locales inexistantes en supposant qu'elles se réfèrent à l'heure correspondante après le début de l'heure d'été (DST) (comme si quelqu'un avait oublié d'avancer son horloge) :
unutbu=# select '2018-03-11 02:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
unutbu=# select '2018-03-11 03:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
Un exemple d'heure locale ambiguë est 2018-11-04 01:00:00
en America/Chicago
. Il se produit deux fois en raison de l'heure d'été. Postgresql résout cette ambiguïté en choisissant l'heure la plus tardive, après la fin de l'heure d'été :
unutbu=# select '2018-11-04 01:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 07:00:00 |
+---------------------+
Notez que cela signifie qu'il n'y a aucun moyen de faire référence à 2018-11-04 06:00:00 UTC
en stockant les heures locales dans America/Chicago
fuseau horaire :
unutbu=# select '2018-11-04 00:59:59'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 05:59:59 |
+---------------------+