Який тип часової позначки слід вибрати в базі даних PostgreSQL?


119

Я хотів би визначити найкращу практику зберігання часових позначок у моїй базі даних Postgres в контексті проекту з багаточасовим поясом.

я можу

  1. виберіть TIMESTAMP WITHOUT TIME ZONEі запам’ятайте, який часовий пояс використовувався під час вставки для цього поля
  2. виберіть TIMESTAMP WITHOUT TIME ZONEі додайте інше поле, яке буде містити назву часового поясу, яке було використано під час вставки
  3. виберіть TIMESTAMP WITH TIME ZONEі вставте часові позначки відповідно

Я маю перевагу перед варіантом 3 (часова мітка з часовим поясом), але хотіла б мати про це освічений висновок.

Відповіді:


142

По-перше, обробка часу та арифметика PostgreSQL є фантастичною, а варіант 3 - загалом. Однак це неповний вигляд часу та часових поясів і може бути доповнений:

  1. Збережіть назву часового поясу користувача як перевагу користувача (наприклад America/Los_Angeles, ні -0700).
  2. Дані користувачів про події / час подаються локально до своєї системи відліку (швидше за все, зміщення від UTC, наприклад -0700).
  3. У додатку конвертуйте час у UTCта зберігається за допомогою TIMESTAMP WITH TIME ZONEстовпця.
  4. Запити часу повернення локально у часовий пояс користувача (тобто перетворення з UTCдо America/Los_Angeles).
  5. Встановіть бази даних timezoneв UTC.

Цей параметр не завжди працює, оскільки отримати часовий пояс користувача важко, і, отже, поради щодо хеджування TIMESTAMP WITH TIME ZONEдля легких програм. Зважаючи на це, дозвольте мені пояснити деякі основні аспекти цього Варіанту 4 більш детально.

Як і варіант 3, причина цього WITH TIME ZONEполягає в тому, що час, коли щось сталося, - це абсолютний момент у часі. WITHOUT TIME ZONEдає відносний часовий пояс. Ніколи, ніколи, ніколи не змішуйте абсолютні та відносні TIMESTAMP.

З точки зору програмності та послідовності, переконайтеся, що всі обчислення зроблено за допомогою UTC як часового поясу. Це не вимога PostgreSQL, але вона допомагає при інтеграції з іншими мовами програмування або середовищами. Встановлення CHECKна стовпчик, щоб переконатися, що запис у стовпці часової марки має зміщення часового поясу, 0є захисним положенням, яке запобігає декільком класам помилок (наприклад, сценарій скидає дані у файл, а щось інше сортує дані про час за допомогою лексичний сорт). Знову ж таки, PostgreSQL не потребує цього для правильного обчислення дат або для перетворення між часовими поясами (тобто PostgreSQL дуже вміло перетворює час між будь-якими двома довільними часовими поясами). Для забезпечення збереження даних, що надходять у базу даних, зі зміщенням нуля:

