PostgreSQL 可以接受按照
POSIX 标准关于
TZ 环境变量的规则书写的时区规范。
POSIX 时区规范不足以应对真实世界时区历史的
复杂性,但有时会有使用它们的理由。
POSIX 时区规范的形式是
STDoffset[DST[dstoffset] [ ,rule] ]
(为了可读性,我们在字段之间显示了空格,但实践中不应使用空格。) 这些字段是:
STD 是用于标准时间的时区缩写。
offset 是该时区标准时间与 UTC 的
偏移。
DST 是用于夏令时的时区缩写。
如果省略这个字段及其后的字段,该时区使用固定的 UTC 偏移,没有
夏令时规则。
dstoffset 是夏令时与 UTC 的偏移。
这个字段通常被省略,因为它的默认值是标准时间
offset 少一个小时,这通常是正确的。
rule 定义夏令时何时生效的规则,如下文
所述。
在这个语法中,时区缩写可以是一个字母串,例如
EST,或者由尖括号包围的任意字符串,例如
<UTC-05>。
注意,这里给出的时区缩写只用于输出,而且即便如此也只在某些时间戳
输出格式中使用。时间戳输入中可识别的时区缩写按
Section B.4 中解释的方式决定。
偏移字段指定与 UTC 相差的小时数,可选地还有分钟数和秒数。
它们的格式是
hh[:mm[:ss]],
可选地带有前导符号(+
或 -)。
正号用于格林尼治
以西的时区。
(注意,这与
PostgreSQL 其他地方使用的 ISO-8601 符号
约定相反。)
hh
可以是一位或两位数字;mm
和 ss(如果使用)必须是两位。
夏令时转换 rule 的
格式是
dstdate[/dsttime],stddate[/stdtime]
(与前文一样,实践中不应包含空格。)
dstdate
和 dsttime 字段定义夏令时何时开始,
而 stddate
和 stdtime 定义标准时间何时开始。
(在某些情况下,特别是赤道以南的时区,前者在一年中可能比后者更
晚。)日期字段具有以下格式之一:
n一个普通整数表示一年中的一天,从零计数到 364,闰年则为 365。
Jn
在这种形式中,n 从 1 计数到 365,
即使存在 2 月 29 日也不计入它。
(因此,发生在 2 月 29 日的转换无法用这种方式指定。
不过,2 月之后的日子的编号在闰年和非闰年是相同的,因此对于
固定日期的转换,这种形式通常比普通整数形式更有用。)
Mm.n.d
这种形式指定一个总是发生在同一个月的同一个星期的转换。
m
标识月份,从 1 到 12。
n
指定由 d 标识的星期几的第
n 次出现。
n 是 1 到 4 之间的数字,或者 5 表示
该星期几在本月中的最后一次出现(可能是第四次或第五次)。
d 是
0 到 6 之间的数字,0 表示星期日。
例如,M3.2.0 表示
“三月的第二个星期日”。
M 格式足以描述许多常见的夏令时转换法规。
但请注意,这些变体都无法应对夏令时法规的变化,因此在实践中,
为命名时区存储的历史数据(存放在 IANA 时区数据库中)对于正确解释
过去的时间戳是必要的。
转换规则中的时间字段具有与之前描述的偏移字段相同的格式,只是它们
不能包含符号。
它们定义发生向另一种时间转换时的当前本地时间。
如果省略,它们默认为 02:00:00。
如果给出了夏令时缩写但省略了转换
rule 字段,
回退行为是使用规则 M3.2.0,M11.1.0,它对应于
2020 年的美国做法(即在三月第二个星期日拨快,在十一月第一个星期日
拨回,两次转换都发生在当地通行时间的凌晨 2 点)。
注意,这条规则对 2007 年之前的年份不能给出正确的美国转换
日期。
举个例子,CET-1CEST,M3.5.0,M10.5.0/3 描述了巴黎
当前(截至 2020 年)的计时做法。
这个规范说标准时间的缩写是 CET,比 UTC 早
(东)一个小时;夏令时的
缩写 CEST,隐式地比 UTC 早两个小时;
夏令时从三月的最后一个星期日 CET 凌晨 2 点开始,到十月的最后一个
星期日 CEST 凌晨 3 点结束。
四个时区名 EST5EDT、
CST6CDT、MST7MDT
和 PST8PDT 看起来像是 POSIX 时区
规范。然而,它们实际上被当作命名时区处理,
因为(出于历史原因)IANA 时区数据库中存在以这些名字命名的文件。
这样做的实际影响是:这些时区名会产生有效的美国历史夏令时
转换,而单纯的 POSIX 规范则不会。
需要警惕的是,POSIX 风格的时区规范很容易写错,因为对时区缩写的
合理性没有任何检查。
例如,SET TIMEZONE TO
FOOBAR0 是可行的,会让系统有效地使用一个相当古怪的 UTC
缩写。