Postgresql 存储时区的合适数据类型是什么?

Postgresql 存储时区的合适数据类型是什么?,postgresql,datetime,timezone,sqldatatypes,Postgresql,Datetime,Timezone,Sqldatatypes,我想简单地使用+hh:mm或-hh:mm格式的字符串。这是否既必要又充分 注意:我不需要存储日期或时间,只需要存储时区。可能是间隔 postgres=# select interval '01:30'; interval ---------- 01:30:00 (1 row) postgres=# select interval '-01:30'; interval ----------- -01:30:00 (1 row) 不幸的是,PostgreSQL不提供时区数据类型,所以

我想简单地使用+hh:mm或-hh:mm格式的字符串。这是否既必要又充分

注意:我不需要存储日期或时间,只需要存储时区。

可能是间隔

postgres=# select interval '01:30'; interval ---------- 01:30:00 (1 row) postgres=# select interval '-01:30'; interval ----------- -01:30:00 (1 row)
不幸的是,PostgreSQL不提供时区数据类型,所以您可能应该使用文本

乍一看,interval似乎是一个合乎逻辑的选项,它适合于某些用途。然而,它没有考虑夏令时,也不考虑在同一UTC偏移的不同区域具有不同的DST规则的事实。p> 从UTC偏移量到时区之间没有1:1的映射

例如,澳大利亚/悉尼新南威尔士州的时区在夏令时为UTC+10东部标准时,或UTC+11东部标准时。是的,这与美国使用的首字母缩略词EST相同;时区首字母缩略词在tzdata数据库中是非唯一的,这就是Pg具有时区缩写设置的原因。更糟糕的是,布里斯班-昆士兰处于几乎相同的长度,并且处于UTC+10东部时间。。。但是没有夏令时,所以在新南威尔士州的DST期间,有时它与新南威尔士州的偏移量为-1

更新:最近澳大利亚采用了A前缀,因此它使用AEST作为其东部各州TZ的首字母缩写,但EST和WST仍然普遍使用

很困惑


如果您只需要存储UTC偏移量,则间隔是合适的。如果要存储时区,请将其存储为文本。目前验证并转换为时区偏移是一件痛苦的事情,但至少它可以处理DST。

+hh:mm和-hh:mm不是时区,它们是UTC偏移。一种很好的格式可以将它们保存为有符号整数,偏移量以分钟为单位。您也可以使用诸如interval之类的工具,但这仅在您希望直接在PostgreSQL中进行日期计算(如在查询中)时才有帮助。通常情况下,虽然您使用另一种语言进行这些计算,但这取决于该语言是否支持interval类型以及是否具有良好的日期/时间库。但是将一个整数转换成某种类似于间隔的类型,比如Pythons-timedelta应该很简单,所以我个人会将它存储为一个整数

时区有名称,虽然时区没有标准名称,但tz或zoneinfo数据库中有一个事实上的标准,即欧洲/巴黎、美洲/纽约或美国/太平洋。这些应该存储为字符串

Windows使用完全不同的名称,例如浪漫时光不问。您可以将它们和字符串一起存储,但我会避免,这些名称在Windows之外不使用,而且这些名称毫无意义。此外,windows的翻译版本倾向于使用这些时区的翻译名称,这使得情况更加糟糕

PDT和EST等缩写不能用作时区名称,因为它们不是唯一的。我想是四个,还是五个?不同的时区都称为CST,所以这是不可用的


简而言之:对于时区,将名称存储为字符串。对于UTC偏移量,以分钟为单位将偏移量存储为有符号整数。

在理想情况下,您可以拥有一组已知时区的外键。您可以通过视图和域完成类似的操作

David E.Wheleer的这篇文章创建了一个域,该域作为时区进行了有效性测试:

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 ) );
有一个已知时区的列表很有用,在这种情况下,您可以省去域,只在一个包含从视图中获取的已知时区名称的表中强制执行约束,从而避免在其他地方公开域:

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)
);

在postgres中,您已经可以将任何时间戳或时间戳强制转换到指定时区或从指定时区强制转换,因此不需要从表中查找值。可以在检查约束中直接使用此表达式,因此也不需要为此创建函数:

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框架在下拉框中提供了时区列表,那么此验证就足够了,然后,您的检查约束只是一个备份。

是否有指向所有时区的标准文本字符串澳大利亚/悉尼的链接?如果您想优化性能+节省空间并降低可移植性/灵活性,tz数据库+枚举很好。是否有指向所有时区的标准文本字符串Australia/Sydney的链接?postgresql不会将时区存储在带有时区的列中,它们只是在选定时区中显示给用户。它们总是存储在utcI中。我不确定你的评论是否适用于我的答案,或者确实适用于这个问题。这是关于s toring a timezone,而不是存储带有时区的时间戳。