CREATE TABLE my_tbl (
  my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR:  new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1

Це не на 100% досконало, але він забезпечує достатньо сильний захід проти стоп-сигналів, завдяки якому дані вже перетворені на UTC. Існує маса думок, як це зробити, але це здається найкращим на практиці з мого досвіду.

Критика обробки часового поясу бази даних є значною мірою виправданою (є безліч баз даних, які обробляють це з великою некомпетентністю), однак обробка часових міток та часових поясів PostgreSQL є досить приголомшливою (незважаючи на кілька «особливостей» тут і там). Наприклад, одна така особливість:

-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 15:47:58.138995-07
(1 row)

test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:02.235541
(1 row)

Зауважте, що AT TIME ZONE 'UTC'смужка інформації часового поясу і створює родича, TIMESTAMP WITHOUT TIME ZONEвикористовуючи орієнтир вашої цілі ( UTC).

При перетворенні з неповного TIMESTAMP WITHOUT TIME ZONEв а TIMESTAMP WITH TIME ZONEвідсутній часовий пояс успадковується від вашого з'єднання:

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
        -7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
        -7
(1 row)

-- Now change to UTC    
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 22:48:40.540119+00
(1 row)

-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:49.444446
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
         0
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
         0
(1 row)

Суть:

  • зберігати часовий пояс користувача як названу мітку (наприклад America/Los_Angeles), а не зміщення від UTC (наприклад -0700)
  • використовуйте UTC для всього, якщо немає вагомих причин зберігати ненульовий зсув
  • трактувати всі ненульові моменти UTC як помилку введення
  • ніколи не змішуйте та не порівнюйте відносні та абсолютні часові позначки
  • також використовувати UTCяк timezoneв , якщо це можливо в базі даних

Примітка мови випадкового програмування: datetimeТип даних Python дуже добре підтримує відмінність між абсолютним та відносним часом (хоч і спочатку засмучує, поки ви не доповните його бібліотекою типу PyTZ ).


EDIT

Дозвольте мені трохи більше пояснити різницю між відносними та абсолютними.

Абсолютний час використовується для запису події. Приклади: "Користувач 123 увійшов" або "Церемонії випуску починаються з 2011-05-28 14:00 PST." Незалежно від місцевого часового поясу, якщо ви могли телепортувати місце, де відбулася подія, ви можете стати свідком того, що відбувається. Дані більшості часу в базі даних є абсолютними (і тому в TIMESTAMP WITH TIME ZONEідеалі мають бути зміщення +0 та текстова мітка, що представляє правила, що регулюють певний часовий пояс, а не зміщення).

Відносною подією було б записувати або планувати час чогось з точки зору ще не визначеного часового поясу. Приклади: "двері нашого бізнесу відкриваються о 8 ранку та закриваються о 21:00", "зустрічаємось кожного понеділка о 7 ранку на щотижневій зустрічі зі сніданком" або "кожного Хеллоуїна о 20 вечорі". Взагалі, відносний час використовується в шаблоні чи фабриці для подій, а абсолютний час використовується майже для всього іншого. Є один рідкісний виняток, на який варто звернути увагу, який повинен ілюструвати значення відносного часу. Для майбутніх подій, які є досить далекими в майбутньому, де може бути невизначеність абсолютного часу, в який щось може статися, використовуйте відносну мітку часу. Ось приклад із реального світу:

Припустимо, це 2004 рік, і вам потрібно запланувати доставку на 31 жовтня 2008 року о 13:00 на Західному узбережжі США (тобто America/Los_Angeles/ PST8PDT). Якби ви зберігали це, використовуючи абсолютний час використання ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE, доставка відображалася б о 14:00, оскільки уряд США прийняв Закон про енергетичну політику 2005 року, який змінив правила, що регулюють літній час. У 2004 році, коли було заплановано доставку, дата 10-31-2008мала б тихоокеанський стандартний час ( +8000), але починаючи з 2005 року і в базах даних часового поясу визнано, що 10-31-2008це буде тихоокеанський літній час (+0700). Збереження відносної позначки часу з часовим поясом призвело б до правильного розкладу доставки, оскільки відносна мітка не захищена від непроінформованого порушення Конгресу. Там, де обмеження між відносним та абсолютним часом для планування речей є нечіткою лінією, але моє правило полягає в тому, що для планування в майбутньому нічого, крім 3-6 місяців, слід використовувати відносні часові позначки (запланований = абсолютний проти запланованого = родич ???).

Інший / останній тип відносного часу - це INTERVAL. Приклад: "сеанс очікує через 20 хвилин після входу користувача". А INTERVALможе використовуватися правильно з абсолютними часовими позначками ( TIMESTAMP WITH TIME ZONE) або відносними часовими позначками ( TIMESTAMP WITHOUT TIME ZONE). Не менш правильно сказати, що "сеанс користувача закінчується через 20 хвилин після успішного входу (login_utc + session_duration)" або "наша ранкова зустріч зі сніданком може тривати лише 60 хвилин (recurring_start_time + meeting_length)".

Останні біти плутанини: DATE, TIME, TIME WITHOUT TIME ZONEі TIME WITH TIME ZONEвсе відносні типи даних. Наприклад: '2011-05-28'::DATEвідображає відносну дату, оскільки у вас немає інформації про часовий пояс, яку можна було б використати для ідентифікації опівночі. Аналогічно, '23:23:59'::TIMEце відносно, оскільки ви не знаєте ні часового поясу, ні DATEпредставленого часом. Навіть з '23:59:59-07'::TIME WITH TIME ZONE, ти не знаєш, що DATEб це було. І, нарешті, DATEчасовий пояс насправді не є DATE, це TIMESTAMP WITH TIME ZONE:

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 07:00:00
(1 row)

test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 00:00:00
(1 row)

Внесення дат і часових поясів у бази даних - це гарна річ, але легко отримати тонко невірні результати. Необхідні мінімальні додаткові зусилля для правильного та повного зберігання інформації про час, однак це не означає, що додаткові зусилля завжди потрібні.


2
Якщо ви точно скажете postgresql правильний часовий пояс, у якому знаходиться часова мітка користувача, postgresql зробить важкий підйом за кадром. Перетворити його самостійно - це лише запозичення проблеми.
Сет Робертсон

1
@Sean - з обмеженням вашого чека, як ви коли-небудь вставляєте мітку часу set timezone to 'UTC'? Ви знаєте, що всі дати, відомі часовому поясу, зберігаються всередині UTC ?

2
Суть перевірки полягає в тому, щоб переконатися, що дані зберігаються з нульовим зміщенням від UTC. Сортування та отримання інформації та порівняння часу з ненульовим зміщенням схильне до помилок. Застосовуючи нульове зміщення UTC, ви можете послідовно взаємодіяти з даними з однієї точки зору майже з нульовим ризиком, що поводиться передбачувано у всіх сценаріях. Якби практичні часові позначки підтримували текстові зображення часових поясів, мої думки з цього приводу були б іншими. : ~]
Шон

