2016-02-16 127 views
2

在处理Postgres中的时间戳时,我花了很多时间阅读“最佳实践”,并且得到了许多相互矛盾的答案。当使用TIMESTAMPTZ数据类型时,我认为NOW()NOW() at time zone 'utc'会导致在数据库中插入相同的数据。许多在线评论都表明,内部时区并未实际存储,而是转换为UTC。对TIMESTAMPTZ内部转换感到困惑

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

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 

回答

2

原因是隐式型投timestamptimestamptz

  • now()返回数据类型timestamptz
  • now() AT TIME ZONE 'UTC'返回数据类型timestamp
  • (now() AT TIME ZONE 'UTC')::timestamptz蒙上timestamptimestamptz假设当前时区在这个过程中。这是引入差异的地方。

而这正是发生在你INSERT一个timestamp值成timestamptz列。 Postgres必须假设一些时区。你似乎预计UTC会被假定。虽然更合理的默认值是当前时区设置。如果您在会话中设置UTC,则会获得您期望的行为。

演示:

随着我的时间区 '欧洲/维也纳',这是目前领先UTC的1小时(冬季时间):

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中的文本表示看起来不同,因为它被调整为会话的时区,但值是相同的。

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

基础这里:

+1

绝对精彩的解释和往常一样。对此,我真的非常感激。谢谢。 – Sam