Выполняет ли запущенная JVM изменение в часовом поясе компьютера?
Я не могу найти какие-либо конкретные документы, чтобы ответить на этот вопрос.
Я написал несколько простых тестовых кодов для разработки того, что на самом деле происходит на Java 1.8 на OS X 10.12:
public static void main(String[] _args) throws InterruptedException {
while (true) {
int calendarTimezoneOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
System.out.println("calendarTimezoneOffset = " + calendarTimezoneOffset);
ZoneOffset offset = ZonedDateTime.now().getOffset();
System.out.println("offset = " + offset);
Thread.sleep(1000);
}
}
Как старый способ (календарь), так и новый способ (Java 8 Date and Time library) не обнаруживают никаких изменений, которые я вношу в часовой пояс ОС во время работы JVM. Мне нужно остановить и запустить код, чтобы получить измененный часовой пояс.
Это по дизайну? Является ли это надежным поведением в реализациях JVM и операционных системах?
Ответы
Ответ 1
TimeZone.getDefault()
спецификация довольно ясно о том, как она получает часовой пояс:
Получает значение по умолчанию TimeZone виртуальной машины Java. Если кешированный доступен TimeZone по умолчанию, возвращается его клон. В противном случае метод принимает следующие шаги для определения часового пояса по умолчанию.
- Используйте значение свойства user.timezone как идентификатор часового пояса по умолчанию, если оно доступно.
- Обнаружение идентификатора часового пояса платформы. Источник отображения часового пояса платформы и сопоставления идентификаторов может отличаться в зависимости от реализации.
- Использовать GMT в качестве последнего средства, если данный или обнаруженный идентификатор часового пояса неизвестен.
По умолчанию TimeZone, созданный из идентификатора, кэшируется, а его клон вернулся. Значение свойства user.timezone устанавливается равным ID вернуться.
В документации указано, что зона кэширована; на этот метод влияет системное свойство user.timezone и наоборот.
В спецификации также говорится, что кеш очищается TimeZone.setDefault(null)
:
Если зона имеет значение NULL, кешированный по умолчанию TimeZone очищается.
То есть, чтобы перечитать системный часовой пояс, вы должны
- Очистить кеш тайм-кода, вызывая
TimeZone.setDefault(null)
;
- Очистить системное свойство
user.timezone
, вызвав System.clearProperty("user.timezone");
Попробуйте выполнить следующее тестирование:
while (true) {
TimeZone.setDefault(null);
System.clearProperty("user.timezone");
System.out.println("Offset = " + TimeZone.getDefault().getRawOffset() / 3600);
System.out.println("Zone ID = " + System.getProperty("user.timezone"));
Thread.sleep(1000);
}
Ответ 2
Я ищу некоторые из документации JVM, связанные с этой проблемой, но нет ничего, что упоминает, что изменения в базовой ОС распространяются на JVM.
Я думаю, что класс TimeZone
содержит ответ. Если вы посмотрите на него, вы увидите, что есть приватная переменная с именем defaultTimeZone
, которая содержит часовой пояс, который по умолчанию используется экземплярами Date/Calendar. Эта переменная устанавливается в методах setDefaultZone
и setDefault
.
Метод setDefaultZone
является приватным и вызывается только из getDefaultRef
, который вызывается из конструкторов Date/Calendar. Вот код из него:
static TimeZone getDefaultRef() {
TimeZone defaultZone = defaultTimeZone;
if (defaultZone == null) {
// Need to initialize the default time zone.
defaultZone = setDefaultZone();
assert defaultZone != null;
}
// Don't clone here.
return defaultZone;
}
Из этого метода очевидно, что любые новые экземпляры Date/Calendar будут использовать уже установленный экземпляр календаря и не будут проверять, совпадает ли он с тем, который использует ОС.
Метод setDefault
довольно прост, он просто устанавливает переменную defaultTimeZone
в новое значение.
public static void setDefault(TimeZone zone) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new PropertyPermission("user.timezone", write"));
}
defaultTimeZone = zone;
}
Считывая документацию по этому методу, вы прочтете это утверждение: If zone is null, the cached default TimeZone is cleared
. Это означает, что будущие вызовы getDefault
или getDefaultRef
будут извлекать последнюю часовую зону ОС из ее первой записи из свойства user.timezone
. Если это свойство пустое, оно будет считывать фактический часовой пояс системы.
Зная это, я думаю, что можно с уверенностью предположить, что изменения в часовом поясе OS не распространяются на TimeZone по умолчанию, поскольку это значение кэшируется и никогда не обновляется, если клиент не хочет этого делать. Как я вижу это, есть два возможных способа сделать это:
- Используйте метод
TimeZone.setDefault
при передаче желаемого часового пояса в качестве нового часового пояса по умолчанию
-
Используйте следующий код, чтобы установить его значение, которое использует ваша система:
System.setProperty("user.timezone", "");
TimeZone.setDefault(null);