Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a running JVM detect a change to the computer's timezone?

Tags:

java

jvm

I can't find any specific docs to answer this question.

I wrote some simple test code to work out what actually happens on Java 1.8 on OS X 10.12:

public static void main(String[] _args) throws InterruptedException {
    while (true) {
        int calendarTimezoneOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
        System.out.println("calendarTimezoneOffset = " + calendarTimezoneOffset);

        ZoneOffset offset = ZonedDateTime.now().getOffset();
        System.out.println("offset = " + offset);

        Thread.sleep(1000);
    }
}

Both the old way (Calendar) and the new way (Java 8's Date and Time library) do not detect any change I make to the OS's timezone while the JVM is running. I need to stop and start the code to get the changed timezone.

Is this by design? Is this reliable behaviour across JVM implementations and operating systems?

like image 997
Steve McLeod Avatar asked Aug 12 '16 08:08

Steve McLeod


2 Answers

TimeZone.getDefault() specification is pretty clear about how it gets the time zone:

Gets the default TimeZone of the Java virtual machine. If the cached default TimeZone is available, its clone is returned. Otherwise, the method takes the following steps to determine the default time zone.

  • Use the user.timezone property value as the default time zone ID if it's available.
  • Detect the platform time zone ID. The source of the platform time zone and ID mapping may vary with implementation.
  • Use GMT as the last resort if the given or detected time zone ID is unknown.

The default TimeZone created from the ID is cached, and its clone is returned. The user.timezone property value is set to the ID upon return.

The documentation states that the zone is cached; this method is affected by user.timezone system property and vice versa.

The specification also says that the cache is cleared by TimeZone.setDefault(null):

If zone is null, the cached default TimeZone is cleared.

That is, in order to re-read the system time zone, you have to

  1. Clear TimeZone cache by calling TimeZone.setDefault(null);
  2. Clear user.timezone system property by calling System.clearProperty("user.timezone");

Try the following test:

    while (true) {
        TimeZone.setDefault(null);
        System.clearProperty("user.timezone");

        System.out.println("Offset = " + TimeZone.getDefault().getRawOffset() / 3600);
        System.out.println("Zone ID = " + System.getProperty("user.timezone"));

        Thread.sleep(1000);
    }
like image 183
apangin Avatar answered Sep 17 '22 23:09

apangin


I search for some of the JVM documentation related to this issue, but there isn't a thing which mentions that changes to the underlying OS are propagated to the JVM.

I think the TimeZone class holds the answer. If you look in it, you will see that there is a private variable called defaultTimeZone which holds the timezone which is used by default by the Date/Calendar instances. This variable is set in the methods setDefaultZone and setDefault.

The setDefaultZone method is private and called only from the getDefaultRef which is called from the Date/Calendar constructors. Here is the code from it:

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

From this method, is obvious that any new instances of Date/Calendar will use an already set calendar instance and won't check if it's the same as the one the OS uses.

The setDefault method is pretty straighforward, it just sets the defaultTimeZone variable to a new value.

public static void setDefault(TimeZone zone) {
   SecurityManager sm = System.getSecurityManager();
   if (sm != null) {
      sm.checkPermission(new PropertyPermission("user.timezone", write"));
   }
   defaultTimeZone = zone;
}

Reading the documentation for this method you will read this statement: If zone is null, the cached default TimeZone is cleared. This means that future calls to the getDefault or getDefaultRef will retrieve the latest OS timezone from reading it first from the user.timezone property. If this property is empty, it will read the actual TimeZone of the system.

Knowing this things, I think it's safe to assume that changes to the OS timezone aren't propagated to the default TimeZone since that value is cached and never updated unless the client wishes to do it. The way I see it, there are two possible ways to do it:

  1. Use the TimeZone.setDefault method while passing a desired timezone as the new default timezone
  2. Use the following code to set it to the value your system uses:

    System.setProperty("user.timezone", ""); TimeZone.setDefault(null);

like image 24
Slimu Avatar answered Sep 18 '22 23:09

Slimu