Timezone in JVM

I wrote a Scala code to get the current time. However, the output is different on the development server and docker.

import java.util.Calendar

println(Calendar.getInstance().getTime)

On my development server, it outputs Sun Oct 18 18:01:01 CST 2020, but in docker, it print a UTC time.

I guess it related to the timezone setting and do a research, here is the result.

How Did JVM Detect Timezone

All of the code can be found in this function: private static synchronized TimeZone setDefaultZone()

  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;
      }
}

First, it will check whether JVM has user.timezone property. If not, it will call this native method getSystemTimeZoneID, it was implemented in java.base/share/native/libjava/TimeZone.c, and the main logic is in java.base/unix/native/libjava/TimeZone_md.c.

In Timezone_md.c, it will find timezone by following steps, it will return the timezone immediately once found.

  1. Find TZ environment.
  2. Read /etc/timezone.
  3. Read /etc/localtime. If it is a soft link(ex: /usr/share/zoneinfo/Asia/Shanghai), return timezone by path. Otherwise, compare the content with all files in /usr/share/zoneinfo, if found, return timezone.
  4. Return GMT as timezone.

How to Change Timezone

The available timezone in Linux can be listed by this command: timedatectl list-timezones

Add JVM param

You can add -Duser.timezone=Asia/Shanghai as JVM parameters.

Set TZ environment variable

Add export TZ=Asia/Shanghai in .bashrc.

Change /etc/timezone

Set its content to Asia/Shanghai

Change /etc/localtime

Link it to /usr/share/zoneinfo/Asia/Shanghai

Change timezone manually in Java Program

All of these methods should work

  • Add this line before get time: TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))
  • Set JVM property by code System.setProperty("user.timezone", "Asia/Shanghai")
  • Set timezone manually in Calendar Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"))

Ref:

  1. How to Set the JVM Time Zone
  2. jvm linux 时区设置
  3. Java default timezone detection, revisited
  4. Java读取系统默认时区
  5. How to set a JVM TimeZone Properly
comments powered by Disqus