2014-09-26 69 views
2

我正在開發一個Java應用程序,它位於與我的不同時區的服務器上的MySQL數據庫,並且我試圖決定在我的數據庫上使用DATETIME還是TIMESTAMP。Java和MySQL中的時間戳和時區轉換

在閱讀Should I use field 'datetime' or 'timestamp'?MySQL documentation之類的問題之後,我決定TIMESTAMP對我更好,因爲它將值轉換爲UTC存儲,並返回到當前時區進行檢索。

另外,正如用戶Jesper在this線程中解釋的那樣,java.util.Date對象在內部只有一個UTC時間戳(即Epoch以來的毫秒數),而當您執行toString()時,它會根據您的當前時區。

對我來說,這看起來很不錯:將日期時間存儲爲UTC時間戳,然後根據當前時區顯示它們。

我正要那樣做,但後來我發現這個從Java documentation用於預處理語句,並得到了很困惑:

無效的setTimestamp(INT parameterIndex, 時間戳X, 日曆CAL) throws SQLException

使用給定的Calendar對象將指定參數設置爲給定的java.sql.Timestamp值 。驅動程序使用日曆對象 構建SQL TIMESTAMP值,然後驅動程序將其發送到 數據庫。使用Calendar對象時,驅動程序可以計算考慮到自定義時區的 時間戳。如果未指定日曆對象 ,則該驅動程序使用默認時區,該時區是運行應用程序的虛擬機的 。

在此之前,我認爲時間戳通常以UTC爲準。爲什麼人們會想要一個本地化的時間戳而不是本地化的表示的呢?這不會讓每個人都感到困惑嗎?

這些轉換是如何工作的?如果Java採用UTC時間戳並將其轉換爲任意時區,它如何告訴MySQL它在哪個時區?

MySQL不會假定此時間戳記是UTC,然後檢索不正確的本地化值?

回答

5

日期時間處理是一個爛攤子

answer by Teo第一段是非常有見地的和正確的:在Java中的日期時間處理是一個爛攤子。所有其他語言的Ditto &我知道的開發環境。日期時間的工作是困難和棘手的,尤其是容易出錯和令人沮喪的,因爲我們認爲它是直觀的日期時間。但是,當涉及到數據類型,數據庫,序列化,本地化,跨時區調整以及計算機編程隨附的所有其他手續時,「直觀地」不會削減它。

不幸的是,計算機行業基本上選擇了忽略日期時間工作的這個問題。正如統一碼需要很長時間才能被髮明,因爲明顯的需求,業界也一直在解決日期處理問題上下功夫。

不依賴於計數紀元以來

但我必須和它的結論不同意。使用自數計數並不是最好的解決方案。使用count-since-epoch本質上是令人困惑,容易出錯和不兼容的。

我們做數學,而不是使用位創建數字數據類型。我們創建字符串類來處理處理文本的細節,而不是純粹的八位字節。我們也應該創建數據類型和類來處理日期時間值。

早期的Java團隊(和他們之前的IBM & Taligent)嘗試使用java.util.Date和java.util.Calendar及相關類。不幸的是,這種嘗試是不夠的。雖然日期時間本質上是令人困惑的,但這些課程更加混淆了。

喬達時間

據我所知,Joda-Time項目是採取的日期時間徹底,可靠和成功的方式,第一個項目。即便如此,Joda-Time的創造者並不完全滿意。他們繼續在Java 8中創建java.time package,並將其擴展爲threeten-extra project。 Joda-Time和java.time共享相似的概念,但是不同,每個都有一些優點。

數據庫問題

具體來說,java.util.Date & .Calendar類缺乏日期僅值,而時間的日和時區。他們缺乏沒有日期和時區的時間價值。在Java 8之前,Java團隊添加了被稱爲java.sql.Datejava.sql.Time類的攻擊,這些類是僞裝成僅限日期的日期時間值。 Joda-Time和java.time均提供LocalDateLocalTime類。

