3

純粋に新しい API を使用していた Calendar API を使用して、既存の API を使用しなければならないコードの一部を開発していました。変換で奇妙な動作が発生しました。次の例を参照してください。

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");

String date1 = "0000-01-01T00:00:00Z";
Calendar calendar = Calendar.getInstance();
calendar.setTime(df.parse(date1));
Instant instant = calendar.toInstant();
ZonedDateTime zonedDateTime = instant.atZone(calendar.getTimeZone().toZoneId());

System.out.println(calendar.getTime() + " " +calendar.getTimeZone().getDisplayName());
System.out.println(instant.toString());
System.out.println(zonedDateTime.toString());

String date2 = "0000-01-01T00:00:00+01:00";
calendar.setTime(df.parse(date2));
instant = calendar.toInstant();
zonedDateTime = instant.atZone(calendar.getTimeZone().toZoneId());

System.out.println(calendar.getTime() + " " +calendar.getTimeZone().getDisplayName());
System.out.println(instant.toString());
System.out.println(zonedDateTime.toString());

私が得ている出力は次のとおりです。

Thu Jan 01 01:00:00 CET 1 Central European Standard Time
-0001-12-30T00:00:00Z
-0001-12-30T00:09:21+00:09:21[Europe/Paris]
Thu Jan 01 00:00:00 CET 1 Central European Standard Time
-0001-12-29T23:00:00Z
-0001-12-29T23:09:21+00:09:21[Europe/Paris]

したがって、グレゴリオ暦の最初の行はどちらの場合でも正しいです。

  • case1 の +01:00 ゾーンで 1 月 1 日午前 1:00 を取得します。
  • ケース 2 では、1 月 1 日の午前 0:00 を +01:00 ゾーンで取得します。

Calendarからに変換した後、Instant突然次のようになったため、日付に関する問題が既に発生しています。

  • ケース1の場合、12月30日(48時間前)に
  • ケース 2 の 12 月 29 日 (72 時間前) に ... また、変換中に導入された数百ミリ秒の小さなランダムな不正確さもあることがわかりました。

Instantnext からtoに変換するZonedDateTimeと、今

  • ヨーロッパ/パリに渡されたinstant.atZone()タイムゾーンが+00:09:21の奇妙なタイムゾーンになったため、9分21秒後

私はそれをさらにテストし、一般的に と の間の変換はCalendarInstant1583 年より前の日付では信頼性が非常に低くなり、Instant1911Local/ZonedDateTime年より前の日付ではタイムゾーンの問題により信頼性が低くなります。

1911 年より前の日付の時刻を保存/変換する人はほとんどいないことはわかっています (ただし、そのような使用例はまだ想像できます)。クリストファー・コロンブスが発見されたアメリカに出発した時期を見てみましょう!:

1492-08-03
1492-08-11T23:00:00Z
1492-08-11

その結果、両方の API からエポック ミリ秒を取得すると、1911 年より前の早い時期に同じ ISO 日付に対して異なる結果が得られることもわかりました (問題はCalendarの実装にあるようです)。

System.out.println(Instant.parse("1911-01-01T00:00:00Z").toEpochMilli());
calendar.setTime(df.parse("1911-01-01T00:00:00Z"));
System.out.println(calendar.getTimeInMillis());

「うまく機能する」ように変換する正しい方法は何ですか (tm) ?

注:これまでのところ、最も安全な方法は、最初に ISO 日付文字列に変換することだと思います。より良い解決策はありますか?

4

1 に答える 1