在 Postgres 中时区之间转换
Converting Between Timezones in Postgres
我想了解 Postgre 中的时间戳和时区。我想我明白了,直到我红了 this 文章。
关注 "Converting Between Timezones" 部分。它有两个例子。
(考虑默认时区配置为 UTC。)
示例 1
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'); outputs 2015-12-31 16:00:00
根据文章和我的理解,因为 timezone
函数的 '2016-01-01 00:00'
部分只是一个字符串,所以它被默默地转换为默认的 UTC。因此,根据 timezone
函数的要求,它会从 '2016-01-01 00:00' UTC
转换为 US/Pacific
,即 2015-12-31 16:00:00
。
示例 2
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp); outputs 2016-01-01 08:00:00+00
对不起,我不明白为什么,那里的解释也没有帮助。好的,timezone
函数的 '2016-01-01 00:00'::timestamp
部分不再是字符串,而是实际的时间戳。在什么时区?如果是UTC,输出就得和例1一样。所以自动转换成US/Pacific
?那么输出是UTC吗?但为什么?我在我的 timezone
中要求 US/Pacific
而不是 UTC。
请解释 timezone
在获取时间戳并被要求转换时的行为。谢谢。
这是我的理解。请告诉我。
我在 postgresql.conf
中定义的默认时区是 UTC。检查此代码
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);
现在,假装这是 Postgre 说话:
有为输出定义的特殊时区。所以我将以默认的 UTC 输出所有内容。我们走吧。
1 (timestamptz '2012-03-05 17:00:00+0')
这是时间感知数据,偏移量为0,所以是UTC。默认值也是 UTC。我将按原样保存(无需转换)并输出 2012-03-05 17:00:00+00
因为 UTC 输入到 UTC 保存到 UTC 输出。
2 (timestamptz '2012-03-05 18:00:00+1')
也是时间感知数据,偏移量是+1,所以不是UTC。偏移负 1 以将其转换为 UTC,因此我可以将其保存为 UTC,这是默认设置。输出 2012-03-05 17:00:00+00
因为非 UTC 输入到 UTC 保存到 UTC 输出。
3 (timestamp '2012-03-05 18:00:00+1')
时间无关数据。忽略偏移量,假设这是默认的 UTC 并按原样保存。输出 2012-03-05 18:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出。
4 (timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
再次是时间不敏感的数据。忽略偏移量(如果有)。然后将其转换为给定的 AT TIME ZONE '+6'
偏移量,这样我就可以将其视为完整的时间感知数据。所以我最后的数据是2012-03-05 17:00:00+00
。但这仍然不是时间感知数据。因此,我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出。
5 (timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
和前面的数据一样,时间不敏感的数据。如果有的话,我会忽略偏移量。然后我将它转换为给定的 AT TIME ZONE 'UTC'
,所以没有实际转换,因为没有实际偏移量(UTC 偏移量为 0)。所以我的最终数据是2012-03-05 17:00:00
。但这仍然不是时间感知数据。因此,我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
6 (timestamp '2012-03-05 17:00:00'::timestamp)
这是时间无关数据,再次转换为时间无关数据。因此,与 4 一样,我将忽略任何偏移量(如果有)。也没有 AT TIME ZONE
,所以没有转换。我最后的时间无关数据是 '2012-03-05 17:00:00'
。我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
7 (timestamp '2012-03-05 17:00:00'::timestamptz)
这是时间不敏感的数据,转换为时间敏感的数据。但是没有抵消,转换,什么都没有。所以,这是 UTC。所以,我会按原样保存它。输出 2012-03-05 17:00:00+00
因为 UTC 输入到 UTC 保存到 UTC 输出。
(总的来说希望以上内容对大家有所帮助)
现在!关于文章
示例 1
SELECT timezone('US/Pacific', '2016-01-01 00:00');
时间无关数据,但我可以将其转换为时间感知数据。根据article,由于没有时区信息,所以可以在默认的UTC时区解析。
因此,时间感知的 UTC 数据,按原样保存,但在输出之前将其转换为 US/Pacific
。这就是为什么 article 说 "We get the wall time in California for 2016-01-01 00:00 UTC. " 输出是 2015-12-31 16:00:00
,对于 '2016-01-01 00:00'
UTC 输入,这是加利福尼亚的挂钟时间。
Article 也表示 "Note that we passed the timestamp as a string, which was implicitly cast to a timestamptz"。
这可以写成SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz);
并且仍然输出2015-12-31 16:00:00
。时间感知数据,没有偏移量,所以它的偏移量是 0,所以它是 UTC。 UTC 也是默认设置,因此只需按原样保存即可。在输出之前将其转换为 US/Pacific
。这就是它再次输出 2015-12-31 16:00:00
的原因。
因为“timezone(zone, timestamp)
等价于SQL-符合构造 timestamp AT TIME ZONE zone
”,根据 article,那么
SELECT timezone('US/Pacific', '2016-01-01 00:00');
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz);
timestamptz '2016-01-01 00:00' at time zone 'US/Pacific'
timestamptz '2016-01-01 00:00+00' at time zone 'US/Pacific'
都都一样
Time-aware data (or make it be time-aware),无偏移,保存为UTC,转换后输出,如US/Pacific
.
示例 2
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp);
时间无关数据。我能否将其转换为 UTC 默认值,如示例 1 所示?不,因为转换为时间无关(::timestamp
部分)。我无能为力。它是时间感知数据。
如果有偏移量,我会忽略。与上面4不同的是,没有定义偏移量,没有AT TIME ZONE '+ or -X'
。因此,为了获得 UTC,我将根据 US/Pacific
将 '2016-01-01 00:00'
转换回 UTC。从 Pacific 到 UTC 添加 8 小时。我的 UTC 现在是 2016-01-01 08:00:00+00
。按原样保存。输出 2016-01-01 08:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
同样,根据 article“timezone(zone, timestamp)
等同于符合 SQL 的构造 timestamp AT TIME ZONE zone
”,所以
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp);
timestamp '2016-01-01 00:00' at time zone 'US/Pacific'
timestamp '2016-01-01 00:00+00' at time zone 'US/Pacific'
都都一样
时间无关数据,忽略偏移量,转换回UTC,这是UTC,另存为UTC输出为UTC。
谢谢
让我解释一下这两个例子:
我们都假定时区为 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
时的绝对时间点是什么?
你提到:
Ok, the '2016-01-01 00:00'::timestamp
part of the timezone function is no longer a string, but an actual timestamp. In what timezone?
'2016-01-01 00:00'::timestamp
是一个 timestamp
,即墙上时间。它没有时区的概念。
我想你可能还没有完全理解 timestamp
和 timestamptz
之间的区别,这是这里的关键。只需将它们视为 wall time,即挂在墙上的时钟上显示的世界某个地方的时间,以及 absolute time,即我们宇宙中的绝对时间。
您在自己的回答中举的例子不太准确。
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
,即使用Europe/Berlin 时区。
示例 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)
这是在问:偏移+6小时的时区墙上的时钟显示2012-03-05 11:00:00
时的绝对时间是多少?
示例 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
之间的区别是关键。想想挂钟时间与绝对时间。
我想了解 Postgre 中的时间戳和时区。我想我明白了,直到我红了 this 文章。
关注 "Converting Between Timezones" 部分。它有两个例子。
(考虑默认时区配置为 UTC。)
示例 1
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'); outputs 2015-12-31 16:00:00
根据文章和我的理解,因为 timezone
函数的 '2016-01-01 00:00'
部分只是一个字符串,所以它被默默地转换为默认的 UTC。因此,根据 timezone
函数的要求,它会从 '2016-01-01 00:00' UTC
转换为 US/Pacific
,即 2015-12-31 16:00:00
。
示例 2
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp); outputs 2016-01-01 08:00:00+00
对不起,我不明白为什么,那里的解释也没有帮助。好的,timezone
函数的 '2016-01-01 00:00'::timestamp
部分不再是字符串,而是实际的时间戳。在什么时区?如果是UTC,输出就得和例1一样。所以自动转换成US/Pacific
?那么输出是UTC吗?但为什么?我在我的 timezone
中要求 US/Pacific
而不是 UTC。
请解释 timezone
在获取时间戳并被要求转换时的行为。谢谢。
这是我的理解。请告诉我。
我在 postgresql.conf
中定义的默认时区是 UTC。检查此代码
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);
现在,假装这是 Postgre 说话:
有为输出定义的特殊时区。所以我将以默认的 UTC 输出所有内容。我们走吧。
1 (timestamptz '2012-03-05 17:00:00+0')
这是时间感知数据,偏移量为0,所以是UTC。默认值也是 UTC。我将按原样保存(无需转换)并输出 2012-03-05 17:00:00+00
因为 UTC 输入到 UTC 保存到 UTC 输出。
2 (timestamptz '2012-03-05 18:00:00+1')
也是时间感知数据,偏移量是+1,所以不是UTC。偏移负 1 以将其转换为 UTC,因此我可以将其保存为 UTC,这是默认设置。输出 2012-03-05 17:00:00+00
因为非 UTC 输入到 UTC 保存到 UTC 输出。
3 (timestamp '2012-03-05 18:00:00+1')
时间无关数据。忽略偏移量,假设这是默认的 UTC 并按原样保存。输出 2012-03-05 18:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出。
4 (timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
再次是时间不敏感的数据。忽略偏移量(如果有)。然后将其转换为给定的 AT TIME ZONE '+6'
偏移量,这样我就可以将其视为完整的时间感知数据。所以我最后的数据是2012-03-05 17:00:00+00
。但这仍然不是时间感知数据。因此,我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出。
5 (timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
和前面的数据一样,时间不敏感的数据。如果有的话,我会忽略偏移量。然后我将它转换为给定的 AT TIME ZONE 'UTC'
,所以没有实际转换,因为没有实际偏移量(UTC 偏移量为 0)。所以我的最终数据是2012-03-05 17:00:00
。但这仍然不是时间感知数据。因此,我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
6 (timestamp '2012-03-05 17:00:00'::timestamp)
这是时间无关数据,再次转换为时间无关数据。因此,与 4 一样,我将忽略任何偏移量(如果有)。也没有 AT TIME ZONE
,所以没有转换。我最后的时间无关数据是 '2012-03-05 17:00:00'
。我假设这是我的默认 UTC 并按原样保存。输出 2012-03-05 17:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
7 (timestamp '2012-03-05 17:00:00'::timestamptz)
这是时间不敏感的数据,转换为时间敏感的数据。但是没有抵消,转换,什么都没有。所以,这是 UTC。所以,我会按原样保存它。输出 2012-03-05 17:00:00+00
因为 UTC 输入到 UTC 保存到 UTC 输出。
(总的来说希望以上内容对大家有所帮助)
现在!关于文章
示例 1
SELECT timezone('US/Pacific', '2016-01-01 00:00');
时间无关数据,但我可以将其转换为时间感知数据。根据article,由于没有时区信息,所以可以在默认的UTC时区解析。
因此,时间感知的 UTC 数据,按原样保存,但在输出之前将其转换为 US/Pacific
。这就是为什么 article 说 "We get the wall time in California for 2016-01-01 00:00 UTC. " 输出是 2015-12-31 16:00:00
,对于 '2016-01-01 00:00'
UTC 输入,这是加利福尼亚的挂钟时间。
Article 也表示 "Note that we passed the timestamp as a string, which was implicitly cast to a timestamptz"。
这可以写成SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz);
并且仍然输出2015-12-31 16:00:00
。时间感知数据,没有偏移量,所以它的偏移量是 0,所以它是 UTC。 UTC 也是默认设置,因此只需按原样保存即可。在输出之前将其转换为 US/Pacific
。这就是它再次输出 2015-12-31 16:00:00
的原因。
因为“timezone(zone, timestamp)
等价于SQL-符合构造 timestamp AT TIME ZONE zone
”,根据 article,那么
SELECT timezone('US/Pacific', '2016-01-01 00:00');
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz);
timestamptz '2016-01-01 00:00' at time zone 'US/Pacific'
timestamptz '2016-01-01 00:00+00' at time zone 'US/Pacific'
都都一样
Time-aware data (or make it be time-aware),无偏移,保存为UTC,转换后输出,如US/Pacific
.
示例 2
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp);
时间无关数据。我能否将其转换为 UTC 默认值,如示例 1 所示?不,因为转换为时间无关(::timestamp
部分)。我无能为力。它是时间感知数据。
如果有偏移量,我会忽略。与上面4不同的是,没有定义偏移量,没有AT TIME ZONE '+ or -X'
。因此,为了获得 UTC,我将根据 US/Pacific
将 '2016-01-01 00:00'
转换回 UTC。从 Pacific 到 UTC 添加 8 小时。我的 UTC 现在是 2016-01-01 08:00:00+00
。按原样保存。输出 2016-01-01 08:00:00+00
因为,我不知道我不关心我会假装这是我的默认 UTC 输入到 UTC 保存到 UTC 输出
同样,根据 article“timezone(zone, timestamp)
等同于符合 SQL 的构造 timestamp AT TIME ZONE zone
”,所以
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp);
timestamp '2016-01-01 00:00' at time zone 'US/Pacific'
timestamp '2016-01-01 00:00+00' at time zone 'US/Pacific'
都都一样
时间无关数据,忽略偏移量,转换回UTC,这是UTC,另存为UTC输出为UTC。
谢谢
让我解释一下这两个例子:
我们都假定时区为 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
时的绝对时间点是什么?
你提到:
Ok, the
'2016-01-01 00:00'::timestamp
part of the timezone function is no longer a string, but an actual timestamp. In what timezone?
'2016-01-01 00:00'::timestamp
是一个 timestamp
,即墙上时间。它没有时区的概念。
我想你可能还没有完全理解 timestamp
和 timestamptz
之间的区别,这是这里的关键。只需将它们视为 wall time,即挂在墙上的时钟上显示的世界某个地方的时间,以及 absolute time,即我们宇宙中的绝对时间。
您在自己的回答中举的例子不太准确。
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
,即使用Europe/Berlin 时区。
示例 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)
这是在问:偏移+6小时的时区墙上的时钟显示2012-03-05 11:00:00
时的绝对时间是多少?
示例 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
之间的区别是关键。想想挂钟时间与绝对时间。