Java: исправить неверный часовой пояс в дате объекта - PullRequest
3 голосов
/ 24 января 2020

Внешний API возвращает объект с датой.
В соответствии со спецификацией API все даты всегда указываются в GMT.

Однако сгенерированные клиентские классы (которые я не могу редактировать) неправильно устанавливают часовой пояс. Вместо этого он использует местный часовой пояс без преобразования даты в этот часовой пояс.

Итак, короче говоря, у меня есть объект с датой, которую я знаю как GMT, но она говорит CET. Как я могу исправить эту ошибку, не меняя местный часовой пояс на компьютере или делая что-то вроде этого:

LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
                        ZoneId.of("CET"));

Спасибо.

Ответы [ 2 ]

2 голосов
/ 24 января 2020

tl; dr ⇒ используйте ZonedDateTime для преобразования

public static void main(String[] args) {
    // use your date here, this is just "now"
    Date date = new Date();
    // parse it to an object that is aware of the (currently wrong) time zone
    ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
    // print it to see the result
    System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));

    // extract the information that should stay (only date and time, NOT zone or offset)
    LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
    // print it, too
    System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    // then take the object without zone information and simply add a zone
    ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
    // print the result
    System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}

Выход:

2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]

Объяснение:

Причиной, по которой ваш подход не только исправил зону, но и соответствующим образом скорректировал время (что хорошо при желании), является использование LocalDateTime, созданного из Instant. Instant представляет момент времени, который может иметь разные представления в разных зонах, но он остается одним и тем же моментом. Если вы создадите LocalDateTime из него и поместите другую зону, дата и время конвертируются в целевую зону. Это не просто замена зоны с сохранением даты и времени, как они есть.

Если вы используете LocalDateTime из ZonedDateTime, вы извлекаете представление даты и времени, игнорируя зону, что позволяет впоследствии добавить другую зону и сохранить дату и время такими, какими они были.

Редактировать: если код работает в той же JVM, что и неисправный код, вы можете использовать ZoneId.systemDefault(), чтобы получить тот же часовой пояс, что и в неисправном коде. И в зависимости от вкуса вы можете использовать ZoneOffset.UTC вместо ZoneId.of("GMT").

0 голосов
/ 24 января 2020

Боюсь, вы не сможете обойти некоторые расчеты здесь. Я настоятельно рекомендую придерживаться подхода, основанного на java.time классах, но в качестве альтернативы вы можете использовать класс java.util.Calendar и myCalendar.get(Calendar.ZONE_OFFSET) для этих вычислений:

https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET

...