我認爲這是一個設計缺陷,任何日期 - 時間框架將使時區可選。隨意選擇會造成無窮無盡的混亂,因爲程序員和其他人一樣,無意識地按照自己的個人時區思考,除非得到提示。所以在日期時間工作中往往沒有注意到這個問題。添加JVM默認值變化的問題。順便說一句,同樣的問題,Locale
同樣應該總是明確指定。
UTC
你的業務邏輯,數據存儲和數據交換應該總是在UTC完成。幾乎每個數據庫都有一個功能,用於調整UTC中的任何輸入並以UTC存儲。
向用戶呈現日期時間時,請調整到預期的時區。序列化日期時間值時,請使用ISO 8601字符串格式。具體請參見VickyArora的the Answer for Oracle(我是一個Postgres人)。請務必仔細閱讀文檔,並通過試驗來充分理解數據庫的行爲。在這方面,SQL規範並沒有詳細說明,行爲差異很大。
java。sql
請記住,使用Java和JDBC時,您將使用java.sql.Timestamp
和相關數據類型。他們始終使用UTC,自動。未來希望更新JDBC驅動程序以直接使用Java 8及更高版本中構建的java.time框架中定義的新數據類型。
java.time
老班由java.time過時。 學習使用java.time,同時避免使用舊的java.util.Date/.Calendar並使編程生活更愉快。
在更新JDBC driver之前,您可以使用java.time中內置的轉換便利方法。接下來看例子,其中Instant
是UTC的時刻,ZonedDateTime
是瞬時調整爲time zone。
Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant , zoneId);
要走另一個方向。
java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from(zdt.toInstant());
如果您需要原始時區,其存儲
如果您的業務需求考慮原始輸入數據的時區是重要的,要記住,然後商店,明確在你的數據庫中一個單獨的列表。您可以使用offset-from-UTC,但不提供完整的信息。時區是偏移量加過去,現在和將來處理異常的一組規則,如Daylight Saving Time。所以proper time zone name是最合適的,比如America/Montreal
。
日期,只是含糊
你說你收集許多日期僅值,沒有時間的天,沒有時區。 java.time中的類是LocalDate
。與LocalTime
和LocalDateTime
一樣,「本地...」部分意味着沒有特定的位置,所以沒有時區,因此也不是時間軸上的一個點 - 沒有實際意義。
請注意,根據定義,僅限日期的值不明確。在任何特定時刻,日期都會在世界各地變化。例如,在Paris France的午夜之後是新的一天,但在Montréal Québec日期仍然是「昨天」。
通常在商業中某些時區是隱含的,甚至無意識地直覺。對數據點的無意識直覺從長遠來看並不適用,尤其是在軟件方面。更好地明確什麼時區是打算。您可以將預定區域與數據庫表中另一列的日期一起存儲,也可以在編程代碼中進行註釋。我相信它會更好更安全地存儲日期時間值。那麼我們如何將日期轉換爲日期?
通常,新的一天是午夜之後的時刻,即一天中的第一個時刻。您可能認爲這意味着時間爲00:00:00.0
,但並非總是如此。 Daylight Saving Time (DST)和其他可能的異常可能會將第一時刻推到另一個wall-clock time。讓java.time確定正在經歷LocalDate
類及其atStartOfDay
方法的第一個時刻的正確時間。
ZoneId zoneId = ZoneId.of("America/Montreal");
LocalDate today = LocalDate.now(zoneId);
ZonedDateTime todayStart = today.atStartOfDay(zoneId);
在一些業務環境中,新的一天可能被定義(或假設)爲營業時間。例如,如果出版商New York表示他們在當地時間上午9點說「書籍草稿應在1月2日前到期」。讓我們在那個時區獲取該日期的那個時間。
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zdt = ZonedDateTime.of(2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId);
這是什麼意思了,筆者在New Zealand工作?通過調用withZoneSameInstant
調整爲她的particular time zone以供演示。
ZoneId zoneId_Pacific_Auckland = ZoneId.of("Pacific/Auckland");
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant(zoneId_Pacific_Auckland);
數據庫 對於數據庫存儲我們轉變成一個Instant
(一個在UTC的時間軸上的時刻),並通過作爲java.sql.Timestamp
如前面上方看到。
java.sql.Timestamp ts = java.sql.Timestamp.from(zdt.toInstant());
從數據庫中檢索時,轉換回紐約日期時間。將java.sql.Timestamp
轉換爲Instant
,然後應用時區ZoneId
以獲得ZonedDateTime
。
Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant , zoneId);
如果您database driver與JDBC 4.2或更高版本的規定,您可以通過/取java.time類型,而不是直接轉換到/自java.sql類型。嘗試使用PreparedStatement::setObject
和ResultSet::getObject
方法。
如果插入數據的用戶和查詢數據的用戶在不同的時區,您會發現什麼? –
@Gopi你可以重新寫一下倒數第二顆子彈嗎? –