美文网首页
使用openjdk的时区未设置,引发了线上系统的诡异问题

使用openjdk的时区未设置,引发了线上系统的诡异问题

作者: 天草二十六_简村人 | 来源:发表于2022-07-14 16:36 被阅读0次

    一、使用openjdk作为docker 镜像

    • 建议在环境变量中指定时区
    apk add tzdata && \
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone && \
    apk del tzdata && \
    
    
    • 也可以在java -jar命令行指定时区
    java -jar -Duser.timezone=Asia/Shanghai app.jar
    
    

    二、详解openjdk的获取时区

    3.1、java程序中获取默认时间

    import java.util.TimeZone;
    
    public class Test {
        public static void main(String[] args) {
            System.out.println(TimeZone.getDefault());
        }
    }
    
    >javac Test.java
    >java Test
    sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
    

    如果是oracle jdk,打印的时区详情是:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null]

    3.2、openjdk的源码跟踪

    类TimeZone

    package java.util;
    
    // defaultTimeZone 使用关键词volatile修饰
    private static volatile TimeZone defaultTimeZone;
    
    // 默认的时区就是GMT
    static final String         GMT_ID        = "GMT";
    
       public static TimeZone getDefault() {
            return (TimeZone) getDefaultRef().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;
        }
    
    

    jar命令行参数-Duser.timezone=GMT+8

    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;
        }
    
    public static void setDefault(TimeZone zone)
        {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                sm.checkPermission(new PropertyPermission
                                   ("user.timezone", "write"));
            }
            defaultTimeZone = zone;
        }
    

    3.3、TimeZone.c类中,会读取系统的时区

    /*
     * Gets the platform defined TimeZone ID
     */
    JNIEXPORT jstring JNICALL
    Java_java_util_TimeZone_getSystemTimeZoneID(JNIEnv *env, jclass ign,
                                                jstring java_home, jstring country)
    {
        const char *cname;
        const char *java_home_dir;
        char *javaTZ;
    
        if (java_home == NULL)
            return NULL;
    
        java_home_dir = JNU_GetStringPlatformChars(env, java_home, 0);
        if (java_home_dir == NULL)
            return NULL;
    
        if (country != NULL) {
            cname = JNU_GetStringPlatformChars(env, country, 0);
            /* ignore error cases for cname */
        } else {
            cname = NULL;
        }
    
        /*
         * Invoke platform dependent mapping function
         */
        javaTZ = findJavaTZ_md(java_home_dir, cname);
    
        free((void *)java_home_dir);
        if (cname != NULL) {
            free((void *)cname);
        }
    
        if (javaTZ != NULL) {
            jstring jstrJavaTZ = JNU_NewStringPlatform(env, javaTZ);
            free((void *)javaTZ);
            return jstrJavaTZ;
        }
        return NULL;
    }
    

    3.4、findJavaTZ_md(java_home_dir, cname);的不同操作系统实现

    • unix操作系统
    • windows操作系统


      solaris系统和windows系统.png

    相关文章

      网友评论

          本文标题:使用openjdk的时区未设置,引发了线上系统的诡异问题

          本文链接:https://www.haomeiwen.com/subject/pexfirtx.html