另一個具體問題是java.util.Date的分辨率爲毫秒,但數據庫通常使用微秒或納秒。爲了消除這種差距,Java早期團隊創建了另一個黑客類別java.sql.Timestamp。雖然技術上是一個java.util.Date子類,但它也可以跟蹤小數秒到納秒分辨率。所以在進入和退出這種類型時,您可能會失去或獲得更精細的秒數粒度,而不會意識到這一事實。所以這可能意味着你期望的平等值不是。

混淆的另一個來源是SQL數據類型TIMESTAMP WITH TIME ZONE。由於時區信息是而不是存儲,該名稱是一個誤稱。將名稱設想爲TIMESTAMP WITH RESPECT FOR TIME ZONE,因爲在將日期時間值轉換爲UTC時,會使用任何傳遞的時區偏移信息。

具有納秒級分辨率的java.time包具有一些特定功能,可以更好地與數據庫進行日期時間數據通信。

我可以寫更多,但是這些信息可以從搜索StackOverflow中搜索諸如joda,java.time,sql timestamp和JDBC等單詞。

使用Joda-Time和JDBC的例子Postgres。 Joda-Time使用immutable objectsthread-safety。因此,我們不是修改一個實例(「mutate」),而是根據原始值創建一個新實例。

String sql = "SELECT now();"; 
… 
java.sql.Timestamp now = myResultSet.getTimestamp(1); 
DateTime dateTimeUtc = new DateTime(now , DateTimeZone.UTC); 
DateTime dateTimeMontréal = dateTimeUtc.withZone(DateTimeZone.forID("America/Montreal")); 

專注於UTC

在此之前,我還以爲在時間戳UTC是按照慣例始終。爲什麼人們會想要一個本地化時間戳而不是本地化表示呢?這不會讓每個人都感到困惑嗎?

確實。 SQL標準定義了一個TIMESTAMP WITHOUT TIME ZONE,它忽略並刪除任何包含的時區數據。我無法想象這有用。這位Postgres專家David E. Wheeler,says as much in recommending總是使用TIMESTAMP WITH TIME ZONE。 Wheeler引用了一個狹義的技術例外(分區),甚至在將數據保存到數據庫之前將所有值轉換爲UTC。

最佳做法是在調整到本地化時區以便呈現給用戶時以UTC來處理和存儲數據。您可能有時想記住其本地時區中的原始日期時間數據;如果是這樣,除了轉換爲UTC之外,還要保存該值

準則

的第一步,以更好的日期時間處理是避免java.util.Date & .Calendar,使用喬達時間和/或java.time,專注於UTC,學習的行爲你特定的JDBC驅動程序和特定的數據庫(儘管SQL標準,數據庫在日期時間處理方面差別很大)。

1

你的問題是現在的問題,我認爲這些天是巨大的。 DB(通過SQL)和服務器端本身(通過Java等編程語言)都提供了處理日期和時間的方法。我認爲現狀是高度不規範和有點混亂(個人意見:)

我的答案是部分,但我會解釋爲什麼。

你是對的,Java的Date(和Calendar)存儲時間爲自Unix Epoch以來的毫秒數(這很好)。它不僅發生在Java中,而且也發生在其他編程語言中。在我看來,完美的計時架構自然而然地誕生了:Unix時代是1970年1月1日,午夜,UTC。因此,如果你選擇的時間存儲爲自Unix紀元毫秒你有很多的好處:

  • 架構清晰:服務器端可與UTC,客戶端顯示通過當地時區
  • 數據庫簡單的時間:你存儲一個數(毫秒),而不是像DateTime是否
  • 編程效率複雜的數據結構:在多數編程語言你有日期/時間對象能夠構造時服用毫秒因爲曆元(其如你所說,允許自動轉換爲客戶端側的時區)

使用此方法時,我發現代碼和體系結構更簡單,更靈活。我停止試圖理解諸如DateTime(或Timestamp)之類的東西,只有在我必須修復遺留代碼時才處理它們。