Java 可以通過 Timezone 獲取時區,但是通過 Timezone 獲取的時區是 JVM 初始化時保存的時區,並不是操作系統所設置的時區。當修改過操作系統的時區后,JVM 並不會同步更新。Timezone 獲取時區的代碼如下:
// 獲取 JVM 啟動時獲取的時區
TimeZone.getDefault();
// 獲取任意指定區域的時區
String[] zoneIDs = TimeZone.getAvailableIDs();
for(String zoneID: zoneIDs) {
TimeZone.getTimeZone(zoneID);
}
當修改了操作系統的時區后,但JVM 並不會同步更新,因此直接通過 Timezone 獲取默認時區並不是修改后的時區。若要程序獲取修改后的操作系統時區,則可以這樣修改:
// 將獲取默認時區的兩個前置條件設置為 false, 令其獲取系統時間,原理見后面分析
synchronized (TimeZone.class) {
TimeZone.setDefault(null);
System.setProperty("user.timezone", "");
TimeZone.getDefault();
}
這么做的原因是,我們需要調用Timezone 的本地方法 getSystemTimeZoneID(String javaHome),請看源碼
/**
* Returns the reference to the default TimeZone object. This
* method doesn't create a clone.
*/
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;
}
private static synchronized TimeZone setDefaultZone() {
TimeZone tz;
// get the time zone ID from the system properties
String zoneID = AccessController.doPrivileged(
new GetPropertyAction("user.timezone"));
// if the time zone ID is not set (yet), perform the
// platform to Java time zone ID mapping.
if (zoneID == null || zoneID.isEmpty()) {
String javaHome = AccessController.doPrivileged(
new GetPropertyAction("java.home"));
try {
zoneID = getSystemTimeZoneID(javaHome); // 重點,這個方法就是我們需要調用的,但是 Timezone 並沒有對外提供接口訪問該方法,因此只能將前置條件改為 false, 令程序調用該方法即可獲取操作系統的時間。
if (zoneID == null) {
zoneID = GMT_ID;
}
} catch (NullPointerException e) {
zoneID = GMT_ID;
}
}
// Get the time zone for zoneID. But not fall back to
// "GMT" here.
tz = getTimeZone(zoneID, false);
if (tz == null) {
// If the given zone ID is unknown in Java, try to
// get the GMT-offset-based time zone ID,
// a.k.a. custom time zone ID (e.g., "GMT-08:00").
String gmtOffsetID = getSystemGMTOffsetID();
if (gmtOffsetID != null) {
zoneID = gmtOffsetID;
}
tz = getTimeZone(zoneID, true);
}
assert tz != null;
final String id = zoneID;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
System.setProperty("user.timezone", id);
return null;
}
});
defaultTimeZone = tz;
return tz;
}
轉自:https://blog.csdn.net/zhaoxj_2017/article/details/99676852
