我想简单地使用格式为" + hh:mm" (或" -hh:mm")。这既充实又充足?
注意:我不需要存储日期或时间,只需存储时区。
答案 0 :(得分:24)
不幸的是PostgreSQL没有提供时区数据类型,所以你应该使用text
。
interval
乍一看似乎是一个合乎逻辑的选项, 适合某些用途。但是,它没有考虑夏令时,也没有考虑同一UTC偏移中的不同区域具有不同的DST规则这一事实。
从UTC偏移回到时区没有1:1的映射。
例如,Australia/Sydney
(新南威尔士州)的时区为夏令时UTC+10
(EST
)或UTC+11
(EDT
)时间。是的,这与美国使用的首字母缩写词EST
相同;时区首字母缩略词在tzdata数据库中是非唯一的,这就是Pg具有timezone_abbreviations
设置的原因。更糟糕的是,布里斯班(昆士兰州)几乎处于相同的长度并处于UTC+10 EST
...但是没有夏令时,所以有时它会在-1
偏移到新的南威尔士在新南威尔士州的夏令时期间。
(更新:最近澳大利亚采用了A
前缀,因此它使用AEST
作为东部各州的TZ首字母缩写,但是EST
和{{1保持常用)。
困惑多少?
如果您需要存储的是 UTC偏移量,那么WST
是合适的。如果您要存储时区,请将其存储为interval
。目前验证和转换为时区偏移是一种痛苦,但至少它应对DST。
答案 1 :(得分:13)
“+ hh:mm”和“-hh:mm”不是时区,它们是UTC偏移量。保存它们的一种好格式是带符号的整数,偏移量以分钟为单位。您也可以使用interval
之类的东西,但只有在PostgreSQL中直接进行日期计算时才会有所帮助,比如查询等。通常你用其他语言进行这些计算,然后依赖于该语言是否支持interval
类型并且具有良好的日期/时间库。但是将整数转换为类似interval
类似的类型,如Pythons timedelta
应该是微不足道的,所以我个人只是将它存储为整数。
时区有名称,虽然时区没有标准名称,但“tz”或“zoneinfo”数据库中有一个事实上的标准,其名称如“Europe / Paris”,“Americas / New_York” “或”美国/太平洋“。那些应该存储为字符串。
Windows使用完全不同的名称,例如“浪漫时间”(不要问)。你可以存储它们以及字符串,但我会避免它,这些名称不在Windows之外使用,名称没有意义。此外,Windows的翻译版本倾向于使用这些时区的翻译名称,使其更糟糕。
“PDT”和“EST”等缩写词不能用作时区名称,因为它们不是唯一的。有四个(我认为,或者它是五个?)不同的时区都被称为“CST”,所以这是不可用的。
简而言之:对于时区,请将名称存储为字符串。对于UTC偏移,将偏移量以分钟为单位存储为有符号整数。
答案 2 :(得分:13)
在理想世界中,您可以拥有一组已知时区的外键。您可以通过视图和域来做一些与此相近的事情。
David E. Wheleer的这个wiki tip创建了一个域,该域以其作为时区的有效性进行了测试:
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
获取),避免需要在其他地方公开域名:
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)
);
答案 3 :(得分:0)
也许间隔
postgres=# select interval '01:30'; interval ---------- 01:30:00 (1 row) postgres=# select interval '-01:30'; interval ----------- -01:30:00 (1 row)
答案 4 :(得分:0)
在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
根据您的要求,您可能需要采用普通约束违规将为您提供的格式的错误,但是在许多情况下,就足够了。
如果您使用的Web框架在下拉框中提供了可以提供的时区列表,则此验证就足够了,那么您的检查约束只是一个备份。