set timezone = 'Asia/Ho_Chi_Minh';
我将显示的时间设置为 GMT+7。
select '2021-02-16 09:00' AT TIME ZONE 'Asia/Singapore';
select '2021-02-16 09:00+06' AT TIME ZONE 'Asia/Singapore';
输出
2021-02-16 10:00:00
2021-02-16 11:00:00
因为AT TIME ZONE
转换timestamp without time zone
为timestamp with time zone
,反之亦然(https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT),并且因为两个输出都是timestamp without time zone
(您可以看到它们没有 + - 部分,或pg_typeof
以确认),我推断两个字符串文字都被解释为timestamp with time zone
。
我知道第二个选择有+06
指示时区的部分,这是有意义的。
问题:
为什么没有 + 或 - 时区部分的第一个选择会被解释为像timestamp with time zone
第二个选择一样?
我知道我可以用 TIMESTAMP/TIMESTAMP WITH TIME ZONE 指定类型或用 ::timestamp/::timestamptz 进行转换,但假设我不执行任何操作,是否有任何文档描述字符串文字发生了什么?
这是因为类型偏好。为了使
at time zone
构造工作,您指定的未知文字需要是timestamp
,timeTZ
或者timestampTZ
- 为了打破它们之间的联系并选择一个,Postgres 检查pg_type.typispreferred
:TimestampTZ
是该组中的首选类型:db<>fiddle 上的演示
Postgres 将其重写
at time zone
为pg_catalog.timezone()
函数调用。它已重载,并且可用的变体之一接受timestampTZ
第二个参数。除非您明确为文字指定类型并选择timestamp
,否则 Postgres 必须按照预定义的 偏好为您猜测并选择一个timestampTZ
。这意味着在您的两个示例中,您都以 开头
timestampTZ
,然后被 剥离at time zone
,返回移位的timestamp without time zone
。顺便说一句,它也应该是您喜欢的类型。
'Asia/Singapore'
实际上,右边也存在类似的问题。有一些变体pg_catalog.timezone()
接受一个interval
而不是text
那里,但由于它们不仅是不同类型,而且是不同类别的类型,因此 Postgres 必须打破类别限制:如果可用,则'S'
“字符串类型”是首选,因此text
获胜。这种对字符串的偏向在第 10 章类型转换、10.3 函数、步骤 4.e中进行了解释:中的字符串文字
select '2021-02-16 09:00' AT TIME ZONE 'Asia/Singapore';
(即 部分'2021-02-16 09:00'
)被解释为具有时区,因为您将时区设置为'Asia/Ho_Chi_Minh'
。PostgreSQL 将
'2021-02-16 09:00'
其视为位于2021-02-16 09:00
中Asia/Ho_Chi_Minh time zone (UTC+7)
。因此,所有时间都与胡志明市有关。正如您所提到的,您需要使用
SELECT '2021-02-16 09:00:00'::timestamp AT TIME ZONE 'Asia/Singapore';
,并且它将首先被视为没有时区的时间戳。如果您从未将时区设置为胡志明市,PostgreSQL 将使用默认时区(在我的情况下为美国/纽约)。