2013-06-27 186 views
2

我認爲這兩個禍害之一是時間戳和時區(另一個是字符編碼)的神祕面紗,其中一個保持一遍又一遍在磕磕絆絆......PostgreSQL和時間戳

在這方面,我目前在存儲到PostgreSQL數據庫的Java應用程序中存在與不同時間戳有關的問題。

爲了保持簡單起見,假設具有如下表:

CREATE TABLE ts_test 
(
    id integer NOT NULL, 
    utc timestamp without time zone, 
    local timestamp with time zone, 
    CONSTRAINT pk PRIMARY KEY (id) 
) 

所以,我必須存儲UTC時間戳和一個本地的,這在我的情況是歐洲中部夏令時間,所以目前UTC +2。

進一步假設具有表中,psql的控制檯上的輸出如下(數據庫UTC運行)2項:

# select id, utc, local, local-utc as diff from ts_test; 
id |   utc   |   local   | diff 
----+---------------------+------------------------+---------- 
    1 | 2012-06-27 12:00:00 | 2012-06-27 12:00:00+00 | 00:00:00 
    2 | 2012-06-27 12:00:00 | 2012-06-27 14:00:00+00 | 02:00:00 
(2 rows) 

現在,有幾個問題就出來了:

  • 究竟本地列中的輸出是否意味着?
  • 系統如何知道時區,我插入的值?
  • 如何查看存儲的實際原始值(例如毫秒)?

我假設第一行的本地「12:00:00 + 00」意味着它是UTC中的12:00,它在CEST中又是14:00。但看起來(我們的數據庫管理員告訴我的),第二行的本地「14:00:00 + 00」是14:00 CEST的正確值 - 這是2小時的差異支持的。

但產生通過SQL INSERT二號線,我必須寫

insert into ts_test (id, utc, local) values (2, '2012-06-27 12:00:00', '2012-06-27 16:00:00+02'); 

再次不支持的預測。

因此,總結這個長問題 - 任何人都可以啓發我詳細說明整個事情的工作原理,輸出應該是什麼意思,以及如何正確地將本地時間戳寫入數據庫? 。

+0

「+ 00」部分是凍結的時區偏移量。您可能想要捕獲實際的時區名稱。任何觀察夏令時的地區都會在一年中從一個「時區」轉到另一個「時區」。在任何情況下,您的本地時間都存儲爲零偏移量,這意味着UTC,這是不正確的。有關[Postgres日期/時間文檔](http://www.postgresql.org/docs/9.2/static/datatype-datetime.html)中的更多信息。 – tadman

+0

@tadman:謝謝你的提示。我已經考慮過PostgresQL文檔,但還沒有回答我的問題。 - 根據您鏈接的頁面,它表示時間戳記始終以UTC格式存儲,但在特定時區輸出。所以,是問題的一部分,我的數據庫以UTC運行,儘管我在CEST中編寫本地日期?當我將時區參數改爲'歐洲/柏林'時,那麼我得到所有當地人和差異的+2的輸出。這又會支持我對第一線的假設,但我被告知,第二條線是正確的。 *仍然困惑* –

+0

列類型實際上是「帶時區*偏移*的時間戳」,因爲實際時區產生的偏移量未被記錄。在一年的不同時間有很多UTC + 2時區。 – tadman

回答

1

按照local列的輸出,你的SQL會話的時區設置爲UTCGMT,而不是你住的地方的時區想必這是你的意思是:數據庫中UTC運行。這是問題的根源,但我們試着詳細說明。

數據庫本身作爲數據存儲庫沒有時區,但每個SQL會話都有自己的時區。

當他們通過一個SQL會議要求,中timestamp without time zone值是旋轉到會話的時區和提出了一個時間偏移,而對於timestamp with time zone值被旋轉到會話的時區和呈現此時區的時間偏移。這是兩者之間的差異。

時區永遠不會存儲在任何數據類型中,因爲讀取值時,所有重要的是請求此值的SQL會話的時區。

設置你的SQL時區UTC是不是一個好主意,因爲它違背了你的問題的這另一部分:

所以,我必須存儲UTC時間戳和一個本地的,這在我案例 是歐洲中部夏令時,所以當前UTC + 2

讓SQL會話知道您的實時區域,它將開始按預期工作。如果你不這樣做,timestamp with time zone實質上是無用的。

還要注意的是存儲utc timestamp without time zonelocal timestamp with time zone同時沒有意義的,因爲你總是可以得到utc有:

SELECT local AT TIME ZONE 'UTC' FROM ts_test WHERE... 

編輯:問題的答案在註釋:

問:你是說如果我的時區設置爲我的當地時間 會話,那麼我應該看到例如... 14:00:00 + 02在本地爲 utc值... 12:00:00

是的。

問:從我的應用程序 寫什麼東西到本地字段時,它在哪裏設置時區?

沒錯。

問:如何在JDBS會話中設置此項?

我不知道JDBC,但在SQL級別,這將是例如:

SET timezone='Europe/Berlin'; 

通常情況下它會自動從環境設定,但可以在不同層次被迫包括postgresql.conf。在會話中顯式設置它將覆蓋其他任何內容。

問:我可以看到的時間戳的原始值不知何故,以確保顯示

當它 不只是一種表象的問題,我不知道怎麼做,除非pageinspect在較低級別運行。

+0

我確定有人會說,存儲utc和local都沒有意義,但是我完全同意,但我在項目中遇到了這種情況,必須處理它。 (這裏有一個原因在這裏超出了範圍。)關於你對會話時區的評論,你是說如果我的時區設置爲當地時間,那麼我應該看到例如...當地時間14:00:00 + 02爲...的時間值爲12:00:00?當從我的應用程序寫入本地字段時,哪個時區設置在那裏很重要?在JDBS會話中如何設置? –

+0

而且,我能否以某種方式查看時間戳的原始值,以確保它在顯示時不僅僅是表示問題? –

+0

@Geziefer:我已經更新了答案 –