Відповіді:
Відмінності висвітлюються в документації 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)
timestamp with time zone
а також timestamp without time zone
у Postgres do * насправді не зберігають інформацію про часовий пояс. Ви можете підтвердити це, поглянувши на сторінку doc типу даних: Обидва типи займають однакову кількість октетів і мають діапазон збереження значень, таким чином, немає місця для зберігання інформації про часовий пояс. Текст сторінки це підтверджує. Щось неправильне: "без Тз" означає "ігнорувати зміщення при вставці даних", а "з тз" означає "використовувати зміщення для налаштування на UTC".
Я намагаюся пояснити це більш зрозуміло, ніж згадана документація PostgreSQL.
Жоден з TIMESTAMP
варіантів не зберігає часовий пояс (або зміщення), незважаючи на те, що підказують назви. Різниця полягає в інтерпретації збережених даних (і в передбачуваному додатку), а не у самому форматі зберігання:
TIMESTAMP WITHOUT TIME ZONE
зберігає місцеві дати (час. дата настінного календаря та годинник настінного часу) Його часовий пояс не визначений, наскільки 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
зберігається як a TIMESTAMP WITH TIME ZONE
, який відображається за часовим поясом UTC, і дає такий самий рік, місяць, день, години, хвилини, секунди та мікросекунди, як і в локальній даті. Але це не призначене для відображення точки на часовій лінії, про яку говорить інтерпретація UTC, це лише спосіб закодування місцевих полів дати-часу. (Це деякий скупчення точок на часовій лінії, оскільки реальний часовий пояс не є UTC; ми не знаємо, що це.)
TIMESTAMP WITH TIME ZONE
тому, щоб отримати a як a Instant
. Обидва представляють крапку на часовій шкалі в UTC. Instant
На мою думку, OffsetDateTime
це надається перевагу над тим , що це більш самодокументування: A TIMESTAMP WITH TIME ZONE
завжди виводиться з бази даних як UTC, а an Instant
завжди в UTC, так що це природна відповідність, тоді як аніме OffsetDateTime
може виконувати інші компенсації.
OffsetDateTime
як відображений тип Java. Я не впевнений, чи Instance
все ще десь неофіційно підтримується.
'2011-07-01 06:30:30+00'
і '2011-07-01 06:30:30+05'
ігнорується, але я вмію це робити, insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
і він перетворить його на utc правильно. де дата - мітка часу без часового поясу. Я намагаюся зрозуміти, в чому полягає головне значення часової позначки з часовим поясом і виникають проблеми.
::timestamptz
. З цим ви перетворите рядок у TIMESTAMP WITH TIME ZONE
, і коли це буде надалі перетворено WITHOUT TIME ZONE
, він буде зберігати день "настінного календаря" та час настінного годинника цього моменту, як видно з часового поясу сеансу (який, можливо, є UTC). Це все ще буде лише локальною міткою часу з невстановленим зміщенням (без зони).
Ось приклад, який повинен допомогти. Якщо у вас є часова мітка з часовим поясом, ви можете перетворити її в будь-який інший часовий пояс. Якщо у вас немає базового часового поясу, він не буде перетворений правильно.
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
timestamp
і що timestamptz
означає. timestamptz
означає абсолютний момент часу (UTC), тоді як timestamp
позначає, що годинник показав у певному часовому поясі. Таким чином, переходячи timestamptz
до часового поясу, ви запитуєте, що показував годинник у Нью-Йорку в цей абсолютний момент часу? тоді як при "перетворенні" a timestamp
ви запитуєте, який був абсолютний момент часу, коли годинник у Нью-Йорку показав х?
AT TIME ZONE
Конструкція є мозок тізера самостійно, навіть якщо ви вже розумієте WITH
vs. WITHOUT TIME ZONE
типи. Тож цікавий вибір для їх пояснення. (: ( AT TIME ZONE
перетворює WITH TIME ZONE
часову WITHOUT TIME ZONE
now()::timestamp AT TIME ZONE 'CST'
не має сенсу, якщо тільки ви, що в який момент годинник для зони "CST" показував би час, який показує ваш місцевий годинник,