SimpleDateFormat с немецкой локалью - Java 8 против Java 10+ - PullRequest
0 голосов
/ 18 мая 2018

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

@Test
public void testParseDate() throws ParseException {
    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";

    DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
    Date date = dateFormatter.parse(toParse);

    //skipped assumptions
}

Этот тест проходит в Java 8 и ниже.Однако с Java 10 и выше это приводит к java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014".

Для записи : Помимо de_DE, исключение также выдается для локалей de_CH, de_AT, de_LU.

Мне известноиз-за того, что форматирование даты было изменено с JDK 9 ( JEP 252 ).Однако я считаю, что это разрушительное изменение, нарушающее обратную совместимость.Выдержка:

В JDK 9 данные репозитория общих локализаций (CLDR) Консорциума Unicode включены в качестве данных локали по умолчанию, так что вы можете использовать стандартные данные локали без каких-либо дальнейших действий.

В JDK 8, хотя данные локали CLDR связаны с JRE, по умолчанию она не включена.

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

Добавление . для дня недели (Mo.) компенсирует это, и тест пройден успешно.Однако это не является реальным решением для старых данных (в сериализованной форме, такой как XML).

При проверке этого stackoverflow post кажется, что поведение является преднамеренным для немецкого языка и можетсмягчить, указав java.locale.providers в режиме COMPAT.Однако мне не нравится идея полагаться на какое-то значение системного свойства по двум причинам, так как это может привести к изменению

  1. в следующих выпусках JDK.
  2. будет забыто в разных версияхокружения.

Мой вопрос:

  • Как я могу поддерживать обратную совместимость унаследованного кода с этим конкретным шаблоном даты, без переписывания /изменение существующих сериализованных данных или добавление / изменение системных свойств (например, java.locale.providers), которые могут быть забыты в разных средах (серверы приложений, автономные банки, ...)?

Ответы [ 3 ]

0 голосов
/ 14 июня 2018

Отформатированное значение в java 8 было Fr Juni 15 00:20:21 MESZ +0900 2018 Но оно изменилось на Fr.Juni 15 00:20:21 MESZ +0900 2018 EEE включает.ЭТО ПРОБЛЕМА СОВМЕСТИМОСТИ, и не имеет значения, что более старые версии кода не работают в более новых версиях (извините за переводчик). Если строка даты - ваша, вы должны добавить точку для пользователей новой версии.Или заставьте пользователей использовать Java 8 для использования вашего программного обеспечения.

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

    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    String str = toParse.substring(0, 2) + "." + toParse.substring(2);
    String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";

    DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
    System.out.println(dateFormatter.format(System.currentTimeMillis()));
    Date date = dateFormatter.parse(str);

Еще раз извините за мой плохой английский.

0 голосов
/ 14 июня 2018

Просто упомянуть: SimpleDateFormat - это старый способ форматирования дат, который, кстати, не является поточно-ориентированным.Начиная с Java 8 появились новые пакеты с именами java.time и java.time.format, и вы должны использовать их для работы с датами.Для ваших целей вы должны использовать класс DateTimeFormatter .

0 голосов
/ 18 мая 2018

Я не говорю, что это хорошее решение, но кажется, что оно проходит.

    Map<Long, String> dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di", 
            3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
    Map<Long, String> monthTexts = Map.ofEntries(Map.entry(1L, "Jan"), 
            Map.entry(2L, "Feb"), Map.entry(3L, "Mär"), Map.entry(4L, "Apr"),
            Map.entry(5L, "Mai"), Map.entry(6L, "Jun"), Map.entry(7L, "Jul"),
            Map.entry(8L, "Aug"), Map.entry(9L, "Sep"), Map.entry(10L, "Okt"),
            Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.DAY_OF_WEEK, dayOfWeekTexts)
            .appendLiteral(' ')
            .appendText(ChronoField.MONTH_OF_YEAR, monthTexts)
            .appendPattern(" dd HH:mm:ss z Z yyyy")
            .toFormatter(Locale.GERMANY);

    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    OffsetDateTime odt = OffsetDateTime.parse(toParse, formatter);
    System.out.println(odt);
    ZonedDateTime zdt = ZonedDateTime.parse(toParse, formatter);
    System.out.println(zdt);

Вывод работает на моем Oracle JDK 10.0.1:

2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]

С другой стороны, никакого хорошего решения не может быть.

java.time, современный Java-интерфейс даты и времени, позволяет нам указывать тексты, используемые для полей как для форматирования, так и для синтаксического анализа.Поэтому я использую это как для дня недели, так и для месяца, указывая сокращения без точки, которые использовались со старыми данными языкового стандарта COMPAT или JRE.Я использовал Java 9 Map.of и Map.ofEntries для построения карт, которые нам нужны.Если это также работает в Java 8, вы должны найти какой-то другой способ заполнить две карты, я вам верю.

Если вам нужен старомодный java.util.Date (вероятно, вустаревшая кодовая база), конвертируйте так:

    Date date = Date.from(odt.toInstant());
    System.out.println("As legacy Date: " + date);

Вывод в моем часовом поясе (Европа / Копенгаген, вероятно, примерно соответствует вашему):

As legacy Date: Mon Aug 18 11:25:26 CEST 2014

Предложение по стратегии

Я думаю, что если бы это был я, я бы поступил следующим образом:

  1. Подождите .Установите соответствующее системное свойство из Java: System.setProperty("java.locale.providers", "COMPAT,CLDR");, чтобы оно не было забыто ни в одной среде.Данные локали COMPAT существуют примерно с 1.0 (я думаю, по крайней мере, близко), поэтому от них зависит много кода (не только вашего).Название было изменено с JRE на COMPAT в Java 9. Для меня это может звучать как план сохранить данные в течение достаточно долгого времени.Согласно документации по раннему доступу она все еще будет доступна в Java 11 (следующая версия Java "долгосрочной поддержки") без предупреждения об устаревании или чего-либо подобного.И если он будет удален в какой-то будущей версии Java, вы, вероятно, сможете вскоре выяснить, что можете решить проблему до обновления.
  2. Используйте мое решение выше .
  3. Используйте интерфейс поставщика услуг локали , с которым связан Василий Бурк.Нет сомнений, что это хорошее решение на случай, если данные COMPAT будут удалены в неизвестное время в будущем.Возможно, вы даже сможете скопировать данные локали COMPAT в ваши собственные файлы, чтобы они не могли их отобрать у вас, а только проверьте, есть ли проблемы с авторским правом, прежде чем вы это сделаете.Причина, по которой я в последний раз упоминаю о хорошем решении, заключается в том, что вы сказали, что не довольны необходимостью устанавливать системное свойство в любой возможной среде, в которой может выполняться ваша программа.Насколько я могу судить, использование ваших собственных данных локали через интерфейс поставщика услуг локали все равно потребует от вас установки того же системного свойства (только для другого значения).
...