Подтвердить что ты не робот

Проблемы с Java 8 - DateTimeFormatter и ISO_INSTANT с ZonedDateTime

Поэтому я ожидаю, что этот код будет работать под новым пакетом дата/время Java 8, поскольку все, что он делает, - это преобразовать заданный ZonedDateTime в строку и обратно с использованием того же встроенного экземпляра DateTimeFormatter (ISO_INSTANT):

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: Не удалось получить ZonedDateTime от TemporalAccessor с использованием DateTimeFormatter и ZonedDateTime в Java 8

Кто-нибудь знает, что здесь происходит?

4b9b3361

Ответ 1

Форматер ISO_INSTANT документирован здесь - "Это особый случайный формат, предназначенный для того, чтобы позволить человеку читаемую форму Мгновенного действия". Таким образом, этот форматтер предназначен для использования с Instant не a ZonedDateTime.

Форматирование

При форматировании ISO_INSTANT может форматировать любой временный объект, который может предоставить ChronoField.INSTANT_SECONDS и ChronoField.NANO_OF_SECOND. Оба Instant и ZonedDateTime могут предоставить эти два поля, поэтому обе работают:

// works with Instant
Instant instant = Instant.now();
System.out.println(instant.format(DateTimeFormatter.ISO_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, но ZonedDateTime требует также ZoneId:

Для анализа a ZonedDateTime важно, чтобы существовала часовая зона ZoneId. Часовой пояс может быть (а) проанализирован из строки или (б) указан в форматтере (с использованием 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 (любой из этих трех может использоваться для синтаксического анализа ZonedDateTime без указания withZone()).

Резюме

Форматировщик ISO_INSTANT является специальным форматированием, предназначенным для работы с Instant. Если вы используете ZonedDateTime, вы должны использовать другой форматтер, например ISO_DATE_TIME или ISO_ZONED_DATE_TIME.

Ответ 2

Я не уверен, но это может быть ошибка в Java 8. Возможно, это было предназначено для того, чтобы вести себя таким образом, но я думаю, что обходной путь, который я собираюсь предложить вам, должен быть по умолчанию (когда no 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

Я не знаю, является ли это ожидаемым поведением или нет (возможно, это так), но технически форматер ISO_INSTANT не включает часовой пояс. Если вы попытаетесь использовать форматтера DateTimeFormatter.ISO_ZONED_DATE_TIME, вы получите то, что ожидаете.