Как я могу получить смещение часового пояса без правильного форматирования DST?

Я не нашел способа получить смещение часового пояса без вмешательства летнего времени.

LocalDateTime dt = LocalDateTime.now();
...
ZoneId zone = ZoneId.of(s);
ZonedDateTime zdt = dt.atZone(zone);
ZoneOffset offset = zdt.getOffset();

Казалось бы, это работает, но начальный DataTime может зависеть от летнего времени.

Есть еще вот это:

int rawOffset = tZone.getRawOffset();

Однако это всегда положительное число, поэтому мы никогда не вернём -05:00.

Итак, я хочу получить смещение без вмешательства DST. Есть предположения?


person markthegrea    schedule 06.06.2017    source источник
comment
Я не понимаю, чего вы хотите добиться. Зона не имеет фиксированного смещения. Смещение зависит от даты.   -  person JB Nizet    schedule 06.06.2017
comment
В качестве примечания: getRawOffset() близок к тому, что вы хотите, и обязательно должен возвращать отрицательные значения, где это уместно. Разница в том, что вы можете не получить из этого исторически правильные (или даже релевантные) значения.   -  person Joachim Sauer    schedule 06.06.2017
comment
Чего вы пытаетесь достичь? Похоже, вы пытаетесь пересчитать что-то самостоятельно, что, вероятно, вместо этого должно обрабатываться каким-то классом java.time. Для чего вам нужна эта информация?   -  person Joachim Sauer    schedule 06.06.2017
comment
Если я правильно понял, вы хотите знать, каково смещение часового пояса, когда сейчас не летнее время, верно? И это должно быть независимо от текущей даты?   -  person    schedule 06.06.2017
comment
Вы совершенно правы, @Hugo.   -  person markthegrea    schedule 06.06.2017
comment
Не существует такой вещи, как "смещение часового пояса, если сейчас не летнее время", "независимо от текущей даты". Часовой пояс по определению представляет собой историческую коллекцию прошлых, настоящих и * будущих изменений смещения относительно UTC, используемого конкретным регионом. Таким образом, не существует такого понятия, как «нормальное смещение», базовое смещение, которое вы, кажется, ищете. Ваш вопрос может быть связан с общим фундаментальным непониманием часовых поясов: они ненастоящие, не в том смысле, что они естественны. Зоны и смещения создаются людьми, в частности капризными политиками, которые часто меняют правила.   -  person Basil Bourque    schedule 07.06.2017
comment
Не могли бы вы рассматривать DST как модификацию базового часового пояса? Я не могу найти записи об изменении часового пояса с тех пор, как он был установлен в Америке. Однако летнее время постоянно меняется. Вы говорите, что «нормального смещения» не существует, но оно есть. Что еще вы бы назвали ZoneRules.getStandardOffset(Instant) (ответ ниже)?   -  person markthegrea    schedule 07.06.2017
comment
@markthegrea Изменение базового (не летнего) часового пояса не является обычным явлением, но это также происходит, особенно до 1900 года, когда в каждом городе было свое местное время, а затем было решено перейти на некоторое смещение UTC. Дело в том, что это может произойти в любое время и в любом месте, потому что это зависит от правительств и законов, а не от логических причин. Но я понимаю вашу точку зрения, это настолько редкое событие, что мы его даже не рассматриваем (а должны).   -  person    schedule 07.06.2017
comment
@markthegrea ZoneRules.getStandardOffset(Instant) получает стандартное смещение в определенный момент (поскольку стандарт может меняться со временем, вам всегда нужен Instant в качестве эталона) - только потому, что сегодня он один, это не значит всегда было так в прошлом. Это также не означает, что в будущем всегда будет так: если завтра какое-то правительство решит изменить часовой пояс страны/штата/региона/чего-то еще, нам всем придется обновить наши системы с помощью этого нового правила ( и getStandardOffset для Instant в будущем вернет это новое смещение).   -  person    schedule 07.06.2017
comment
Просто дополняю: в Бразилии штат Акко имел стандартное смещение -05 без перехода на летнее время. С 1986 по 1988 год у них стали переходить на летнее время (стандарт -05, летнее время -04), с 1989 по 2008 год просто стандартное -05 без летнего времени, затем с 2009 по 2013 год стандарт изменился на -04 (без летнего времени), а с 2014 года стандарт вернулся к -05 (без перехода на летнее время). И это не единственный случай — вы можете найти больше здесь. И всегда где-то есть правительство, желающее изменить стандартный зачет. Таким образом, даже для получения стандартного смещения вам понадобится Instant в качестве ссылки.   -  person    schedule 07.06.2017
comment
Так нужно ли обновлять JVM, чтобы получить эту информацию? Я имею в виду, очевидно, что это так, но я никогда не слышал об этом как об обновлении! Должна быть в обычной версии. Отличная дискуссия!   -  person markthegrea    schedule 09.06.2017
comment
Есть инструмент для его обновления: oracle.com/technetwork/java /javase/tzupdater-readme-136440.html, а также список последних обновлений: oracle.com/technetwork/java/javase/tzdata-versions-138805.html   -  person    schedule 09.06.2017
comment
Инструмент tzupdater можно использовать, если вы не можете (или не хотите) обновлять свою версию Java, но, конечно, новые обновления информации о часовом поясе также включены в более новые версии JVM. Например, в Java SE 8u131 b32 файл включен обновленный tzdata2017b   -  person    schedule 09.06.2017


