Java 8 DateTimeFormatter отклоняет правильную дату/время ISO 8601 со смещением

Я использую строку формата для DateTimeFormatter: uuuu-MM-dd'T'HH:mm:ssX

который должен поддерживать все возможные форматы смещения часового пояса, в том числе: Z, 00, 00:00, 0000

Согласно официальной документации DateTimeFormatter, квалификатор «X» должен соответствовать смещению в следующих форматах:

Смещение зоны X 'Z' для смещения нуля-X Z; -08; -0830; -08:30; -083015; -08:30:15;

но на самом деле это не так

строка ввода: "2014-01-01T00:30:00+00:00"

результат: java.time.format.DateTimeParseException: текст '2014-01-01T00:30:00+00:00' не может быть проанализирован, не проанализированный текст найден в индексе 22

строка ввода: "2014-01-01T00:30:00Z"

результат: верно

код:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX");
OffsetDateTime parsed = OffsetDateTime.parse(dateTimeAsString, formatter);

JDK 1.8.0_192 (Oracle, а не OpenJDK)


person Victor Mikhailov    schedule 05.02.2019    source источник
comment
Вы читали, что количество Х имеет значение? Есть разница между тем, что принимают XXX, XX и X.   -  person Ole V.V.    schedule 05.02.2019
comment
xxx не работает и с Z   -  person Victor Mikhailov    schedule 05.02.2019


Ответы (3)


Это немного сложно. Как говорит jvdmr, количество X имеет значение. XXXXX узнает -08:30:15, но не -083015. XXXX распознает последнее, но не первое.

Чтобы принять во внимание все возможные форматы примеров, нам нужно указать различные возможности. Это можно сделать в строке шаблона формата с помощью квадратных скобок. К ним относятся дополнительные детали. Небольшой эксперимент показал, что следующий шаблон охватывает все примеры:

uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]

Давайте попробуем:

    DateTimeFormatter formatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]");
    for (String dts : new String[] {
            "2014-01-01T00:30:00-08:30:15", "2014-01-01T00:30:00-083015",
            "2014-01-01T00:30:00-08:30", "2014-01-01T00:30:00-0830",
            "2014-01-01T00:30:00-08", "2014-01-01T00:30:00Z",
    }) {
        System.out.println(OffsetDateTime.parse(dts, formatter));
    }

Вывод из этого фрагмента:

2014-01-01T00:30-08:30:15
2014-01-01T00:30-08:30:15
2014-01-01T00:30-08:30
2014-01-01T00:30-08:30
2014-01-01T00:30-08:00
2014-01-01T00:30Z

Изменить

VelNaga предлагает не жестко кодировать форматы даты и времени ISO. Поскольку запись строк шаблона формата подвержена ошибкам, это может быть хорошей идеей. Например:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .appendPattern("[XXXXX][XXXX][X]")
            .toFormatter();

Вывод с использованием этого средства форматирования такой же, как и с использованием описанного выше. Это более многословно, но мы легко можем решить, что оно того стоит, поскольку оно менее подвержено ошибкам и может быть более четким и легким для чтения.

person Ole V.V.    schedule 05.02.2019

Из документов ( выделение мое):

Смещение X и x: форматирует смещение на основе количества букв шаблона. Одна буква выводит только час, например '+01', если минуты не указаны. -zero, в этом случае также выводятся минуты, например «+0130». Две буквы выводят часы и минуты без двоеточия, например «+0130». Три буквы выводят часы и минуты с двоеточием, например "+01:30". Четыре буквы выводят часы и минуты и необязательные секунды без двоеточия, например "+013015". Пять букв выводят часы и минуты и необязательную секунду с двоеточием, например «+01:30:15». Шесть или более букв вызывают исключение IllegalArgumentException. Буква шаблона «X» (верхний регистр) будет выводить «Z», когда смещение, которое должно быть выведено, равно нулю, тогда как буква шаблона «x» (нижний регистр) выводит «+00», «+0000» или «+00». :00'.

Это также работает в обратном порядке для парсинга дат. Вы хотите анализировать как с двоеточиями, так и без них, что означает, что вам придется использовать необязательные разделы, поскольку ни один шаблон не поддерживает это. Попробуйте этот шаблон: "uuuu-MM-dd'T'HH:mm:ss[XXX][XXXX]"

person jvdmr    schedule 05.02.2019

Не используйте жесткий код для любого формата даты ISO. DateFormatter уже имеет статический метод для преобразования формата даты ISO. Используйте приведенный ниже код,

Форматтер DateTimeFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;

OffsetDateTime разобран = OffsetDateTime.parse (dateTimeAsString, средство форматирования);

Это точно сработает. Найдите эту ссылку. для дополнительных параметров форматирования.

person VelNaga    schedule 05.02.2019
comment
Верно... мы должны включить шаблон. Но я вижу, что Оле В.В. уже поделился ответом. можно использовать Оле В.В. отвечать. - person VelNaga; 05.02.2019
comment
Я не могу использовать статический метод, потому что формат исходит из-за пределов кода Java в качестве параметра (файл конфигурации или server.properties) - person Victor Mikhailov; 06.02.2019
comment
@jvdmr Одна подсказка: смесь расширенных и базовых вариантов ISO, как показано в 2014-01-01T00:30:00+0000, не является допустимым ISO в соответствии с документом стандарта ISO, но, конечно, вы можете проанализировать его с помощью хакерского (и уродливого) обходного пути, такого как "uuuu-MM-dd'T'HH:mm:ss[XXX][XXXX]" - person Meno Hochschild; 06.02.2019