ZonedDateTime будет использовать тот же часовой пояс зимы, когда летнее летнее время?
ZonedDateTime zdt = ZonedDateTime.of(2015, 10, 18, 0, 30, 0, 0,
ZoneId.of("America/Sao_Paulo"));
System.out.println(zdt); // 2015-10-18T01:30-02:00[America/Sao_Paulo]
Вы можете видеть, что час равен 1, в то время как мы устанавливаем час как 0, а часовой пояс равен UTC-02:00, в то время как переход на летнее время должен быть UTC-03:00.
Но вот другой пример:
ZonedDateTime zdt = ZonedDateTime.of(2015, 10, 18, 0, 30, 0, 0,
ZoneId.of("America/Los_Angeles"));
System.out.println(zdt); //2015-10-18T00:30-07:00[America/Los_Angeles]
Вы можете видеть, что часовой пояс перехода на летнее время равен
UTC-07:00, а час - 0, Как мы установили.Почему они разные?
2 ответов:
Как уже объяснялось в ответе @Misha's , это происходит из-за правил летнего времени.
В Сан-Паулу DST начинается в полночь 2015-10-18: часы движутся вперед на 1 час, поэтому он "прыгает" с
23:59:59на01:00:00. Существует разрыв между00:00:00и00:59:59, поэтому время00:30регулируется соответствующим образом.Вы можете проверить, являются ли дата и время допустимыми для часового пояса, используя классы
ZoneRulesиZoneOffsetTransition:ZoneId sp = ZoneId.of("America/Sao_Paulo"); ZoneRules rules = sp.getRules(); // check if 2015-10-18 00:30 is valid for this timezone LocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30); List<ZoneOffset> validOffsets = rules.getValidOffsets(dt); System.out.println(validOffsets.size()); // size is zero, no valid offsets at 00:30
getValidOffsetsметод возвращает все допустимые смещения для указанной даты / времени. Если список пуст, это означает, что дата/время не "существует" в этом часовом поясе (обычно из-за DST часы прыгают вперед).Когда дата/время существует в часовом поясе, возвращается смещение:
ZoneId la = ZoneId.of("America/Los_Angeles"); rules = la.getRules(); validOffsets = rules.getValidOffsets(dt); System.out.println(validOffsets.size()); // 1 - date/time valid for this timezone System.out.println(validOffsets.get(0)); // -07:00Для часового пояса
Los_Angelesвозвращается 1 допустимое смещение:-07:00.PS: смещение изменений обычно происходит из-за ДСТ, но это не всегда так. DST и смещения определяются правительствами и законами, и они могут меняйтесь в любое время. Таким образом, разрыв в допустимом смещении может также означать, что такое изменение произошло (какой-то политик решил изменить стандартное смещение страны, поэтому разрыв не обязательно может быть связан с ДСТ).
Вы также можете проверить, когда происходит изменение и какое смещение до и после него:
ZoneId sp = ZoneId.of("America/Sao_Paulo"); ZoneRules rules = sp.getRules(); // get the previous transition (the last one that occurred before 2015-10-18 00:30 in Sao_Paulo timezone ZoneOffsetTransition t = rules.previousTransition(dt.atZone(sp).toInstant()); System.out.println(t);Вывод:
Переход[разрыв в 2015-10-18T00: 00-03: 00 до -02: 00]
Это означает, что есть разрыв (часы движутся вперед) на
2015-10-18T00:00, а смещение изменится с-03:00на-02:00(таким образом, часы перемещаются на 1 час вперед).Вы также можете получить всю эту информацию отдельно:
System.out.println(t.getDateTimeBefore() + " -> " + t.getDateTimeAfter()); System.out.println(t.getOffsetBefore() + " -> " + t.getOffsetAfter());Вывод:
2015-10-18T00: 00 - > 2015-10-18T01: 00
-03:00 -> -02:00Это показывает, что в
00:00часы перемещаются непосредственно в01:00(Поэтому00:30не может существовать). Во второй строке-смещения до и после изменения.
Если вы проверяете переходы в
Los_Angelesчасовой пояс, вы увидите, что его DST начинается и заканчивается в разные даты:ZoneId la = ZoneId.of("America/Los_Angeles"); rules = la.getRules(); // 2015-10-18 00:30 in Los Angeles Instant instant = dt.atZone(la).toInstant(); System.out.println(rules.previousTransition(instant)); System.out.println(rules.nextTransition(instant));Вывод:
Таким образом, в часовом поясеПереход[разрыв в 2015-03-08T02: 00-08: 00 to -07: 00]
Переход[перекрытие в 2015-11-01T02: 00-07: 00 до -08: 00]Los_AngelesDST начинается в2015-03-08и заканчивается в2015-11-01. Вот почему в2015-10-18все часы действительны (нет никакой корректировки, как это происходит вSao_Pauloчасовом поясе).
Некоторые часовые пояса имеют правила перехода (например "DST начинается в третье воскресенье октября" ) вместо просто переходов (например, "DST начинается в эту конкретную дату и Время" ), и вы также можете использовать их, если они доступны:
ZoneId sp = ZoneId.of("America/Sao_Paulo"); ZoneRules rules = sp.getRules(); // hardcoded: Sao_Paulo timezone has 2 transition rules, the second one is relative to October // but you should always check if the list is not empty ZoneOffsetTransitionRule tr = rules.getTransitionRules().get(1); // get the transition for year 2015 ZoneOffsetTransition t = tr.createTransition(2015); // use t the same way as above (the output will be the same)
Еще один способ проверить, допустимы ли дата и время для некоторого часового пояса-использовать метод
ZonedDateTime.ofStrict, который создает исключение, если дата и время недопустимы для часового пояса:ZoneId sp = ZoneId.of("America/Sao_Paulo"); ZoneId la = ZoneId.of("America/Los_Angeles"); LocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30); System.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-7), la)); // OK System.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-3), sp)); // throws java.time.DateTimeExceptionПервый случай в порядке, потому что смещение
-7допустимо для Лос-Анджелеса, для данного дата-время. Во втором случае возникает исключение, поскольку смещение-3недопустимо для Сан-Паулу в заданную дату / время.
Это происходит потому, что выбранное вами время попадает в промежуток между полуночью и 01:00 ночи, когда Бразилия переходит на летнее время. Это время фактически невозможно, и поэтому вы получаете поведение, описанное в документации :
В случае разрыва, когда часы скачут вперед, не существует допустимого смещения. Вместо этого локальная дата-время корректируется, чтобы быть позже по длине промежутка. Для типичного перехода на летнее время на один час местная дата-время будет переместился на час позже в смещение, обычно соответствующее "лету".
Вы можете наблюдать то же самое поведение в зоне Los_Angeles, выбрав время между 02: 00 и 03: 00 в соответствующую ночь Марта:
zdt = ZonedDateTime.of(2015, 3, 8, 2, 30, 0, 0, ZoneId.of("America/Los_Angeles")); System.out.println(zdt);
Comments