Значения метки времени хранятся по-разному в PostgreSQL, когда тип данных WITH TIME ZONE
по сравнению с WITHOUT TIME ZONE
? Можно ли проиллюстрировать различия с помощью простых тестовых примеров?
Разница между отметками времени с/без часового пояса в PostgreSQL
Ответ 1
Различия описаны в документации PostgreSQL для типов даты/времени. Да, обработка TIME
или TIMESTAMP
различается в зависимости от WITH TIME ZONE
или WITHOUT TIME ZONE
. Это не влияет на то, как хранятся значения; это влияет на то, как они интерпретируются.
Влияние часовых поясов на эти типы данных подробно описано в документации. Разница возникает из того, что система может разумно знать о значении:
-
С часовым поясом как частью значения, значение может быть отображено как локальное время в клиенте.
-
Без часового пояса как части значения очевидным часовым поясом по умолчанию является UTC, поэтому он отображается для этого часового пояса.
Поведение отличается в зависимости как минимум от трех факторов:
- Настройка часового пояса в клиенте.
- Тип данных (т.
WITH TIME ZONE
илиWITHOUT TIME ZONE
) значения. - Указано ли значение в определенном часовом поясе.
Вот примеры, охватывающие комбинации этих факторов:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
Ответ 2
Я пытаюсь объяснить это более понятно, чем упомянутая документация PostgreSQL.
Ни один из вариантов TIMESTAMP
хранит часовой пояс (или смещение), несмотря на то, что предлагают названия. Разница заключается в интерпретации хранимых данных (и в предполагаемом приложении), а не в самом формате хранения:
-
TIMESTAMP WITHOUT TIME ZONE
хранит местную дату-время (также известную как дата настенного календаря и время настенных часов). PostgreSQL может определить его часовой пояс, хотя ваше приложение может знать, что это такое. Следовательно, PostgreSQL не выполняет преобразование, связанное с часовым поясом, при вводе или выводе. Если значение было введено в базу данных как'2011-07-01 06:30:30'
, то не'2011-07-01 06:30:30'
, в каком часовом поясе вы отобразите его позже, оно все равно будет'2011-07-01 06:30:30'
год 2011, месяц 07, день 01, 06 часов, 30 минут и 30 секунд (в каком-то формате). Кроме того, PostgreSQL игнорирует любое смещение или часовой пояс, которые вы указываете во входных данных, поэтому'2011-07-01 06:30:30+00'
и'2011-07-01 06:30:30+05'
совпадают. а просто'2011-07-01 06:30:30'
. Для разработчиков Java: это аналогjava.time.LocalDateTime
. -
TIMESTAMP WITH TIME ZONE
сохраняет точку на временной шкале UTC. Как это выглядит (сколько часов, минут и т.д.) Зависит от вашего часового пояса, но оно всегда относится к одному и тому же "физическому" моменту (например, моменту реального физического события). Ввод внутренне преобразован в UTC, и то, как он хранится. Для этого необходимо знать смещение входных данных, поэтому, когда вход не содержит явного смещения или часового пояса (например,'2011-07-01 06:30:30'
), предполагается, что он находится в текущем часовом поясе PostgreSQL. сеанс, в противном случае используется явно указанное смещение или часовой пояс (как в'2011-07-01 06:30:30+05'
). Вывод отображается в преобразованном в текущий часовой пояс сеанса PostgreSQL. Для разработчиков на Java: он аналогиченjava.time.Instant
(хотя и с более низким разрешением), но с JDBC и JPA 2.2 вы должны отобразить его наjava.time.OffsetDateTime
(или наjava.util.Date
илиjava.sql.Timestamp
конечно).
Некоторые говорят, что оба варианта TIMESTAMP
хранят дату и время в формате UTC. Вроде, но, по моему мнению, это сбивает с толку. TIMESTAMP WITHOUT TIME ZONE
хранится как TIMESTAMP WITH TIME ZONE
, которая отображается с часовым поясом UTC и дает тот же год, месяц, день, часы, минуты, секунды и микросекунды, что и в местной дате-времени. Но это не означало представлять точку на временной шкале, о которой говорит интерпретация UTC, это просто способ кодирования локальных полей даты и времени. (Это некоторый кластер точек на временной шкале, поскольку реальный часовой пояс не является UTC; мы не знаем, что это такое.)
Ответ 3
Вот пример, который должен помочь. Если у вас есть временная метка с часовым поясом, вы можете преобразовать ее в любой другой часовой пояс. Если у вас нет базового часового пояса, он не будет правильно преобразован.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Выход:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
Ответ 4
SELECT MAX('2017-07-06 12:20:48.446+00') - MIN('2017-06-06 12:20:48.446+00')
as time_to_take from table_name