我找不到任何具体的文档来回答这个问题。
我写了一些简单的测试代码来解决OS X 10.12上Java 1.8实际发生的事情:
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的日期和时间库)都没有检测到我在JVM运行时对操作系统的时区所做的任何更改。我需要停止并启动代码以获取更改的时区。
这是设计的吗?这是跨JVM实现和操作系统的可靠行为吗?
答案 0 :(得分:8)
TimeZone.getDefault()
规范很清楚如何获得时区:
获取Java虚拟机的默认TimeZone。如果缓存了 默认TimeZone可用,返回其克隆。否则, 方法采用以下步骤来确定默认时区。
- 使用user.timezone属性值作为默认时区ID(如果可用)。
- 检测平台时区ID。平台时区和ID映射的来源可能因实施而异。
- 如果给定或检测到的时区ID未知,请使用GMT作为最后的手段。
从ID创建的默认TimeZone被缓存,其克隆是 回。 user.timezone属性值设置为ID 返回。
文档说明该区域已缓存;此方法受 user.timezone 系统属性的影响,反之亦然。
规范还说明TimeZone.setDefault(null)
清除了缓存:
如果zone为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);
}
答案 1 :(得分:3)
我搜索了一些与此问题相关的JVM文档,但是没有提到底层操作系统的更改会传播到JVM。
我认为TimeZone
类有答案。如果你查看它,你会看到有一个名为defaultTimeZone
的私有变量,它保存了Date / Calendar实例默认使用的时区。此变量在方法setDefaultZone
和setDefault
中设置。
setDefaultZone
方法是私有的,只能从日期/日历构造函数调用的getDefaultRef
调用。以下是代码:
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;
}
从这种方法来看,显然日期/日历的任何新实例都将使用已设置的日历实例,并且不会检查它是否与操作系统使用的实例相同。
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
属性中首先读取最新的操作系统时区。如果此属性为空,则它将读取系统的实际TimeZone。
知道这些事情,我认为可以安全地假设对操作系统时区的更改没有传播到默认的TimeZone,因为该值被缓存并且永远不会更新,除非客户端希望这样做。我看到它的方式,有两种可能的方法:
TimeZone.setDefault
方法使用以下代码将其设置为系统使用的值:
System.setProperty("user.timezone", "");
TimeZone.setDefault(null);