ZonedDateTime изменить поведение JDK 8/11 - PullRequest
1 голос
/ 22 мая 2019

Я мигрирую приложение с jdk 8 на 11 и вижу, что ZonedDateTime меняет поведение в отношении перехода на летнее время. JDK8

        ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
        System.out.println(parse);

Выход: 2037-05-10T19: 15 + 02: 00 [Европа / Париж]

JDK11 / 12

        ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
        System.out.println(parse);

2037-05-10T20: 15 + 02: 00 [Европа / Париж]

Может кто-нибудь объяснить мне, почему они изменили это поведение?

С уважением,

1 Ответ

6 голосов
/ 22 мая 2019

Это известная ошибка в Java 8: JDK-8066982

Я считаю, что то, что вы испытываете в Java 8, действительно является этой ошибкой: ZonedDateTime.parse () возвращает неправильный ZoneOffset при падении перехода DST.Название ошибки не рассказывает всей истории.Реальная проблема заключается в том, что в Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME (которая неявно используется используемым вами одним аргументом ZonedDateTime.parse) игнорируется смещение, если в проанализированную строку включен идентификатор часового пояса.Это в сочетании с базой данных часовых поясов, которая не согласуется с вашей строкой о смещении, используемом в Париже в октябре 2037 года, приводит к анализу момента времени, который конфликтует со смещением в строке.

Ошибка исправлена ​​вJava 9. Таким образом, в Java 9, 10 и 11, поскольку то же самое разногласие относительно смещения все еще существует, анализируемый момент основан на смещении строки.Затем он преобразуется в часовой пояс из строки с использованием правил из базы данных часовых поясов.Это приводит к изменению смещения с +01: 00 на +02: 00 и часового дня соответственно с 19:15 до 20:15.Я согласен с Java 9+ в том, что это правильное поведение.

Не используйте ZonedDateTime для дат в далеком будущем

Ваша проблема также частично вызвана использованием ZonedDateTime для будущей даты,Это рекомендуется только в самом ближайшем будущем, где мы предполагаем, что правила зоны не изменяются.Для даты и времени в 2037 году вам следует либо использовать Instant, если вы знаете момент времени, либо LocalDateTime, если вы знаете только дату и время дня.Только когда время приближается, и вы полагаете, что ваша установка Java получила последние обновления часового пояса, преобразуйте в ZonedDateTime.

. Как уже обсуждалось в комментариях, мы, вероятно, не знаем правильныхСмещение UTC для Парижа в октябре 2037 года еще.Похоже, что ЕС, скорее всего, откажется от летнего времени (DST) с 2021 года, и, насколько я знаю, французские политики еще не решили, сколько времени будет во Франции после этого.

Что если бы мы хотеливремя дня из строки?

Чтобы получить время из строки (19:15), выполните синтаксический разбор в LocalDateTime:

    String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
    LocalDateTime dateTime
            = LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
    System.out.println("Date and time from string: " + dateTime);

Вывод (выполняется на Java 11)):

Дата и время из строки: 2037-05-10T19: 15

На случай, если вы захотите полное поведение Java 8 в более поздней версии Java - как яупомянуто, это не рекомендуется, вы не должны использовать ZonedDateTime здесь:

    TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
    LocalDateTime dateTime = LocalDateTime.from(parsed);
    ZoneId zone = ZoneId.from(parsed);
    ZonedDateTime java8Zdt = dateTime.atZone(zone);
    System.out.println("Time from string in zone from string: " + java8Zdt);

Время от строки в зоне от строки: 2037-05-10T19: 15 + 02: 00 [Европа /Париж]

...