Думайте [об этом] как событие календаря.UTC для этого не имеет смысла
Похоже, вы хотите сохранить местное время относительно определенного часового пояса.В этом случае сохраните timestamp
(без часового пояса) и timezone
в отдельном столбце.
Например, предположим, вы хотите записать событие, которое произойдет в 10 часов утра 26 февраля 2030 года.в Чикаго, и это должно быть в 10:00 по местному времени независимо от правила часового пояса, действующего на эту дату.
Если база данных хранит метку времени без часового пояса:
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 |
+---------------------+-----------------+
Позже вы можете найти дату и время UTC для события, используя
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 |
+---------------------+
Запрос возвращает дату и время UTC, 2030-02-26 16:00:00
, что соответствует 2030-02-26 10:00:00
местному времени в Чикаго.
Использование AT TIME ZONE
задерживает применение правил часового пояса до момента выполнения запроса вместо вставки timestamptz
.
Использование AT TIME ZONE
в timestamp
локализует дату и время в указанном часовом поясе, но сообщает дату и время в часовом поясе пользователя .Использование AT TIME ZONE
для timestamptz
преобразует дату и время в заданный часовой пояс, а затем сбрасывает смещение, возвращая, таким образом, timestamp
.Выше AT TIME ZONE
используется дважды: сначала для локализации timestamp
, а затем для преобразования возвращенного timestamptz
в новый часовой пояс (UTC).Результат - timestamp
в UTC.
Вот пример, демонстрирующий поведение AT TIME ZONE
на timestamp
с:
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
и 2030-02-26 08:00:00-08
являются одинаковыми датами, но сообщаются в разных часовых поясах пользователя.Это показывает, что 10:00 в Чикаго - это 8:00 в Лос-Анджелесе (с использованием текущих определений часовых поясов):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Альтернативой двойному использованию AT TIME ZONE
является установка часового пояса пользователя вUTC
.Тогда вы могли бы использовать
select localtime AT TIME ZONE tzone
Обратите внимание, что когда это сделано таким образом, вместо timestamp
возвращается timestamptz
.
Остерегайтесь, что хранение локальных времен может быть проблематичным, потому чтомогут быть несуществующие времена и неоднозначные времена.Например, 2018-03-11 02:30:00
- это несуществующее местное время в America/Chicago
.Postgresql нормализует несуществующее местное время, предполагая, что оно относится к соответствующему времени после начала перехода на летнее время (DST) (как будто кто-то забыл перевести свои часы вперед):
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)
Примером неоднозначного местного времени является 2018-11-04 01:00:00
в America/Chicago
.Это происходит дважды из-за летнего времени.Postgresql разрешает эту неоднозначность, выбирая более позднее время после окончания летнего времени:
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 |
+---------------------+
Обратите внимание, что это означает, что нет никакого способа сослаться на 2018-11-04 06:00:00 UTC
, сохраняя местное время в часовом поясе America/Chicago
:
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 |
+---------------------+