让我解释一下这两个例子:
在这两种情况下,我们都假设时区为 UTC(即SET timezone TO UTC)。
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
timezone
---------------------
2015-12-31 16:00:00
(1 row)
这等效于SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz),即Postgres 将字符串隐式转换为timestamptz。
我们知道timezone 函数在timestamp 和timestamptz 之间来回转换:
由于我们给它一个timestamptz 作为输入,它会输出一个timestamp。换句话说,它将绝对时间点2016-01-01 00:00Z 转换为US/Pacific 中的挂壁时间,即洛杉矶的时钟在该绝对时间点显示的内容。
在示例 2 中,我们正在做相反的事情,即采用 timestamp 并将其转换为 timestamptz。换句话说,我们在问:洛杉矶的时钟显示2016-01-01 00:00的绝对时间点是什么?
你提到:
好的,时区函数的'2016-01-01 00:00'::timestamp 部分不再是字符串,而是实际的时间戳。在哪个时区?
'2016-01-01 00:00'::timestamp 是timestamp,即挂墙时间。它没有时区的概念。
我想你可能还没有完全理解timestamp 和timestamptz 之间的区别,这是这里的关键。只需将它们视为墙上时间,即在世界某处挂在墙上的时钟上显示的时间,以及绝对时间,即我们宇宙中的绝对时间.
您在自己的回答中所举的例子并不十分准确。
SELECT ts FROM (VALUES
(timestamptz '2012-03-05 17:00:00+0') -- outputs 2012-03-05 17:00:00+00 --1
,(timestamptz '2012-03-05 18:00:00+1') -- outputs 2012-03-05 17:00:00+00 --2
,(timestamp '2012-03-05 18:00:00+1') -- outputs 2012-03-05 18:00:00+00 --3
,(timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6') -- outputs 2012-03-05 17:00:00+00 --4
,(timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC') -- outputs 2012-03-05 17:00:00+00 --5
,(timestamp '2012-03-05 17:00:00'::timestamp) -- outputs 2012-03-05 17:00:00+00 --6
,(timestamp '2012-03-05 17:00:00'::timestamptz) -- outputs 2012-03-05 17:00:00+00 --7
) t(ts);
您的示例的问题是您正在构建一个包含单列的数据集。由于一列只能有一种类型,因此每一行(或本例中的单个值)都被转换为相同的类型,即timestamptz,即使某些值被计算为timestamp(例如值 3)。因此,您在这里有一个额外的隐式转换。
让我们将示例拆分为单独的查询,看看发生了什么:
示例 1
db=# SELECT timestamptz '2012-03-05 17:00:00+0';
timestamptz
------------------------
2012-03-05 17:00:00+00
您可能已经知道,timestamptz '2012-03-05 17:00:00+0' 和 '2012-03-05 17:00:00+0'::timestamptz 是等价的(我更喜欢后者)。因此,为了使用与文章中相同的语法,我将重写:
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
现在,这里发生了什么?嗯,比你原来的解释要少。该字符串被简单地解析为timestamptz。当打印结果时,它使用当前设置的timezone 配置将其转换回底层数据结构的人类可读表示,即2012-03-05 17:00:00+00。
让我们更改timezone 配置,看看会发生什么:
db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 18:00:00+01
唯一改变的是如何timestamptz 打印在屏幕上,即使用欧洲/柏林时区。
示例 2
db=# SELECT timestamptz '2012-03-05 18:00:00+1';
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
同样,只是解析日期。
示例 3
db=# SELECT timestamp '2012-03-05 18:00:00+1';
timestamp
---------------------
2012-03-05 18:00:00
(1 row)
这与'2012-03-05 18:00:00+1'::timestamp 相同。这里发生的情况是,时区偏移量被简单地忽略了,因为您要求的是 timestamp。
示例 4
db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
让我们重写更简单:
db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
这是在问:时区墙上的时钟显示 2012-03-05 11:00:00 时,偏移量为 +6 小时的绝对时间是多少?
示例 5
db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
让我们重写:
db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
这是在问:UTC 时区墙上的时钟显示2012-03-05 17:00:00 的绝对时间是多少?
示例 6
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
在这里,您将两次投射到timestamp,这没有区别。让我们简化一下:
db=# SELECT '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
我认为这很清楚。
示例 7
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
让我们重写:
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
您首先将字符串解析为timestamp,然后使用当前设置的timezone 将其转换为timestamptz。如果我们更改 timezone,我们会得到其他信息,因为 Postgres 在将 timestamp(或缺少时区信息的字符串)转换为 timestamptz 时假定该时区:
db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+01
(1 row)
这个以 UTC 表示的绝对时间是2012-03-05 16:00:00+00,因此与原始示例不同。
我希望这可以澄清事情。同样,了解timestamp 和timestamptz 之间的区别是关键。想想墙上时间与绝对时间。