2016-05-31 7 views
5

私は異常をjavaの-8変換間java.util.Datejava.time.LocalDateTimeのためのいくつかのtestcodeを書いて、発見されたが normaltimeツー夏からの移行後時間に発生するようです年が2038年かそれ以上のとき。jdk8の日付変換のバグ?

これはjdk8のバグか、私が間違っているかどうかを知りたかっただけですか?

注:私は、Windows-7、64ビットJDKの午前、そんなに悪いことな効果を持っているでしょう2038-UNIXのバグによって影響を受けるべきではありません。ここで

私のデモ・コード:

package conversiontest; 

import java.text.SimpleDateFormat; 
import java.time.LocalDate; 
import java.time.LocalDateTime; 
import java.time.LocalTime; 
import java.time.ZoneId; 

public class ConversionTest { 

    public static void main(String[] args) { 
     new ConversionTest().testDateConversion(); 
    } 

    // Method under test: 
    public java.util.Date toJavaUtilDate(LocalDateTime localDateTime) { 
     return java.util.Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); 
    } 

    // Test-code: 
    public void testDateConversion() { 
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
     LocalDate localDate = LocalDate.of(2016, 1, 1); 
     LocalTime localTime = LocalTime.of(3, 22, 22); // 03:22:22 
     while (!localDate.toString().startsWith("2045-")) { 
      LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); 
      java.util.Date date = toJavaUtilDate(localDateTime); 
      String sLocalDateTime = localDateTime.toString().replace("T", " "); 
      String sJavaUtilDate = sdf.format(date); 
      if (!sLocalDateTime.equals(sJavaUtilDate)) { 
       System.out.println(String.format("FAILURE: '%s' != '%s'", sLocalDateTime, sJavaUtilDate)); 
      } 
      localDate = localDate.plusDays(1); 
     } 
    } 

} 

出力:

FAILURE: '2038-03-28 03:22:22' != '2038-03-28 02:22:22' 
FAILURE: '2039-03-27 03:22:22' != '2039-03-27 02:22:22' 
FAILURE: '2040-03-25 03:22:22' != '2040-03-25 02:22:22' 
FAILURE: '2041-03-31 03:22:22' != '2041-03-31 02:22:22' 
FAILURE: '2042-03-30 03:22:22' != '2042-03-30 02:22:22' 
FAILURE: '2043-03-29 03:22:22' != '2043-03-29 02:22:22' 
FAILURE: '2044-03-27 03:22:22' != '2044-03-27 02:22:22' 

あなたは出力、のLocalDateTimeから見ることができるように(2038年3月28日:22:22) は、java.util.Date(2038-03-28 :22:22)などに変換されます。ただし、では、は2038未満です。

誰でもこの情報を入力していますか?

EDIT:

私ZoneId.systemDefaultは()を与える: "ヨーロッパ/ベルリン"

C:\>java -version 
java version "1.8.0_91" 
Java(TM) SE Runtime Environment (build 1.8.0_91-b15) 
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode) 

C:\>javac -version 
javac 1.8.0_91 
+4

ヒント:あなたはgoogle 'java.util.date 2038'ですか?これにより、例えばhttp://stackoverflow.com/questions/15014589/java-util-date-bugが得られ、また読書の楽しさも得られます。http://stackoverflow.com/questions/4313707/why-should-a-java-programmer -care-about-year-2038-bug – Dilettant

+0

JavaはUnix時間に基づいており、2038年はUnix版のWindows y2kバグです – yonisha

+0

リンクをありがとうございますが、注意深く読んでください。 Javaは**私が使用している64ビットWindowsシステムで2038-bugを持つはずです**。そのことを忘れた、申し訳ありません。 – Rop

答えて

5

異なる結果がで日付から始まる夏の時間への切り替えの不一致、由来します

// for reproducible results 
System.setProperty("user.timezone", "Europe/Berlin"); 

LocalDate[] dates = {LocalDate.of(2037, 3, 29), LocalDate.of(2038, 3, 28)}; 
LocalTime[] time = { LocalTime.of(0, 59, 59), LocalTime.of(1, 00, 01), 
         LocalTime.of(1, 59, 59), LocalTime.of(2, 00, 01) }; 
for(LocalDate localDate : dates) { 
    for(LocalTime localTime1 : time) { 
     ZonedDateTime zoned = LocalDateTime.of(localDate, localTime1) 
          .atZone(ZoneId.of("UTC")) 
          .withZoneSameInstant(ZoneId.systemDefault()); 
     System.out.println(zoned); 
     System.out.println(new java.util.Date(zoned.toEpochSecond()*1000)); 
    } 
    System.out.println(); 
} 

