ZonedDateTime изменить поведение JDK 8/11

Я перемещаю приложение с 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 ответ

Это известная ошибка в 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[Европа / Париж]

Другие вопросы по тегам