2013-11-28 64 views
13

對於ISO8601兼容的日期時間保留時區中的PostgreSQL timestamptz型

2004-10-19 10:23:54+02 

是否有可能具有的值與偏移+02,反映在存儲的列值,並且選擇當它還保存?

從我閱讀的appropriate section of the docs Postgres的默認行爲是轉換爲UTC,在此時原始偏移量丟失。這當然是我所看到的。

數據通過ORM訪問,無法添加任何特殊的tz轉換,所以我確實需要將日期時間與原始偏移量一起存儲,並在選擇時反映該值。

對於任何想要告訴我這是及時的相同實例的人來說,保存這個值對這些數據有重要意義。

+0

是否可以將偏移量存儲在一個單獨的列中,以便您不受Postgres的擺佈? – tadman

+1

@tadman Ha。試圖將其轉換爲一列。似乎並不像一件不合理的事情。 – markdsievers

+0

什麼是數據源?一個字符串文字?還是另一列 - 什麼類型的? –

回答

13

正如你已經想通了自己的時區沒有在所有Postgres的日期/時間類型,甚至不timestamptz保存。它的角色分別只是一個輸入修飾符或輸出修飾符。只有值(時間點)被保存。在這個相關答案充足的細節:

因此,如果您想保留的輸入字符串的一部分,你必須從字符串中提取並保存自己吧。我會用一個表,如:

CREATE TABLE tstz 
... 
, ts timestamp -- without time zone 
, tz text 
) 

tz,是text,可以容納一個數字偏移以及時區縮寫,或時區

難點在於根據解析器遵循的所有各種規則提取時區部分,並且不會輕易破壞。 不用做你自己的程序,而是讓解析器完成工作。考慮這個演示:

WITH ts_literals (tstz) AS (
    VALUES ('2013-11-28 23:09:11.761166+03'::text) 
     ,('2013-11-28 23:09:11.761166 CET') 
     ,('2013-11-28 23:09:11.761166 America/New_York') 
    ) 
SELECT tstz 
     ,tstz::timestamp AS ts 
     ,right(tstz, -1 * length(tstz::timestamp::text)) AS tz 
FROM ts_literals;

SQL Fiddle.

作品帶或不帶日期和時間之間的T。關鍵邏輯如下:

right(tstz, -1 * length(tstz::timestamp::text)) AS tz 

在修剪解析器標識爲日期/時間分量的長度後,取出時間戳字符串的剩餘部分。這依賴於輸入是,當你說:

驗證ISO8601串

+2

感謝您的詳細回覆。如此令人失望,這在一列中是不可能的。 – markdsievers

+3

@markdsievers如果你仔細想想,抵消是多餘的信息。 「真實」時間是UTC/GMT,自紀元以來的毫秒數。如果您的應用程序和數據的環境中您真正關心的是保留偏移量,這意味着您關心當地時間,這意味着您應該捕獲並記錄時區。例如:「太平洋/奧克蘭」。時區超過偏移量,它包括夏令時(DST)和其他異常的規則/歷史記錄。如果沒有記錄時區,則日期時間+偏移量與UTC中的日期時間之間沒有有用的區別。 –

1

原生postgres date/time datatypes不會爲您保留您的輸入時區。如果您需要將它作爲數據庫中的時間戳來查詢並顯示原始信息,那麼您將不得不以某種方式存儲這兩條信息。

我打算建議你的ORM可以定義自定義膨脹/放氣方法來處理魔法,但顯然它不能。你應該指出你正在使用哪個ORM。

您可以讓ORM存儲/檢索數據庫中的字符串,並使用Postgres中的trigger將其轉換爲存儲在執行數據庫端查詢時使用的另一列中的timestamptz。如果你有很多這種類型的數據表,那可能有點笨拙。

如果您確實需要數據庫中單個列中的數據,您可以在Postgres中定義composite type,儘管您的ORM可能無法處理它們。

+0

故意忽略ORM詳細信息因爲這是另一個後續問題的主題。正如標題所暗示的,這個問題簡單地說明了將TZ存儲在Postgres時間戳類型的一列中的可能性。 – markdsievers

2

Java開發人員可以使用Joda Time聯合Jadira用戶類型PersistentDateTimeAndZone。例如:

@Basic(optional = false) 
@Columns(columns = { @Column(name = "modificationtime"), 
     @Column(name = "modificationtime_zone") }) 
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTimeAndZone") 
@Index(name = "payment_modificationtime_idx") 
private DateTime modificationTime = null; 

在這個例子中,DateTime信息是在2列:

  1. modificationtime timestamp without time zone存儲在UTC時間的時間戳
  2. modificationtime_zone varchar(255)到時區ID存儲爲字符串(例如America/Caracas

儘管Joda時間和Jadira(和Hibernate)特定於Java(並且是事實上方法),上述構建RDBMS列以存儲時間戳和時區的方法可以應用於任何編程語言。