web-dev-qa-db-ja.com

Java 8-DateTimeFormatterおよびZonedDateTimeに関するISO_INSTANTの問題

だから私はこのコードが新しいJava 8 date/timeパッケージの下で動作することを期待しています):

ZonedDateTime now = ZonedDateTime.now();
System.out.println(ZonedDateTime.parse(
    now.format(DateTimeFormatter.ISO_INSTANT),
    DateTimeFormatter.ISO_INSTANT));

しかし、明らかにそうではありません:

Exception in thread "main" Java.time.format.DateTimeParseException: Text '2014-09-01T19:37:48.549Z' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {MilliOfSecond=549, NanoOfSecond=549000000, MicroOfSecond=549000, InstantSeconds=1409600268},ISO of type Java.time.format.Parsed
    at Java.time.format.DateTimeFormatter.createError(DateTimeFormatter.Java:1918)
    at Java.time.format.DateTimeFormatter.parse(DateTimeFormatter.Java:1853)
    at Java.time.ZonedDateTime.parse(ZonedDateTime.Java:597)

私はすでにこのエントリを見ましたが、ローカルのものではなくZonedDateTimeオブジェクトが必要であり、8u20がすでにインストールされているため、助けにはなりませんでした: DateTimeFormatterとZonedDateTime in Java 8

ここで何が起こっているのか誰にも分かりますか?

37
asieira

_ISO_INSTANT_フォーマッターは文書化されています ここ -「これは、インスタントの人間が読める形式を許可することを目的とした特別な場合のフォーマッターです」。そのため、このフォーマッタはInstantではなくZonedDateTimeで使用することを目的としています。

書式設定

フォーマット時に、_ISO_INSTANT_は、_ChronoField.INSTANT_SECONDS_および_ChronoField.NANO_OF_SECOND_を提供できる一時オブジェクトをフォーマットできます。 InstantZonedDateTimeの両方がこれらの2つのフィールドを提供できるため、両方とも機能します。

_// works with Instant
Instant instant = Instant.now();
System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));

// works with ZonedDateTime 
ZonedDateTime zdt = ZonedDateTime.now();
System.out.println(zdt.format(DateTimeFormatter.ISO_INSTANT));

// example output
2014-09-02T08:05:23.653Z
_

解析

解析するとき、_ISO_INSTANT_は_ChronoField.INSTANT_SECONDS_と_ChronoField.NANO_OF_SECOND_のみを生成します。 Instantはこれらの2つのフィールドから作成できますが、ZonedDateTimeにはZoneIdも必要です。

ZonedDateTimeを解析するには、タイムゾーンZoneIdが存在することが不可欠です。タイムゾーンは、(a)文字列から解析するか、(b)フォーマッタに指定できます(JDK 8u20を使用):

_// option a - parsed from the string
DateTimeFormatter f = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);

// option b - specified in the formatter - REQUIRES JDK 8u20 !!!
DateTimeFormatter f = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);
_

_ISO_ZONED_DATE_TIME__ISO_OFFSET_DATE_TIME_ 、および _ISO_DATE_TIME_ のドキュメントを参照してくださいwithZone())を指定せずにZonedDateTimeを解析します。

概要

_ISO_INSTANT_フォーマッターは、Instantで動作するように設計された特別な場合のフォーマッターです。 ZonedDateTimeを使用している場合は、_ISO_DATE_TIME_や_ISO_ZONED_DATE_TIME_などの別のフォーマッターを使用する必要があります。

57
JodaStephen

わからないが、これはJava 8.のバグかもしれないデフォルトの動作になります(ZoneIdが指定されていない場合は、システムのデフォルトを使用します)。

ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT
    .withZone(ZoneId.systemDefault());
System.out
    .println(ZonedDateTime.parse(now.format(formatter), formatter));

OpenJDKで修正された同様のバグがあります: JDK-8033662 -しかしそれは似ているだけで、まったく同じではありません。

3
Cristina

それが予想される動作であるかどうかはわかりませんが(おそらくそうです)、技術的にはISO_INSTANTフォーマッタにはタイムゾーンは含まれません。 DateTimeFormatter.ISO_ZONED_DATE_TIMEフォーマッタではなく、期待どおりの結果が得られます。

1
assylias