Відповіді:
Відмінності висвітлюються в документації 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Конструкція є мозок тізера самостійно, навіть якщо ви вже розумієте WITHvs. WITHOUT TIME ZONEтипи. Тож цікавий вибір для їх пояснення. (: ( AT TIME ZONEперетворює WITH TIME ZONEчасову WITHOUT TIME ZONE
now()::timestamp AT TIME ZONE 'CST'не має сенсу, якщо тільки ви, що в який момент годинник для зони "CST" показував би час, який показує ваш місцевий годинник,