簡體   English   中英

使用夏令時分配 java.sql.Timestamp

[英]Assigning java.sql.Timestamp with daylight savings time

我無法理解java.sql.timestamp

如果我運行 java 棄用的構造函數:

java.sql.Timestamp(106,2,26,1,0,0,0)
java.sql.Timestamp(106,2,26,2,0,0,0)
java.sql.Timestamp(106,2,26,3,0,0,0)   //<-- Separated by one hour

我得到:

2006-03-26 01:00:00.0
2006-03-26 03:00:00.0     
2006-03-26 03:00:00.0   //<--These last two are the same

夏令時發生在這些時間(至少在我的國家)。 但是時間前后的日期不會移動。 為什么兩個不同的小時會在同一時間返回?

我想獲得時間戳作為我的輸入,我該如何強制?

不要使用這個已棄用的構造函數,它已被棄用,因為它使用默認時區。

使用具有適當時區 (CET) 的日歷(或 DateFormat),設置日歷的字段(或解析包含要插入的日期的字符串),從日歷/日期中獲取毫秒數,並構建時間戳從毫秒。

使用System.currentTimeMillis(); 為您提供格林威治標准時間,不受夏令時、閏秒和其他意外調整日期的影響。

long now = System.currentTimeMillis();

或手動指定時區:

long ms = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTimeInMillis();

來源: 在 Java 中獲取 GMT 時間

tl;博士

夏令時 (DST) 切換意味着凌晨 2 點從未存在過 所以你提供了無效的輸入。

ZonedDateTime類試圖通過調整您的時間輸入來提供幫助,跳到凌晨 3 點,就像時鍾在敲擊 02:00 時比早上跳了一個小時一樣。

ZonedDateTime
.of( 2006 , 3 , 26 , 2 , 0 , 0 , 0 , ZoneId.of( "Africa/Tunis" ) )
.toString()

2006-03-26T03:00+02:00[非洲/突尼斯]

避免遺留的日期時間類

java.sql.Timestamp是一個糟糕的類,以及它的兄弟類,例如java.util.DateCalendar / GregorianCalendar 它的許多設計問題之一是時區處理混亂。

相反,只使用現代java.time類。

時間

對於時刻(日期、時間和時區或 UTC 偏移量),請使用以下類之一:

  • Instant — 始終以 UTC Instant的時刻。
  • OffsetDateTime — 與 UTC 有偏移量(小時-分鍾-秒數)但時區未知的時刻
  • ZonedDateTime — 具有指定時區的時刻。

聽起來您打算在自己的區域中使用日期時間。

ZoneId

Continent/Region格式指定正確的時區名稱,例如America/MontrealAfrica/CasablancaPacific/Auckland 永遠不要使用ESTIST等 2-4 個字母的縮寫,因為它們不是真正的時區,不是標准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  

如果您想使用 JVM 的當前默認時區,請詢問它並作為參數傳遞。 如果省略,代碼讀起來會變得模棱兩可,因為我們不確定您是否打算使用默認值,或者您是否像許多程序員一樣沒有意識到這個問題。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

ZonedDateTime

指定特定地區(時區)的人們使用的掛鍾時間中所見的時間和日期。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt_1 = ZonedDateTime.of( 2006, 3 , 26 , 1 , 0 , 0 , 0 , z ) ;
ZonedDateTime zdt_2 = ZonedDateTime.of( 2006, 3 , 26 , 2 , 0 , 0 , 0 , z ) ;
ZonedDateTime zdt_3 = ZonedDateTime.of( 2006, 3 , 26 , 3 , 0 , 0 , 0 , z ) ;

System.out.println("zdt_1.toString(): " + zdt_1);
System.out.println("zdt_2.toString(): " + zdt_2);
System.out.println("zdt_3.toString(): " + zdt_3);

zdt_1.toString(): 2006-03-26T01:00+01:00[非洲/突尼斯]

zdt_2.toString(): 2006-03-26T03:00+02:00[非洲/突尼斯]

zdt_3.toString(): 2006-03-26T03:00+02:00[非洲/突尼斯]

夏令時 (DST)

我們在第三個項目上看到了一個驚喜,我們要求凌晨 2 點但在凌晨 3 點得到回復。 這是可以理解的,因為夏令時 (DST)切換或“提前”。 從 2005 年開始, 突尼斯采用了夏令時 2009 年晚些時候,他們恢復了知覺並僅恢復到標准時間。

凌晨 2 點不存在

所以突尼斯當天沒有凌晨 2 點。 當時鍾敲擊凌晨 2 點時,它跳到了凌晨 3 點。 凌晨 2 點從未存在過。 那一天只有 23 小時,而不是通常的 24 小時。 所以我們上面的代碼要求一個無效的日期時間。 ZonedDateTime類,而不是拋出Exception ,試圖通過調整到有效的時間來幫助我們。 ZonedDateTime.ofJavaDoc 說明了這一點:

從年、月、日、小時、分鍾、秒、納秒和時區獲取 ZonedDateTime 的實例。

這會創建一個分區日期時間,該日期時間與七個指定字段的本地日期時間盡可能接近。 時區規則,例如夏令時,意味着並非每個本地日期時間都對指定區域有效,因此本地日期時間可能會被調整。

本地日期時間被解析為時間線上的一個瞬間。 這是通過為區域 ID 的規則定義的本地日期時間查找與 UTC/格林威治的有效偏移量來實現的。

在大多數情況下,本地日期時間只有一個有效偏移量。 在重疊的情況下,當時鍾回退時,有兩個有效的偏移量。 此方法使用通常對應於“夏天”的較早偏移量。

在有間隙的情況下,當時鍾向前跳躍時,沒有有效的偏移量。 取而代之的是,本地日期時間被調整為延遲間隔的長度。 對於典型的一小時夏令時更改,本地日期時間將在一小時后移動到通常對應於“夏季”的偏移量中。

所以行為是一個特性,而不是一個錯誤。


Java(舊版和現代版)和標准 SQL 中的日期時間類型表


關於java.time

java.time框架內置於 Java 8 及更高版本中。 這些類取代麻煩的老傳統日期時間類,如java.util.DateCalendar ,和SimpleDateFormat

現在處於維護模式Joda-Time項目建議遷移到java.time類。

要了解更多信息,請參閱Oracle 教程 並在 Stack Overflow 上搜索許多示例和解釋。 規范是JSR 310

您可以直接與您的數據庫交換java.time對象。 使用符合JDBC 4.2或更高版本的JDBC 驅動程序 不需要字符串,不需要java.sql.*類。

從哪里獲得 java.time 類?

ThreeTen-Extra項目用額外的類擴展了 java.time。 該項目是未來可能添加到 java.time 的試驗場。 您可以在這里找到一些有用的類,比如IntervalYearWeekYearQuarter ,和更多

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM