Java读取系统默认时区

工作中,遇到一个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

 

如有不正确的地方,欢迎指正!



posted @ 2018-07-25 20:44  远去的列车  阅读(14917)  评论(1编辑  收藏  举报