Det låter som att du vill lagra en lokal tid med avseende på en viss tidszon. Lagra i så fall en timestamp
(utan tidszon) och timezone
i en separat kolumn.
Anta till exempel att du vill spela in en händelse som inträffar kl. 10.00 den 26 februari 2030 i Chicago och att den måste vara kl. 10.00 lokal tid oavsett vilken tidszonsregel som gäller det datumet.
Om databasen lagrar tidsstämpeln utan tidszon:
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 |
+---------------------+-----------------+
Senare kan du hitta UTC-datumtid för händelsen med
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 |
+---------------------+
Frågan returnerar UTC datetime, 2030-02-26 16:00:00
, vilket motsvarar 2030-02-26 10:00:00
lokaltid i Chicago.
Använder AT TIME ZONE
försenar tillämpningen av tidszonsreglerna till när frågan görs istället för när timestamptz
infogades.
Använder AT TIME ZONE
på en timestamp
lokaliserar datum och tid till den givna tidszonen, men rapporterar datum och tid i användarens tidszon .Använder AT TIME ZONE
på en timestamptz
konverterar datetime till den givna tidszonen, tappar sedan offseten och returnerar därmed en timestamp
.Ovan, AT TIME ZONE
används två gånger:först för att lokalisera en timestamp
och nästa för att konvertera den returnerade timestamptz
till en ny tidszon (UTC). Resultatet är en timestamp
i UTC.
Här är ett exempel som visar AT TIME ZONE
s beteende på 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
och 2030-02-26 08:00:00-08
är samma datum och tider men rapporteras i olika användartidszoner. Detta visar att 10:00 i Chicago är 8:00 i Los Angeles (med nuvarande tidszonsdefinitioner):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Ett alternativ till att använda AT TIME ZONE
två gånger är att ställa in användarens tidszon
till UTC
. Då kan du använda
select localtime AT TIME ZONE tzone
Observera att när du gör det på detta sätt, en timestamptz
returneras istället för en timestamp
.
Tänk på att det kan vara problematiskt att lagra lokala tider eftersom det kan finnas obefintliga tider och oklara tider. Till exempel 2018-03-11 02:30:00
är en obefintlig lokaltid i America/Chicago
. Postgresql normaliserar obefintliga lokala tider genom att anta att det refererar till motsvarande tid efter att sommartid (DST) har börjat (som om någon glömt att ställa klockan framåt):
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)
Ett exempel på en tvetydig lokaltid är 2018-11-04 01:00:00
i America/Chicago
. Det inträffar två gånger på grund av sommartid. Postgresql löser denna tvetydighet genom att välja den senare tidpunkten, efter sommartid:
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 |
+---------------------+
Observera att detta betyder att det inte finns något sätt att referera till 2018-11-04 06:00:00 UTC
genom att lagra lokala tider i America/Chicago
tidszon:
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 |
+---------------------+