Ответы (3)


Используйте ZoneRules.getStandardOffset(Instant) .

ZoneId zone = ZoneId.of(s);
ZoneRules rules = zone.getRules();
ZoneOffset standardOffset = rules.getStandardOffset(Instant.now());
person Joachim Sauer    schedule 06.06.2017
comment
Но вы используете Instant.now(), на который влияет переход на летнее время. - person markthegrea; 06.06.2017
comment
Instant.now() представляет текущее время без знания летнего времени или часовых поясов, поэтому оно не может быть испорчено летним временем (за исключением случаев, когда ваши местные часы неверны из-за махинаций с часовыми поясами, тогда все ставки в любом случае отключены). - person Joachim Sauer; 06.06.2017
comment
Итак, я поиграл с этим, и, похоже, это то, что я хочу. Однако можете ли вы объяснить, почему Instant.now() вообще необходим? Почему любая дата должна найти смещение часового пояса от UTC? Почему дата должна иметь значение? - person markthegrea; 06.06.2017
comment
Момент можно использовать, если вам нужна «историческая информация о том, как стандартное смещение менялось с течением времени», чтобы процитировать документацию. Много раз в истории страна или регион меняли свое стандартное смещение. Я считаю, что когда я был ребенком, Испания была в другой зоне, чем моя, но сейчас она в той же зоне. - person Ole V.V.; 06.06.2017
comment
Этот ответ содержит мой любимый фрагмент кода среди ответов. В нем нет моего любимого объяснения. Мы могли бы использовать больше последнего, пожалуйста… - person Ole V.V.; 07.06.2017

TimeZone.getRawOffset() вернет правильное историческое смещение в соответствии с документацией Java API.

общедоступная абстракция int getRawOffset()

Возвращает количество времени в миллисекундах, которое нужно добавить к UTC, чтобы получить стандартное время в этом часовом поясе. Поскольку на это значение не влияет переход на летнее время, оно называется необработанным смещением.

Если базовый подкласс реализации TimeZone поддерживает исторические изменения смещения по Гринвичу, метод возвращает необработанное значение смещения текущей даты. В Гонолулу, например, его исходное смещение изменилось с GMT-10:30 на GMT-10:00 в 1947 году, и этот метод всегда возвращает -36000000 миллисекунд (т. е. -10 часов).

Возвращает: количество необработанного времени смещения в миллисекундах, которое нужно добавить к UTC. См. также: Calendar.ZONE_OFFSET

из: https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getRawOffset()

person Ren Wilson    schedule 06.06.2017
comment
Нужно ли нам нужно использовать устаревший класс TimeZone? Есть ли решение с современными классами? - person Ole V.V.; 06.06.2017

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

Вот почему API требует Instant в качестве ссылки: чтобы узнать, какое было стандартное смещение в этот конкретный момент (независимо от того, находится ли Instant в прошлом, настоящем или будущем). Вы не можете сказать, каково смещение, не зная, когда вы вовремя. И именно поэтому ответ @Joachim является правильным.

Но, как вы сказали в комментариях, что не хотите зависеть от Instant.now(), я сделал код ниже - и имейте в виду, что решение не на 100% идеально по причинам, указанным выше. Это просто лучшее, что у меня есть по вашим критериям.


Вы можете сделать это, используя класс java.time.zone.ZoneRules, который вы можете получить непосредственно из своего экземпляра ZoneId:

// pick a timezone with DST
ZoneId zone = ZoneId.of("America/Sao_Paulo");
// get rules
ZoneRules rules = zone.getRules();
// check if offset changes over time (if it returns false, it means is not fixed, so it has DST)
System.out.println(rules.isFixedOffset()); // false