印刷します:2038あなたは、次のコードを使用して違いを視覚化することができます

2037-03-29T01:59:59+01:00[Europe/Berlin] 
Sun Mar 29 01:59:59 CET 2037 
2037-03-29T03:00:01+02:00[Europe/Berlin] 
Sun Mar 29 03:00:01 CEST 2037 
2037-03-29T03:59:59+02:00[Europe/Berlin] 
Sun Mar 29 03:59:59 CEST 2037 
2037-03-29T04:00:01+02:00[Europe/Berlin] 
Sun Mar 29 04:00:01 CEST 2037 

2038-03-28T01:59:59+01:00[Europe/Berlin] 
Sun Mar 28 01:59:59 CET 2038 
2038-03-28T03:00:01+02:00[Europe/Berlin] 
Sun Mar 28 02:00:01 CET 2038 
2038-03-28T03:59:59+02:00[Europe/Berlin] 
Sun Mar 28 02:59:59 CET 2038 
2038-03-28T04:00:01+02:00[Europe/Berlin] 
Sun Mar 28 04:00:01 CEST 2038 

私たちが見ることができるように、両方の実装は2037年に夏の時間に切り替えた瞬間に同意し、java.util.*実装スイッチ1時間後に2038

のこの行動の変化がどのtable of hardcoded transition times in sun.util.calendar.ZoneInfo由来のに対し、有限のサイズを有する。 at this pointがわかるように、コードは、getTransitionIndexという方法でインデックスが返されることによって分岐します。インデックスがテーブルの長さ以上の場合は、it falls over to using a SimpleTimeZone implementationです。

私たちは、この問題が発生したことを確認することができます私のシステムで

long l1 = LocalDateTime.of(LocalDate.of(2037, 3, 29), LocalTime.of(1, 00, 01)) 
         .atZone(ZoneId.of("UTC")).toInstant().getEpochSecond()*1000; 
long l2 = LocalDateTime.of(LocalDate.of(2038, 3, 28), LocalTime.of(1, 00, 01)) 
         .atZone(ZoneId.of("UTC")).toInstant().getEpochSecond()*1000; 

TimeZone zone=TimeZone.getTimeZone("Europe/Berlin"); 
Field table=zone.getClass().getDeclaredField("transitions"); 
table.setAccessible(true); 
System.out.println("table length="+((long[])table.get(zone)).length); 

Method getTransitionIndex = zone.getClass() 
    .getDeclaredMethod("getTransitionIndex", long.class, int.class); 
getTransitionIndex.setAccessible(true); 
final Integer UTC_TIME = 0; 
int indexFor2037 = (Integer)getTransitionIndex.invoke(zone, l1, UTC_TIME); 
System.out.println("index for 2037="+indexFor2037); 
int indexFor2038 = (Integer)getTransitionIndex.invoke(zone, l2, UTC_TIME); 
System.out.println("index for 2038="+indexFor2038); 

プリント:

table length=143 
index for 2037=141 
index for 2038=143 

私は2038年に切り換え夏の時間を変更する計画を知らないので、私が思いますjava.timeの実装が正しいことを確認してください。また、ハードコードされた値の有限テーブルに基づく実装が自然な制限を持っていることは明らかです。

+1

最後の文について、JSR-310は「ハードコードされた値の有限テーブル」と、[ZoneRules.getTransitions()によって表される古いAPIのような一連のルール)](http://docs.oracle.com/javase/8/docs/api/java/time/zone/ZoneRules.html#getTransitions--)または「getTransitionRules()」この事実は、制限事項ではありません。しかし、古いzi形式をtzdb.datで置き換えると、2038年に 'Calendar'ベースのAPIがテーブル値からルールへの切り替えに間違っていると強く疑われます。 (2038は元のZoneInfo形式(ziファイル)がint-rangeに限定されているという事実によって説明できます) –

+2

@Meno Hochschild:私は「限界」という言葉を「特定のポイント"。これは行動スイッチを意味し、より高い保守努力を有する。 'getTransitions()'のドキュメントには、歴史的に歴史的なものがあります。履歴は変更や例外に満ちている傾向がありますが、将来は現在のルールが1つしかありません(SimpleTimeZoneがなぜそうするのか不明です。それは間違っている、おそらく、それはテーブルが2037に達するとテストされていない)。もちろん、2039年を振り返ってみると、過去のデータ、変更点、例外などもたくさんあるかもしれません... – Holger