2016-02-16 180 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