2014-06-05 64 views
1

我們得到了我們的系統中的錯誤:當我們將日期設置爲2041 10月20日,我們將日期設置爲2041
月,19 - 23 HSJDK 6日曆2041年10月20日BUG

Calendar c = Calendar.getInstance(); 
    c.set(2041, Calendar.OCTOBER, 20, 0, 0, 0); 
    System.out.println(c.getTime()); 

輸出:

Sat Oct 19 23:00:00 BRT 2041 

這種行爲發生在我們使用JDK 6,在JDK 7的輸出是正確的:

Sat Oct 20 00:00:00 BRT 2041 

但是我們不能在我們的系統中使用jdk 7。任何人都有更多關於這個bug的信息?

-
更多信息:發生這種情況僅在日期後,2040

+0

出於興趣,它是否只在輸出巴西利亞時間時給出錯誤結果(即,如果以UTC輸出的結果是什麼)?如果它是一個特定於JDK6的bug,並且您不能使用JDK7,但可以使用外部庫,您可能會發現joda時間有效(?) –

+0

如果您必須堅持使用版本6,則應該支付Oracle修復錯誤的一部分支持合同(如果它是一個bug),因爲JDK 6現在不在公衆支持範圍內 – mschenk74

+0

如果您在JDK7中觀察到不同的行爲,那麼原因可能是不同的時區數據存儲庫。從Oracle嘗試[TZ-Updater-Tool](http://www.oracle.com/technetwork/java/javase/tzupdater-readme-136440.html)以更新您的存儲庫。 –

回答

3

時區& DST數據的變化

answer by GriffeyDog和意見建議你是因爲你的計算機和JVM上設置的困惑。該問題可能與Daylight Saving Time (DST)有關,因爲10月20日的時間大約爲DST change for Brazildata for time zones and DST隨Java更新一起更新。您可能會看到巴西的更新有所不同。

喬達時間

測試了這一點,通過使用Joda-Time當前版本(現在2.3)。比使用java.util.Date & .Calendar更容易混淆。 Joda-Time中的日期時間知道它自己分配的時區。 Joda-Time同時適用於Java 6 & 7(和8)。 Joda-Time包含自己的時區和DST數據,而不是使用Java捆綁的數據。

使用UTC

正如其中一條評論所暗示的,明確使用UTC進行比較。

示例代碼在約達時間

DateTimeZone timeZoneSao_Paulo = DateTimeZone.forID("America/Sao_Paulo"); 

DateTime dateTimeSao_Paulo = new DateTime(2041, DateTimeConstants.OCTOBER, 20, 1, 2, 3, timeZoneSao_Paulo).withTimeAtStartOfDay(); 
DateTime dateTimeUtc = dateTimeSao_Paulo.withZone(DateTimeZone.UTC); 

DateTime dateTimeSao_PauloBeforeMidnight = new DateTime(2041, DateTimeConstants.OCTOBER, 19, 23, 50, 0, timeZoneSao_Paulo); 
DateTime dateTimeSao_PauloAfterMidnight = dateTimeSao_PauloBeforeMidnight.plusHours(1); 

轉儲到控制檯。

System.out.println("dateTimeSao_Paulo: " + dateTimeSao_Paulo); 
System.out.println("dateTimeUtc: " + dateTimeUtc); 
System.out.println("dateTimeSao_PauloBeforeMidnight: " + dateTimeSao_PauloBeforeMidnight); 
System.out.println("dateTimeSao_PauloAfterMidnight: " + dateTimeSao_PauloAfterMidnight); 

運行時。

dateTimeSao_Paulo: 2041-10-20T01:00:00.000-02:00 
dateTimeUtc: 2041-10-20T03:00:00.000Z 
dateTimeSao_PauloBeforeMidnight: 2041-10-19T23:50:00.000-03:00 
dateTimeSao_PauloAfterMidnight: 2041-10-20T01:50:00.000-02:00 

通過與前一天的工作和通過plusHours方法添加小時的實驗。

正如你可以在上面的輸出,午夜十月2041 19-20是目前預定日期爲DST變化,從UTC偏移抵消-03:00-02:00。一小時加一小時到午夜前跳wall-clock-time兩小時,從23小時到01小時而不是00小時。

此外,請注意,嘗試構建20日00:00:00的時間會導致Joda-Time異常,因爲沒有這樣的日期時間。

日期從數據庫

一個JDBC結果的getDate method返回java.sql.Date這是一個java.util.Date的bastardized版本。

java.sql.Date和java.util.Date都不包含時區信息,但被假定爲UTC。它們之間的區別在於sql版本的時間值被設置爲00:00:00(午夜),很難與SQL data type DATE匹配。在SQL中,DATE意味着僅限日期而沒有時間。不幸的是,舊版本的Java和JDBC沒有日期類,但應該有。這已在Java 8the java.time package及其LocalDate class糾正,但JDBC尚未趕上。

因此,如果您從數據庫中獲取java.sql.Date,則必須諮詢程序員或數據庫管理員以確認該日期時間是否確實指向UTC。它應該是,但適當的日期時間工作避開了許多程序員和管理員,所以你應該驗證。

如果java.sql.Date確實是UTC,那麼通過將它傳遞給DateTime構造函數來轉換爲Joda-Time。我強烈建議將一個DateTimeZone傳遞給新的DateTime,而不是依賴於被分配的JVM的默認時區。

DateTime dateTimeFromDatabaseBrazil = new DateTime(myJavaSqlDate, timeZoneSao_Paulo); 

DateTime dateTimeFromDatabaseUtc = new DateTime(myJavaSqlDate, DateTimeZone.UTC); 

如果分配到非UTC時區,你可能要在當地與懷念調整時間部分的一天的第一刻的一致性專注於日期而不是時間。要獲得第一時間,請致電withTimeAtStartOfDay method(Joda-Time 2.3中的新增功能,舊的「午夜」類別和方法不再使用)。

DateTime dateTimeFromDatabaseBrazil = new DateTime(myJavaSqlDate, timeZoneSao_Paulo).withTimeAtStartOfDay(); 

或者轉換爲LocalDate對象,如果你確定你想要的日期 - 不只是一天中的時間。

+0

我的問題是我使用resultSet.getDate(...)從數據庫中獲取日期。那麼日期就會在這裏產生錯誤。 – lexpfb

+0

@alexpfb關於從數據庫獲取日期的問題已經在StackOverflow上多次詢問和回答,但是我通過在我的答案的最後添加了一個部分來添加我對該主題的看法。 –

3

是您的本地/默認時區不是「BRT」早一個小時?

嘗試使用SimpleDateFormat來輸出您的日期。這在美國中部時間適用於我,將「BRT」設置爲使用的時區。

public static void main(String... args) { 
    Calendar c = Calendar.getInstance(); 
    c.setTimeZone(TimeZone.getTimeZone("BRT")); 
    c.set(2041, Calendar.OCTOBER, 20, 0, 0, 0); 
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 
    sdf.setTimeZone(TimeZone.getTimeZone("BRT")); 
    System.out.println(sdf.format(c.getTime())); 
    System.out.println(c.getTime()); 
    } 
+0

這也是我的想法。 – mttdbrd

+0

我猜這是海報PC上的巴西利亞夏令時? –

+0

這將是我的猜測。 – GriffeyDog

0

這是考慮夏令時。據我所知,你投入的時間不存在。時鐘在當天午夜前進到凌晨1點。我不確定它爲什麼在Java 7中起作用。也許這就是錯誤!

http://www.timeanddate.com/time/change/brazil/brasilia

夏令時發生在十月,每年在巴西的這2041恰好落在10月20日這不是這個日子第三個星期日。這是十月的任何第三個星期天。這裏有一些其他的例子:

 Calendar c = Calendar.getInstance(); 
     c.setTimeZone(TimeZone.getTimeZone("Brazil/East")); 
     c.set(2017, Calendar.OCTOBER, 15, 0, 0, 0); 
     System.out.println(c.getTime()); 
     c.set(2019, Calendar.OCTOBER, 20, 0, 0, 0); 
     System.out.println(c.getTime()); 
     c.set(2035, Calendar.OCTOBER, 21, 0, 0, 0); 
     System.out.println(c.getTime()); 

總體來說,我不會使用變通建議(除非使用喬達時間和拋出異常的)建議,如果你正試圖把在時你係統在現實生活中並不存在於此應用程序的區域/時區中(想到當時正在計劃運行的每日災難恢復作業並且從未發生過)。

+1

通過設置區域設置,您無法治癒時區數據。兩者都是不同的主題。 'Calendar'類中的'Locale'參數只與日曆周計算相關,而不適用於時區數據。 –

+0

@MenoHochschild,我知道這一點,但夏令時是根據十月份的第三個星期日計算的,這是一週計算。對?請解釋一下,如果我誤解了這不相關的話,我會從我的回答中刪除它:)。 –

+1

那麼,tzdb-repository在內部使用特定規則的某些星期計算何時切換到夏令時。但是這與'java.util.GregorianCalendar'的行爲沒有任何關係。在該類中,區域設置參數不用於時區計算。只要研究源代碼,很明顯。 –

相關問題