Различное поведение WeekFields на JVM 8 и JVM 10 - PullRequest
15 голосов
/ 13 марта 2019

У меня очень простая программа:

 public static void main(String[] args) {
        LocalDate year = LocalDate.ofYearDay(2022, 100);
        System.out.println(year);

        System.out.println(WeekFields.of(Locale.GERMAN).weekOfYear());

        System.out.println(year.with(WeekFields.of(Locale.GERMAN).weekOfYear(), 0));
        System.out.println(year.with(WeekFields.of(Locale.GERMAN).weekOfYear(), 0).with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)));
    }

Но в JVM 8 и JVM 10 он ведет себя по-разному. Похоже, проблема в реализации WeekFields.of(Locale.GERMAN).weekOfYear().

На JVM 10 я получаю следующие результаты:

JVM 10

2022-04-10
WeekOfYear[WeekFields[SUNDAY,1]]
2021-12-19
2021-12-13

тогда как на JVM 8:

JVM 8

2022-04-10
WeekOfYear[WeekFields[MONDAY,4]]
2022-01-02
2021-12-27

Почему это происходит? Я делаю что-то, что потенциально может вызвать неопределенное поведение? Или это изменение в поведении где-то указано?

JVM10:

$ java -version
openjdk version "10.0.2" 2018-07-17
OpenJDK Runtime Environment (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4)
OpenJDK 64-Bit Server VM (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode)

JVM8

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

EDIT: JVM 9 ведет себя так же, как JVM 8, а JVM 11 ведет себя как JVM 10

РЕДАКТИРОВАТЬ 2: Я действительно нашел коммит, который изменил поведение -> здесь на github , и мне любопытно, почему это было изменено.

Ответы [ 3 ]

11 голосов
/ 13 марта 2019

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

Я думаю, что JVM10 более корректен, поскольку Locale.GERMAN не относится клюбая страна с таким простым Java предполагает США (как-то сомнительно, чтобы обращаться с этой страной как с мировым стандартом, но так же и с Java).

Вам лучше использовать Locale.GERMANY.Эта страна действительно использует понедельник в качестве первого дня недели (в отличие от США, начинающихся в воскресенье, которые используются в качестве запасного варианта для GERMAN, который является просто языком, а не страной.

Обновление - мое исследование о CLDRданные:

Текущие данные CLDR для резервной страны / территории "001" (= во всем мире) определения недели (понедельник как первый день недели и 1 = минимальные дни первой неделив календарном году). Удивительно, но это отличается от определения в США (воскресенье, 1). Я думаю, Oracle только что сделал свое дело. Лично я согласен с @Holger и скорее ожидаю, что ISO-8601 будет запасным вариантом (понедельник, 4).).

Однако вы можете восстановить поведение Java-8 на вашем компьютере JVM-10, установив следующее системное свойство (не проверено):

java.locale.providers=COMPAT,CLDR,SPI
4 голосов
/ 16 марта 2019

Как исправить

Следующие два параметра эквивалентны. Выберите тот, который вы считаете наиболее подходящим для вашей ситуации.

  • WeekFields.ISO
  • WeekFields.of(Locale.GERMANY) с использованием страны, Германии, вместо языка, немецкого.

Почему это происходит? CLDR и страна против языка

Здесь есть два отличия:

  1. Различные данные о локали по умолчанию в разных версиях Java.
  2. Как уже говорили другие, разница между языковым языком и языком, который включает страну.

Определение недельных схем в разных локалях является частью данных локали. Java может получать данные о локали из четырех источников. Java включала свои собственные данные локали из ранних версий, и они были по умолчанию вплоть до Java 8. Из Java 8 также были включены данные CLDR (Unicode Common Locale Data Repository), и они стали значениями по умолчанию из Java 9. Что, очевидно, изменило некоторые функциональность и сломал некоторый старый код, как вы уже испытали Точнее, по умолчанию:

  • Java 8: JRE, SPI, где JRE ссылается на собственные данные локали Java.
  • Java 9, 10 и 11: CLDR, COMPAT, где CLDR - это то, что говорит, а COMPAT - это новое имя для данных JRE.

Значения по умолчанию можно изменить, установив системное свойство java.locale.providers. Таким образом, мы можем получить поведение Java 8 в Java 9 и более поздних версиях, установив для этого свойства значение COMPAT,SPI. И наоборот, мы можем получить поведение Java 10 в Java 8, установив для него CLDR,JRE. Таким образом, в основе лежит не столько разница между версиями Java, сколько их значения по умолчанию.

Переход от данных Java к данным CLDR заключается в следующем: данным языковых стандартов Java назначаются недельные определения для языковых языковых стандартов (например, немецких) в зависимости от того, на каком языке в основном говорят. В отличие от этого, философия CLDR заключается в том, что вы можете говорить на любом языке в любой стране мира и предпочитаете выбирать недельную схему в зависимости от страны, а не языка. Как следствие, для регионов, в которых не указана страна (например, для Германии), используется определение недели по умолчанию во всем мире.

Почему в CLDR по умолчанию используется определение недели по умолчанию «воскресенье, 1», я не понимаю. Как и другие, я ожидал и предпочел ISO международный стандарт «Понедельник, 4». Как я сказал в комментарии, я также обнаружил заметку о том, что так и должно быть, но это не так (по крайней мере, в версиях CLDR, используемых в Java с 8 по 11).

Java 9 особенная

Как вы заметили, в Java 9 с данными локали по умолчанию вы получаете «Monday 4» из Locale.GERMAN хотя CLDR должен быть первым по умолчанию. С другой стороны, если я установлю java.locale.providers на CLDR в одиночку, я получу «воскресенье 1», как в Java 10 и 11.

Возможное объяснение состоит в том, что версия CLDR, используемая в Java 9, не включает определение недели для немецкого языка. Таким образом, с провайдерами по умолчанию, CLDR, COMPAT, Java возвращается к COMPAT, который предоставляет «Понедельник, 4» для немецкого языка. Когда я использую только CLDR, он возвращается к базовому значению по всему миру, «Sunday, 1». Если это объяснение верное (что я не могу гарантировать), может показаться, что версии данных CLDR, используемые в Java 10 и 11, содержат недельное определение для немецкого языка.

Ссылки

Документация LocaleServiceProvider с информацией о провайдерах региональных данных и спецификациях провайдеров по умолчанию:

Ссылки CLDR:

4 голосов
/ 13 марта 2019

Перечисление Locale различает экземпляры, полезные для языка (например, GERMAN), и экземпляры, полезные для страны (например, GERMANY).Используйте первое, если вы хотите установить другое значение языка и сохранить локальный Locale, с другой стороны используйте второе, чтобы установить и время, и настройки языка.

...