Wygląda na to, że chcesz zapisać czas lokalny w odniesieniu do określonej strefy czasowej. W takim przypadku zapisz timestamp
(bez strefy czasowej) i timezone
w osobnej kolumnie.
Załóżmy na przykład, że chcesz nagrać wydarzenie, które będzie miało miejsce o godzinie 10 rano 26 lutego 2030 roku w Chicago i musi mieć miejsce o godzinie 10 rano czasu lokalnego niezależnie od reguły strefy czasowej obowiązującej w tym dniu.
Jeśli baza danych przechowuje znacznik czasu bez strefy czasowej:
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 |
+---------------------+-----------------+
Później możesz znaleźć datę i godzinę wydarzenia UTC za pomocą
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 |
+---------------------+
Zapytanie zwraca datę i godzinę UTC, 2030-02-26 16:00:00
, co odpowiada 2030-02-26 10:00:00
czas lokalny w Chicago.
Korzystanie z AT TIME ZONE
opóźnia zastosowanie reguł strefy czasowej do kiedy zapytanie jest wykonane zamiast kiedy timestamptz
został wstawiony.
Korzystanie z AT TIME ZONE
na timestamp
lokalizuje datę i godzinę na podaną strefę czasową, ale raporty data i godzina w strefie czasowej użytkownika .Korzystanie z AT TIME ZONE
na timestamp
konwertuje datę i godzinę na podaną strefę czasową, a następnie usuwa przesunięcie, zwracając w ten sposób timestamp
.Powyżej, AT TIME ZONE
jest używany dwukrotnie:najpierw do lokalizacji timestamp
a następnie przekonwertować zwrócony timestamp
do nowej strefy czasowej (UTC). Wynikiem jest timestamp
w UTC.
Oto przykład demonstrujący AT TIME ZONE
zachowanie na 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
i 2030-02-26 08:00:00-08
to te same daty i godziny, ale zgłoszone w różnych strefach czasowych użytkownika. To pokazuje, że 10 rano w Chicago jest 8 rano w Los Angeles (przy użyciu aktualnych definicji stref czasowych):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Alternatywa dla używania AT TIME ZONE
dwa razy to ustawienie strefy czasowej użytkownika
do UTC
. Wtedy możesz użyć
select localtime AT TIME ZONE tzone
Zauważ, że gdy zrobisz to w ten sposób, timestamp
jest zwracany zamiast timestamp
.
Pamiętaj, że przechowywanie czasów lokalnych może być problematyczne, ponieważ mogą istnieć nieistniejące czasy i niejednoznaczne czasy. Na przykład 2018-03-11 02:30:00
to nieistniejący czas lokalny w America/Chicago
. Postgresql normalizuje nieistniejący czas lokalny, zakładając, że odnosi się on do odpowiedniego czasu po rozpoczęciu czasu letniego (DST) (jakby ktoś zapomniał przestawić swój zegar do przodu):
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)
Przykładem niejednoznacznego czasu lokalnego jest 2018-11-04 01:00:00
w America/Chicago
. Występuje dwukrotnie z powodu czasu letniego. Postgresql rozwiązuje tę niejednoznaczność, wybierając późniejszy czas, po zakończeniu czasu letniego:
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 |
+---------------------+
Zauważ, że oznacza to, że nie ma możliwości odwołania się do 2018-11-04 06:00:00 UTC
przechowując czasy lokalne w America/Chicago
strefa czasowa:
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 |
+---------------------+