Невозможно проанализировать строку в формате ISO 8601, без двоеточия в смещении, в Java 8 Date

Я немного разочарован функциональностью формата / разбора даты в Java 8. Я пытался найти конфигурацию Джексона и DateTimeFormatter разобрать "2018-02-13T10:20:12.120+0000" Строка для любой даты Java 8, и не нашел его.
Это java.util.Date пример, который отлично работает:

Date date = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ")
                      .parse("2018-02-13T10:20:12.120+0000");

Тот же формат не работает с новым API даты и времени

ZonedDateTime dateTime = ZonedDateTime.parse("2018-02-13T10:20:12.120+0000",
                   DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ"));

Мы должны иметь возможность форматировать / анализировать дату в любом формате, подходящем для приложения FE UI. Может быть, я что-то неправильно понимаю или ошибаюсь, но я думаю, java.util.Date дает больше гибкости формата и проще в использовании.

1 ответ

Решение

ТЛ; др

Пока ошибка не исправлена:

OffsetDateTime.parse( 
    "2018-02-13T10:20:12.120+0000" , 
    DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
)

Когда ошибка исправлена:

OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" )

подробности

Вы используете неправильные классы.

Избегайте проблемных старых классов наследства, таких как Date, Calendar, а также SimpleDateFormat, Теперь вытеснены классами java.time.

ZonedDateTime Класс, который вы использовали, хорош, он является частью java.time. Но он предназначен для полного часового пояса. Ваша входная строка имеет просто смещение от UTC. Полный часовой пояс, напротив, представляет собой набор смещений, действующих для региона в разные моменты времени, в прошлом, настоящем и будущем. Например, при переходе на летнее время (DST) в большей части Северной Америки смещения меняются два раза в год, уменьшаясь весной, когда мы переводим часы на час вперед, и возвращаемся к более длинным значениям осенью, когда мы переводим часы назад. час.

OffsetDateTime

Только для смещения, а не для часового пояса, используйте OffsetDateTimeучебный класс.

Ваша входная строка соответствует стандарту ISO 8601. Классы java.time по умолчанию используют стандартные форматы при разборе / генерации строк. Поэтому нет необходимости указывать шаблон форматирования.

OffsetDateTime odt = OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" );

Ну, это должно было сработать. К сожалению, в Java 8 есть ошибка (по крайней мере до Java 121 Update 121), когда этот класс не может обработать смещение, пропуская двоеточие между часами и минутами. Таким образом, ошибка кусает +0000 но нет +00:00, Таким образом, до получения исправления у вас есть выбор из двух обходных путей: (a) взлом, манипулирование входной строкой или (b) определение явного шаблона форматирования.

Хак: манипулировать входной строкой, чтобы вставить двоеточие.

String input = "2018-02-13T10:20:12.120+0000".replace( "+0000" , "+00:00" );
OffsetDateTime odt = OffsetDateTime.parse( input );

Более надежный обходной путь - определить и передать шаблон форматирования в DateTimeFormatter объект.

String input = "2018-02-13T10:20:12.120+0000" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" );
OffsetDateTime odt = OffsetDateTime.parse( input , f );

odt.toString (): 2018-02-13T10: 20: 12.120Z

Кстати, вот совет: я обнаружил, что со многими протоколами и библиотеками ваша жизнь становится проще, если в ваших смещениях всегда есть двоеточие, всегда есть часы и минуты (даже если минуты равны нулю) и всегда используется заполнение нуль (-05:00 скорее, чем -5).

Instant

Если вы хотите работать со значениями, которые всегда находятся в UTC (и вы должны это сделать), извлеките Instant объект.

Instant instant = odt.toInstant();

ZonedDateTime

Если вы хотите посмотреть этот момент через призму часов в каком-то регионе, примените часовой пояс.

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );

Смотрите этот код в прямом эфире на IdeOne.com.

Все это много раз освещалось во многих ответах на многие вопросы. Пожалуйста, внимательно прочитайте Stack Overflow перед публикацией. Вы бы обнаружили много десятков, если не сотен примеров.


О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date, Calendar & SimpleDateFormat,

Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на классы java.time.

Чтобы узнать больше, смотрите Oracle Tutorial. И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310.

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классы.

Где взять классы java.time?

  • Java SE 8, Java SE 9, Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval, YearWeek, YearQuarter и многое другое.

Вкратце: это не ошибка, просто ваш шаблон неправильный.

Пожалуйста, используйте тип OffsetDateTime который специально разработан для смещения часовых поясов и использует шаблон следующим образом:

OffsetDateTime odt =
    OffsetDateTime.parse( 
        "2018-02-13T10:20:12.120+0000" , 
        DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSZZZ" )
    )

Подробно о проблемах:

а) 12-часовой формат по сравнению с 24-часовым форматом

"h" обозначает час AM/PM в 12-часовом формате, но, очевидно, вам понадобится "H" для 24-часового формата, как того требует ISO-8601.

б) Форма нулевого смещения

Если вы хотите анализировать нулевое смещение, например, "+0000" вместо "Z" (как описано в документе ISO), вы не должны использовать символ шаблона "X", а "ZZZ". Ссылаясь на синтаксис шаблона:

Смещение Z: форматирование смещения на основе количества букв образца. Одна, две или три буквы выводят часы и минуты без двоеточия, например "+0130". Если смещение равно нулю, на выходе будет "+0000".

c) Ваш ввод НЕ совместим с ISO-8601, поэтому в Java нет ошибок.

Ваше предположение о том, что "2018-02-13T10:20:12.120+0000" должно быть действительным ISO, неверно, потому что вы смешиваете базовый формат (в части смещения) и расширенный формат, который явно запрещен в ISO-бумаге (см.2 (примерная часть) и 4.3.3d). Ссылаясь на ISO-8601:

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

Заявление Б. Бурка о том, что java.timeИмеет ошибку, основывается на том же неверном ожидании ISO-совместимости. И документация скажемISO_OFFSET_DATE_TIMEописывает поддержку только расширенного ISO-формата. См. Также связанную проблему JDK. Не все варианты ISO-8601 напрямую поддерживаются, поэтому правильное построение анализатора на основе шаблонов - это нормально.

если смещение +0000 попробуйте это

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
LocalDate from =LocalDate.parse("2018-02-13T10:20:12.120+0000",f);

Вы можете использовать следующий код, чтобы выполнить свою работу.

String str = "01/01/2015";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
LocalDate dateTime = LocalDate.parse(str, formatter);

System.out.println(dateTime.format(formatter)); // not using toString
Другие вопросы по тегам