Автор оригинала: Andrea Ligios.
Обработка летнего времени экономии времени на Java
1. Обзор
Летнее время , или DST, это практика продвижения часов в летние месяцы для того, чтобы использовать дополнительный час естественного света (экономия тепла, освещение власти, повышение настроения, и так далее).
Он используется несколько стран и должны быть приняты во внимание при работе с датами и временем.
В этом учебнике мы увидим, как правильно обрабатывать DST в Java в зависимости от различных мест.
2. JRE и DST Mutability
Во-первых, чрезвычайно важно понимать, что во всем мире зоны DST изменения очень часто и нет центрального органа, координирующих его.
Страна, а в некоторых случаях даже город, может решить, если и как применить или отменить его.
Каждый раз, когда это происходит, изменение записывается в База данных часовых поясов IANA , и обновление будет развернуто в будущем выпуск JRE .
В случае, если это невозможно подождать, мы можем заставить измененные данные Часового пояса, содержащие новые настройки DST в jrE через официальный инструмент Oracle под названием Инструмент обновления часовой зоны Java , доступны на Java SE скачать страницу .
3. Неправильный путь: Трехтысяй идентификатор часового пояса
Еще в JDK 1,1 дней, API позволило трех букв часового пояса IDs, но это привело к ряду проблем.
Во-первых, это произошло потому, что один и тот же трехпомохий идентификатор может относиться к нескольким часовым поясам. Например, CST может быть США “Центральное стандартное время”, но и “Китайское стандартное время”. Платформа Java могла распознать только одну из них.
Другая проблема заключается в том, что стандартные часовые пояса никогда не принимают летнее время в учетную запись. Несколько областей/регионов/городов могут иметь свой локальный DST в одном и том же стандартном часовом поясе, поэтому Стандартное время не соблюдает его.
Из-за обратной совместимости это еще можно мгновенно java.util.Timezone с трех букв. Тем не менее, этот метод является deprecated и не должны использоваться больше.
4. Правильный путь: идентификатор часового пояса ТДБ
Правильный способ обработки DST в Java заключается в мгновенном Тайм- с конкретным идентификатором часового пояса ТДБ, например. “Европа/Рим”.
Затем мы будем использовать это в сочетании с конкретными временем классами, такими как java.util.Calendar чтобы получить правильную конфигурацию Время в необработанную компенсацию (в часовой пояс GMT) и автоматические корректировки смены DST.
Давайте посмотрим, как переход от GMT-1 GMT-2 (что происходит в Италии 25 марта 2018 года в 02:00) автоматически обрабатывается при использовании правильного Таймзон:
TimeZone tz = TimeZone.getTimeZone("Europe/Rome"); TimeZone.setDefault(tz); Calendar cal = Calendar.getInstance(tz, Locale.ITALIAN); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ITALIAN); Date dateBeforeDST = df.parse("2018-03-25 01:55"); cal.setTime(dateBeforeDST); assertThat(cal.get(Calendar.ZONE_OFFSET)).isEqualTo(3600000); assertThat(cal.get(Calendar.DST_OFFSET)).isEqualTo(0);
Как мы видим, ZONE_OFFSET 60 минут (потому что Италия GMT-1 ) в то время как DST_OFFSET 0 в то время.
Давайте добавим десять минут к Календарь :
cal.add(Calendar.MINUTE, 10);
Теперь DST_OFFSET стало 60 минут тоже, и страна перешла свое местное время от CET (Центральноевропейские времена) CEST (Центральноевропейские летние времена), которое GMT-2 :
Date dateAfterDST = cal.getTime(); assertThat(cal.get(Calendar.DST_OFFSET)) .isEqualTo(3600000); assertThat(dateAfterDST) .isEqualTo(df.parse("2018-03-25 03:05"));
Если мы отобразить две даты в консоли, мы увидим изменение часового пояса, а также:
Before DST (00:55 UTC - 01:55 GMT+1) = Sun Mar 25 01:55:00 CET 2018 After DST (01:05 UTC - 03:05 GMT+2) = Sun Mar 25 03:05:00 CEST 2018
В качестве заключительного теста мы можем измерить расстояние между двумя Дата s, 1:55 и 3:05:
Long deltaBetweenDatesInMillis = dateAfterDST.getTime() - dateBeforeDST.getTime(); Long tenMinutesInMillis = (1000L * 60 * 10); assertThat(deltaBetweenDatesInMillis) .isEqualTo(tenMinutesInMillis);
Как и следовало ожидать, расстояние составляет 10 минут вместо 70.
Мы видели, как избежать попадания в общие ловушки, с которыми мы можем столкнуться при работе с Дата путем правильного использования Таймзон и Локале .
5. Лучший способ: Java 8 Дата/время API
Работа с этими потоками небезопасных и не всегда удобный java.util классы всегда были жесткими, особенно из-за проблем совместимости, которые не позволяли им должным образом рефакторинг.
По этой причине Java 8 представила совершенно новый пакет, который java.time , и совершенно новый набор API, Api даты/времени. Это ISO-ориентированный, полностью поток-безопасный и сильно вдохновлен знаменитой библиотекой Joda-Time.
Давайте более подробно рассмотрим новые классы, начиная с преемника java.util.Date , java.time.LocalDateTime :
LocalDateTime localDateTimeBeforeDST = LocalDateTime .of(2018, 3, 25, 1, 55); assertThat(localDateTimeBeforeDST.toString()) .isEqualTo("2018-03-25T01:55");
Мы можем наблюдать, как Местное время соответствует ISO8601 профиль, стандартная и широко принятая дата-время нотации.
Он совершенно не знает об Зоны и Смещение , однако, именно поэтому мы должны преобразовать его в полностью до н.д.-знать java.time.ZonedDateTime :
ZoneId italianZoneId = ZoneId.of("Europe/Rome"); ZonedDateTime zonedDateTimeBeforeDST = localDateTimeBeforeDST .atZone(italianZoneId); assertThat(zonedDateTimeBeforeDST.toString()) .isEqualTo("2018-03-25T01:55+01:00[Europe/Rome]");
Как мы видим, в настоящее время дата включает в себя два основных задней части информации: 01:00 является ЗонаОффсет , в то время как (Европа/Рим) является ЗонаИд .
Как и в предыдущем примере, давайте запускаем DST через добавление десяти минут:
ZonedDateTime zonedDateTimeAfterDST = zonedDateTimeBeforeDST .plus(10, ChronoUnit.MINUTES); assertThat(zonedDateTimeAfterDST.toString()) .isEqualTo("2018-03-25T03:05+02:00[Europe/Rome]");
Опять же, мы видим, как и время, и зона смещения смещаются вперед, и по-прежнему сохраняя то же расстояние:
Long deltaBetweenDatesInMinutes = ChronoUnit.MINUTES .between(zonedDateTimeBeforeDST,zonedDateTimeAfterDST); assertThat(deltaBetweenDatesInMinutes) .isEqualTo(10);
6. Заключение
Мы видели, что такое летнее время и как справиться с ним с помощью некоторых практических примеров в различных версиях Java core API.
При работе с Java 8 и выше, использование новых java.time пакет поощряется благодаря простоте использования и его стандартной, без резьбовой природы.
Как всегда, полный исходный код доступен более на Github .