Разница между временными метками с / без часового пояса в PostgreSQL - PullRequest
149 голосов
/ 04 мая 2011

Значения меток времени хранятся в PostgreSQL по-разному, когда тип данных WITH TIME ZONE против WITHOUT TIME ZONE?Можно ли проиллюстрировать различия с помощью простых тестовых случаев?

Ответы [ 3 ]

126 голосов
/ 04 мая 2011

Различия описаны в документации 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)
17 голосов
/ 03 января 2018

Я пытаюсь объяснить это более понятно, чем упомянутая документация PostgreSQL.

Ни один из вариантов TIMESTAMP не хранит часовой пояс (или смещение), несмотря на то, что предлагают названия. Разница заключается в интерпретации хранимых данных (и в предполагаемом приложении), а не в самом формате хранения:

  • TIMESTAMP WITHOUT TIME ZONE хранит местное дата-время (aka. Дата и время настенного календаря). PostgreSQL может определить его часовой пояс (хотя ваше приложение может знать, что это такое). Следовательно, PostgreSQL не выполняет преобразование, связанное с часовым поясом, при вводе или выводе. Если значение было введено в базу данных как '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; мы не знаем, что это такое.)

9 голосов
/ 04 мая 2011

Вот пример, который должен помочь. Если у вас есть временная метка с часовым поясом, вы можете преобразовать ее в любой другой часовой пояс. Если у вас нет базового часового пояса, он не будет правильно преобразован.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...