对TIMESTAMPTZ内部转换感到困惑

时间:2016-02-16 13:19:37

标签: postgresql casting timezone timestamp

我花了很多时间阅读最佳做法'当谈到处理Postgres中的时间戳时,并且有很多相互矛盾的答案。使用TIMESTAMPTZ数据类型时,我假设NOW()NOW() at time zone 'utc'会导致在数据库中插入相同的数据。网上的大量评论表明,时区内部并未实际存储,而是转换为UTC。

为什么会这样,当我运行以下内容时,我不会得到重复的结果?

CREATE TABLE testtime(
  mytime TIMESTAMPTZ,
  descr VARCHAR
);

INSERT INTO testtime VALUES
  (NOW(), 'Now at NZST'),
  (NOW() AT TIME ZONE 'utc', 'Now at UTC');
SELECT *
FROM testtime;

结果:

2016-02-17 02:08:30.845071  Now at NZST
2016-02-16 13:08:30.845071  Now at UTC

1 个答案:

答案 0 :(得分:3)

原因是从timestamptimestamptz的隐式类型广告

  • now()返回数据类型timestamptz
  • now() AT TIME ZONE 'UTC'返回数据类型 timestamp
  • (now() AT TIME ZONE 'UTC')::timestamptztimestamp投放到timestamptz假设您当前的时区。这就是引入差异的地方。

当您将INSERT timestamp值添加到timestamptz列时会发生这种情况。 Postgres必须假设 一些 时区。您似乎已经预料到将采用UTC。但是,更合理的默认值是当前时区设置。如果在会话中设置UTC,则会获得预期的行为。

演示:

我的时区'欧洲/维也纳',目前比UTC早一个小时(冬季):

SET timezone = 'Europe/Vienna';
SELECT now() AS now1
     , now() AT TIME ZONE 'UTC' AS now2
     , (now() AT TIME ZONE 'UTC')::timestamptz AS now3;
              now1             |          now2              |           now3
-------------------------------+----------------------------+-------------------------------
 2016-02-16 14:30:07.243082+01 | 2016-02-16 13:30:07.243082 | 2016-02-16 13:30:07.243082+01

与' UTC'相同作为会话的时区设置:

SET timezone = 'UTC';
SELECT now() AS now1
     , now() AT TIME ZONE 'UTC' AS now2
     , (now() AT TIME ZONE 'UTC')::timestamptz AS now3;
              now1             |          now2              |           now3
-------------------------------+----------------------------+-------------------------------
 2016-02-16 13:30:58.739772+00 | 2016-02-16 13:30:58.739772 | 2016-02-16 13:30:58.739772+00

注意前两列如何具有相同的值 - 即使now1中的文本表示看起来不同,因为它被调整到会话的时区,值是相同。

第三列有一个不同的值,因为假定了类型转换的不同时区。

基础知识: