DateTimeFormatter может форматировать дату, но не может прочитать ее собственный формат - PullRequest
0 голосов
/ 19 января 2020

Я пытаюсь проанализировать дату ISO-8601 с литералом 'Z' в конце.

Это отформатирует дату правильно:

String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String string = formatter.format(OffsetDateTime.now());
System.out.println(string);

Печать:

2020-01-19T03:06:58.090Z

Но затем, пытаясь сразу же прочитать его обратно:

TemporalAccessor acc = formatter.parse(string);
OffsetDateTime time = OffsetDateTime.from(acc);
System.out.println(time);

Сбой с:

Exception in thread "main" java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
    at java.time.OffsetDateTime.from(OffsetDateTime.java:370)
    at HelloWorld.main(HelloWorld.java:27)
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
    at java.time.ZoneOffset.from(ZoneOffset.java:348)
    at java.time.OffsetDateTime.from(OffsetDateTime.java:359)
    ... 1 more

Я не могу изменить шаблон для использования не-буквального 'Z', но я заметил, что изменение его на Z, чтобы оно форматировало даты с +0000 на конце, может быть успешно прочитано. Я также не могу изменить с помощью TemporalAccessor для чтения даты, поскольку она исходит из третье лицо (Джексон). Есть идеи?

Ответы [ 2 ]

4 голосов
/ 19 января 2020

Поскольку вы используете литерал 'Z', в данных нет часового пояса, поэтому вам придется указать его вручную:

OffsetDateTime time = LocalDateTime.from(acc).atOffset(ZoneOffset.UTC);
2 голосов
/ 19 января 2020

Z не является литералом

Z в формате ISO 8601 - это смещение нуля от UT C (также известное как часовой пояс Зулу ). Вам нужно отформатировать и разобрать его как таковой, иначе вы получите неправильные результаты. OffsetDateTime.now() может и обычно возвращает дату-время с ненулевым смещением. Когда вы печатаете его со смещением Z, вы печатаете неверную информацию, в другой момент времени.

Я не могу изменить шаблон для использования не буквального 'Z',

Надеюсь, я неправильно понял эту часть. Мне кажется, что у вас есть ошибка, которую вы не можете исправить.

Вам не нужен форматер

OffsetDateTime и другие классы даты и времени из java .time print ISO 8601 отформатируйте их toString методы и проанализируйте тот же формат обратно. Поэтому вам не нужно явно указывать какой-либо форматер.

Возможно, класс Instant соответствует вашим требованиям лучше, чем OffsetDateTime. Instant - это момент времени, независимый от часового пояса и смещения. Его метод toString генерирует строку ISO 8601 в UT C и, следовательно, всегда с Z в конце.

    String string = Instant.now().toString();
    System.out.println(string);

    // Parse back
    Instant time = Instant.parse(string);
    System.out.println(time);

Когда я только что запустил это, вывод был:

2020-01-19T05:37:29.630135Z
2020-01-19T05:37:29.630135Z

Если вы можете требовать, чтобы для входящей строки даты-времени всегда было смещение Z, я должен сказать, что это решение для вас. Вы можете всегда преобразовать Instant в другой тип после анализа, если вам нужно.

Если вы предпочитаете OffsetDateTime:

    String string = OffsetDateTime.now().toString();
    System.out.println(string);

    // Parse back
    OffsetDateTime time = OffsetDateTime.parse(string);
    System.out.println(time);

Пример вывода из моего часового пояса:

2020-01-19T06:37:29.721666+01:00
2020-01-19T06:37:29.721666+01:00

Вы заметили, что напечатано правильное смещение для моего часового пояса, +01:00, а не Z, что хорошо показывает, что Z был бы неправильным. Если вам нужно время UT C, просто скажите OffsetDateTime, передав ZoneOffseet.UTC методу now:

        String string = OffsetDateTime.now(ZoneOffset.UTC).toString();
        System.out.println(string);
2020-01-19T05:43:06.700402Z

Разбор выполняется так: до этого.

...