工作中,遇到一个Java读取默认时区的问题,后来看了openjdk的源码,大致整理一下过程
public class Test { public void test(){ TimeZone.getDefault(); } }
TimeZone.getDefault()会跳到下面代码:
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); 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; }
如果没有设置时区的话,会进入一个native方法
zoneID = getSystemTimeZoneID(javaHome);
这个方法的实现,可以参考
openjdk-8u40-src-b25-10_feb_2015\openjdk\jdk\src\share\native\java\util\TimeZone.c
JNIEXPORT jstring JNICALL Java_java_util_TimeZone_getSystemTimeZoneID(JNIEnv *env, jclass ign, jstring java_home) { const char *java_home_dir; char *javaTZ; jstring jstrJavaTZ = NULL; CHECK_NULL_RETURN(java_home, NULL); java_home_dir = JNU_GetStringPlatformChars(env, java_home, 0); CHECK_NULL_RETURN(java_home_dir, NULL); /* * Invoke platform dependent mapping function */ javaTZ = findJavaTZ_md(java_home_dir); if (javaTZ != NULL) { jstrJavaTZ = JNU_NewStringPlatform(env, javaTZ); free((void *)javaTZ); } JNU_ReleaseStringPlatformChars(env, java_home, java_home_dir); return jstrJavaTZ; }
主要看:
javaTZ = findJavaTZ_md(java_home_dir);
继续参考,下面TimeZone_md.c文件,可以知道find_JavaTZ_md方法的实现
openjdk-8u40-src-b25-10_feb_2015\openjdk\jdk\src\solaris\native\java\util\TimeZone_md.c
char * findJavaTZ_md(const char *java_home_dir) { char *tz; char *javatz = NULL; char *freetz = NULL; tz = getenv("TZ"); #if defined(__linux__) || defined(_ALLBSD_SOURCE) if (tz == NULL) { #else #if defined (__solaris__) || defined(_AIX) if (tz == NULL || *tz == '\0') { #endif #endif tz = getPlatformTimeZoneID(); freetz = tz; } /* * Remove any preceding ':' */ if (tz != NULL && *tz == ':') { tz++; } #ifdef __solaris__ if (tz != NULL && strcmp(tz, "localtime") == 0) { tz = getSolarisDefaultZoneID(); freetz = tz; } #endif if (tz != NULL) { #ifdef __linux__ /* * Ignore "posix/" prefix. */ if (strncmp(tz, "posix/", 6) == 0) { tz += 6; } #endif javatz = strdup(tz); if (freetz != NULL) { free((void *) freetz); } #ifdef _AIX freetz = mapPlatformToJavaTimezone(java_home_dir, javatz); if (javatz != NULL) { free((void *) javatz); } javatz = freetz; #endif } return javatz; }
tz = getPlatformTimeZoneID(),这个函数内容,就不贴了,可以自己看下,总计起来,在Linux系统上,大概过程为以下几步:
1.先找“TZ”变量,没有,到2,
2.读/etc/timezone,没有到3,
3.比较/etc/localtime文件与"/usr/share/zoneinfo目录下所有时区文件,如果有一致的,就为该时区,如果没有,到4,
4.默认为标准GMT
如有不正确的地方,欢迎指正!