6
@Sean: Але, як вказує Джек, усі часові позначки, які знають часовий пояс, принципово зберігаються всередині UTC та перетворюються у ваш локальний часовий пояс при використанні; Фактично, витяг (часовий пояс із ...) завжди буде повертати незалежно від локального часового поясу з'єднання: він не має відношення до того, як часова марка була "збережена". Інакше кажучи, часовий пояс взагалі не є частиною типу і не може бути збережений: "з часовим поясом" - це лише властивість того, як будуть конвертуватися дані при взаємодії з іншими типами. Таким чином, дані взагалі не представляють часових поясів, текстових чи інших.
Джей Фріман -саурік-

@ JayFreeman-saurik-: ти абсолютно прав. "CHECK ()" "є протизаконним заходом для захисту від можливого хитрого коду. Забезпечення того, що дані є UTC за записом, надає скромну гарантію того, що код продуманий або середовище виконання налаштовано правильно.
Шон

58

Відповідь Шона надмірно складна і хибна.

Справа в тому, що і "З ТОМОВОЮ ЗОНОЮ", і "БЕЗ ЧАСОВОЇ ЗОНИ" зберігають це значення як уніксаподібну абсолютну часову позначку UTC. Різниця полягає в тому, як відображається часова марка. Якщо "З часовим поясом", то відображене значення - це збережене значення UTC, переведене в зону користувача. Коли "БЕЗ часового поясу" збережене значення UTC скручується так, щоб відображати одну й ту саму сторону годинника незалежно від того, яку зону встановив користувач ".

Єдина ситуація, коли можна використовувати "БЕЗ часового поясу", коли застосовується номінальне значення годинника незалежно від фактичної зони. Наприклад, коли позначка часу вказує, коли кабінки для голосування можуть закритися (тобто вони закриваються о 20:00, незалежно від часового поясу людини).

Використовуйте вибір 3. Завжди використовуйте "З часовим поясом", якщо немає дуже конкретної причини цього не робити.


10
Девід Е. Уілер, головний експерт з постгресу, погодиться з вашою оцінкою відповідно до його публікації: Завжди використовуйте TIMESTAMP AND TIME ZONE .
Василь Бурк

2
Що робити, якщо у вас браузер перетворить метку часу в локальний часовий пояс? Отже, db ніколи не буде робити перетворення і міститиме лише UTC. Чи було б "БЕЗ часового поясу" прийнятним?
дман

5

Я вважаю за краще варіант 3, оскільки Postgres може виконувати інші перерахунки часових міток щодо часового поясу для вас, тоді як з двома іншими вам доведеться це робити самостійно. Додаткові накладні витрати на зберігання часової позначки з часовим поясом дійсно незначні, якщо ви не говорите про мільйони записів, і в цьому випадку ви, мабуть, вже маєте досить м'які вимоги до зберігання.


19
Неправильно. Немає накладних витрат… Postgres не зберігає часовий пояс ("offset" - це правильний термін, а не часовий пояс, до речі). TIMESTAMP WITH TIME ZONEНазва вводить в оману. Це дійсно означає "зверніть увагу на будь-який заданий зсув під час вставки / оновлення та використовуйте це зміщення, щоб налаштувати дату на UTC". В TIMESTAMP WITHOUT TIME ZONEозначає ім'я «ігнорувати будь-зміщення , яке може бути присутнім під час вставки / оновлення, розглянуть частина дати і часу , як в UTC без необхідності регулювання». Уважно прочитайте документ .
Василь Бурк

1
@BasilBourque дякую за цю інформацію. Неймовірно корисна. Для інших, хто читає цей рядок у документі, сказано: "У літералі, визначеному як часова мітка без часового поясу, PostgreSQL мовчки ігнорує будь-які вказівки часового поясу. Тобто, отримане значення отримується з полів дати / часу в вхідне значення, і не коригується для часового поясу ".
Ейдан Россуд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.