Каков подходящий тип данных для хранения часового пояса?
Я думаю просто использовать строку в формате "+ hh: mm" (или "-hh: mm" ). Является ли это необходимым и достаточным?
Примечание. Мне не нужно сохранять дату или время, просто часовой пояс.
Ответы
Ответ 1
К сожалению, PostgreSQL не предлагает тип данных часового пояса, поэтому вы, вероятно, должны использовать text
.
interval
на первый взгляд кажется логичным вариантом, и он подходит для некоторых целей. Однако он не учитывает летнее время и не учитывает тот факт, что разные регионы в одном и том же UTC-смещении имеют разные правила DST.
Нет отображения 1:1 из UTC offset обратно в часовой пояс.
Например, часовой пояс для Australia/Sydney
(Новый Южный Уэльс) составляет UTC+10
(EST
), или UTC+11
(EDT
) во время летнего времени. Да, тот же аббревиатур EST
, который использует США; аббревиатуры часовых поясов не являются уникальными в базе данных tzdata, поэтому Pg имеет параметр timezone_abbreviations
. Хуже того, Брисбен (Квинсленд) имеет почти ту же самую долготу и находится в UTC+10 EST
... но не имеет летнего сбережения, поэтому когда-то он при смещении -1
к Новому Южному Уэльсу во время НОУ в Южном Уэльсе.
(Обновление: совсем недавно Австралия приняла префикс A
, поэтому использует AEST
в качестве своего аббревиатура TZ в восточных штатах, но EST
и WST
остаются общепринятыми).
Смущает много?
Если все, что вам нужно сохранить, это UTC-смещение, тогда подходит interval
. Если вы хотите сохранить часовой пояс, сохраните его как text
. Это боль, чтобы подтвердить и преобразовать в смещение часового пояса на данный момент, но по крайней мере он справляется с DST.
Ответ 2
"+ hh: mm" и "-hh: mm" не являются часовыми поясами, они являются смещениями UTC. Хороший формат для сохранения - это целое число со знаком со смещением в минутах. Вы также можете использовать такие вещи, как interval
, но это поможет вам только в том случае, если вы хотите выполнять вычисления даты непосредственно в PostgreSQL, например, в запросе и т.д. Обычно, хотя вы делаете эти вычисления на другом языке, а затем это зависит от этого языка если он поддерживает тип interval
и имеет хорошую библиотеку даты и времени или нет. Но преобразование целого числа в некий тип interval
-like, такой как Pythons timedelta
, должно быть тривиальным, поэтому я бы просто сохранил его как целое число.
В часовых поясах есть имена, и, хотя нет стандартизованных имен для часовых поясов, в базе данных "tz" или "zoneinfo" есть один де-факто, и такие имена, как "Европа/Париж", "Америка/Нью-Йорк" "или" США/Тихоокеанский регион ". Они должны храниться как строки.
Windows использует совершенно разные имена, такие как "Romance time" (не спрашивайте). Вы можете хранить их, а также строки, но я бы избегал этого, эти имена не используются вне Windows, и имена не имеют смысла. Кроме того, переведенные версии окон имеют тенденцию использовать переведенные имена для этих часовых поясов, что делает его еще хуже.
Аббревиатуры типа "PDT" и "EST" не могут использоваться в качестве имен часовых поясов, поскольку они не уникальны. Есть четыре (я думаю, или это было пять?) Разных часовых поясов, все называемые "CST", так что они не пригодны для использования.
Короче: для часовых поясов сохраните имя в виде строки. Для смещений UTC сохраните смещение в минутах как целое число со знаком.
Ответ 3
В идеальном мире вы можете иметь внешний ключ для набора известных часовых поясов. Вы можете сделать что-то близкое к этому с представлениями и доменами.
Этот вики-совет Дэвида Э. Уилера создает домен, который проверяется на валидность в качестве часового пояса:
CREATE OR REPLACE FUNCTION is_timezone( tz TEXT ) RETURNS BOOLEAN as $$
BEGIN
PERFORM now() AT TIME ZONE tz;
RETURN TRUE;
EXCEPTION WHEN invalid_parameter_value THEN
RETURN FALSE;
END;
$$ language plpgsql STABLE;
CREATE DOMAIN timezone AS CITEXT
CHECK ( is_timezone( value ) );
Полезно иметь список известных часовых поясов, в этом случае вы можете обойтись без домена и просто применить ограничение в одной таблице, содержащей известные имена часовых pg_timezone_names
(полученной из представления pg_timezone_names
), избегая необходимости выставлять домен в другом месте:
CREATE TABLE tzone
(
tzone_name text PRIMARY KEY (tzone_name) CHECK (is_timezone(tzone_name))
);
INSERT INTO tzone (tzone_name)
SELECT name FROM pg_timezone_names;
Тогда вы можете применить корректность с помощью внешних ключей:
CREATE TABLE myTable (
...
tzone TEXT REFERENCES tzone(tzone_name)
);
Ответ 4
возможно интервал
postgres=# select interval '01:30';
interval
----------
01:30:00
(1 row)
postgres=# select interval '-01:30';
interval
-----------
-01:30:00
(1 row)
Ответ 5
В postgres вы уже можете привести любой TIMESTAMP
или TIMESTAMPTZ
к или из названного часового пояса, поэтому вам не нужно искать значения из таблицы. Вы можете использовать это выражение непосредственно в проверочном ограничении, поэтому вам не нужно создавать функцию для этого:
CREATE TABLE locations (
location_id SERIAL PRIMARY KEY,
name TEXT,
timezone TEXT NOT NULL CHECK (now() AT TIME ZONE timezone IS NOT NULL)
);
Если вы попытаетесь вставить значение, которое не содержит действительный часовой пояс, вы получите ошибку, которая на самом деле довольно удобна для пользователя:
INSERT INTO locations (name, timezone) VALUES ('foo', 'Adelaide/Australia');
ERROR: time zone "Adelaide/Australia" not recognized
В зависимости от ваших требований, вам может потребоваться, чтобы ошибка была в том формате, который предоставит вам обычное нарушение ограничения, однако во многих случаях этого будет достаточно.
Если вы используете веб-фреймворк, который предоставляет вам список часовых поясов, которые вы можете иметь в раскрывающемся списке, тогда этой проверки должно быть достаточно, и тогда ваше контрольное ограничение будет просто резервной копией.