我正在尝试了解Postgre中的时间戳和时区。我想我得到了它,直到我发表this篇文章。
专注于“时区之间的转换”部分。它有两个例子。
(将默认时区配置视为UTC。)
示例1
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'); outputs 2015-12-31 16:00:00
根据文章和我的理解,因为'2016-01-01 00:00'
函数的timezone
部分只是一个字符串,所以它会以静默方式转换为默认的UTC。因此,'2016-01-01 00:00' UTC
根据US/Pacific
函数的要求将timezone
转换为2015-12-31 16:00:00
,即db=# SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp); outputs 2016-01-01 08:00:00+00
。
示例2
'2016-01-01 00:00'::timestamp
对不起,我不明白为什么,那里的解释没有帮助。好的,timezone
函数的US/Pacific
部分不再是字符串,而是实际的时间戳。在什么时区?如果是UTC,则输出必须与示例1相同。因此它会自动转换为US/Pacific
?然后输出是UTC?但为什么?我在timezone
而不是UTC中要求timezone
。
请解释```{r, results='asis', eval=(opts_knit$get('rmarkdown.pandoc.to') == 'latex')}
cat('\\pagebreak')
```
在获取时间戳并被要求转换时的行为方式。谢谢。
答案 0 :(得分:4)
让我解释两个例子:
我们假设时区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
(例如值3),每一行(在这种情况下为单个值)也会转换为相同的类型,即timestamp
。 )。因此,您在此处有一个额外的隐式转换。
让我们将示例拆分为单独的查询,看看发生了什么:
示例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)
这就是问:时区墙上的时钟偏差为+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
,然后使用当前设置的timestamptz
将其转换为timezone
。如果我们更改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
之间的区别是关键。想想墙上时间与绝对时间。
答案 1 :(得分:1)
这是我的理解。请光临我。
我的默认时区,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
因为,I-dont-know-I-not-care-I-will-pre-this-my-my-default-UTC-input to UTC save to UTC output。
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
因为,I-dont-know-I-not-care-I-will-pre-this-my-my-default-UTC-input to UTC save to UTC output。
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
因为,I-dont-know-I-not-care-I-will-pre-this-my-my-default-UTC-input to UTC save to UTC output
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
因为,I-dont-know-I-not-care-I-will-pre-this-my-my-default-UTC-input to UTC save to UTC output
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');
时间不知情的数据,但我可以将其转换为时间感知。根据{{3}},由于没有时区信息,因此可以在默认的UTC时区中对其进行解析。
因此,时间感知的UTC数据按原样保存,但在输出之前将其转换为US/Pacific
。这就是为什么article说"我们在2016-01-01 00:00 UTC获得加利福尼亚的时间。 "对于2015-12-31 16:00:00
UTC输入,输出为'2016-01-01 00:00'
,即加利福尼亚州的预留时间。
article也说"请注意,我们将时间戳作为字符串传递,该字符串被隐式地转换为时间戳"。
这可以写成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'
全部相同
时间感知数据(或使其具有时间感知),无偏移,将其保存为UTC,输出转换为US/Pacific
。
示例2
SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamp);
时间不知情的数据。我可以将其转换为UTC默认值,如例1中所示吗?不,因为转换为时间不知道(::timestamp
部分)。我无能为力。这是时间感知数据。
如果有的话,我会忽略偏移量。与上面的4不同,没有定义偏移,没有AT TIME ZONE '+ or -X'
。因此,为了获得UTC,我将根据'2016-01-01 00:00'
将US/Pacific
转换回UTC。添加8小时从太平洋到UTC。我的UTC现在是2016-01-01 08:00:00+00
。按原样保存。输出2016-01-01 08:00:00+00
因为,I-dont-know-I-not-care-I-will-pre-this-my-my-default-UTC-input to UTC save to UTC output
同样,根据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输出。
由于