В приведенном выше коде rules.isFixedOffset() возвращает false, что означает, что смещение соответствующего часового пояса меняется со временем (таким образом, у него есть изменения DST).

Чтобы узнать, когда происходят эти изменения летнего времени, вы должны получить все переходы:

// get all transitions (all dates where DST starts or ends)
List<ZoneOffsetTransition> transitions = rules.getTransitions();
transitions.stream().forEach(System.out::println);

Это печатает много строк (поскольку в этом часовом поясе почти каждый год летнее время). Вот пример последних двух строк:

Переход[Перекрытие с 00:00-02:00 2039-02:00 до -03:00
Переход[Пробел с 00:00-03:00-2039-03:00 до -02:00]

Это означает, что в 20.02.2039 в полночь смещение изменится с -02:00 на -03:00 (конец летнего времени - часы переводятся на 1 час назад), а в 16.10.2039 в полночь смещение изменится с -03:00 на -02:00 (переход на летнее время — часы переводятся на 1 час вперед).

Итак, если вы хотите узнать смещение, когда это не летнее время, вы просто получаете последний переход, проверяете, является ли он разрывом или перекрытием, и получаете смещение после или до:

// get the last transition
ZoneOffsetTransition last = transitions.get(transitions.size() - 1);

// find the offset without DST interference
ZoneOffset offsetWithoutDST;

// overlap means that clock moves back 1 hour (when DST ends) 
if(last.isOverlap()) {
    // DST ends, get the offset after it
    offsetWithoutDST = last.getOffsetAfter();
} else {
    // DST starts, get the offset before it
    offsetWithoutDST = last.getOffsetBefore();
}
System.out.println(offsetWithoutDST);

В этом случае он печатает -03:00, что является «нормальным» смещением для часового пояса America/Sao_Paulo (когда не летнее время).


PS: обратите внимание, что летнее и даже стандартное смещения могут сильно меняться с течением времени.

Тот факт, что смещение сегодня одно, не означает, что оно всегда было одним и тем же: правительства могут изменить смещение в любое время, и в очень прошлые даты — например, до 1900 года — смещения были гораздо более беспорядочными, чем сегодня (в каждом городе свое время, со смещениями типа -03:06:28 и т. д.).

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

person Community    schedule 06.06.2017
comment
Вы внимательно изучили! Хороший. Каковы преимущества вашего подхода по сравнению с двумя другими ответами? И если для зоны я не могу найти переход в 1987 году — ну, я полагаю, это просто означает, что я могу выбрать любой момент в этом году и запросить его смещение, вы согласны? - person Ole V.V.; 06.06.2017
comment
Кроме того, можем ли мы быть уверены, что переход всегда является переходом на летнее время или обратно? Как минимум тоже должен быть переход при изменении стандартного зачета в связи с каким-то изменившимся законодательством? - person Ole V.V.; 06.06.2017
comment
Это отличная информация! Я поражен тем, что в JVM есть все эти переходы. Однако я считаю, что ответ Иоахима Зауэра намного проще. Спасибо за образование! - person markthegrea; 06.06.2017
comment
@ОлеВ.В. ОП сказал, что не хочет зависеть от Instant.now(), и чтобы получить текущее стандартное смещение, подход последнего перехода - лучшее, что я мог получить. Я знаю, что это не идеальное решение, потому что смещения меняются со временем, и в идеале всегда иметь ссылку на дату/время. Таким образом, это решение является наилучшим из возможных для получения текущего стандартного смещения без зависимости от Instant.now(), но для получения смещения в прошлом, конечно, вам нужна ссылка на дату/время, и я допускаю свое решение. не идеален. - person ; 07.06.2017
comment
И согласно javadoc: A переход между двумя смещениями обычно является результатом перехода на летнее время. Таким образом, это не гарантируется на 100%, но после того, как произошло UTC, мы можем предположить, что большую часть времени они связаны с переходом на летнее время. А когда это не так, мне интересно, как API решает, что является стандартом. - person ; 07.06.2017
comment
@markthegrea Пожалуйста, имейте в виду, что это решение не идеально (я обновил ответ, объясняя это), это просто лучшее, что у меня есть, не завися от Instant.now() (как вы сказали в комментариях). Но ответ Иоахима правильный, потому что вы всегда должны иметь Instant в качестве ссылки, чтобы знать, что такое смещение. И я также должен признать, что я закончил этот код просто для вызова... - person ; 07.06.2017