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, Как мы установили.

Почему они разные?

654   2  

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_Angeles DST начинается в 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

    Ничего не найдено.