JDK-6352812 : (tz) REGRESSION: JDK 1.5 TimeZone.setDefault() inconsistent with JDK 1.4 and Mustang
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2005-11-18
  • Updated: 2011-02-16
  • Resolved: 2005-11-18
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
In JDK 1.4 the default time zone attribute of the TimeZone class was stored as a static variable so if TimeZone.setDefault() was invoked, any subsequent invocation of TimeZone.getDefault() on any thread would return the new default value.

In JDK 1.5 it seems the default time zone attribute is now stored as a thread local variable (InheritableThreadLocal).  This significantly alters the behavior of TimeZone.setDefault() so the scope is only on the thread on which it is invoked or any child threads thence created.

This causes a problem in our application as follows.

When our java application starts up, a login panel is displayed where the user inputs his user name and password.  This panel is displayed via Swing and the user input is processed on the awt event thread.  The main thread of execution then uses the user name to read the user's application defaults, one of which is their default time zone.  The main thread of execution then uses this setting and invokes TimeZone.setDefault().

The problem is that with since the TimeZone.setDefault() was executed on the main thread after the AWT event thread was intialized, any call to TimeZone.getDefault() on the AWT event thread has the original default read from the java system properties while the main thread of execution has the new value read from the user's application defaults.  Any subsequent activity on the AWT event thread (i.e. any user action) is therefore peformed using the wrong time zone.  In our case we have a user in Chicago performing activities for our Seoul office.  His application winds up in the state where the main thread has "Asia/Seoul" as his default time zone while the AWT event thread has a default time zone of  "America/Chicago".

This behavior is a significant departure from the JDK 1.4 where TimeZone.setDefault() could be invoked on the main thread of activity and the AWT event thread would pick up those changes from TimeZone.getDefault().

This problem isn't limited the the AWT event thread.  Any thread initialized prior to the TimeZone.setDefault() invocation on the main thread is vulnerable.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the included source code, TimeZoneTest.java.  You will see different results for the JDK 1.4 and JDK 1.5.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
For JDK 1.4 we get the following results (see TimeZoneTest):

---------- BEFORE TimeZone.setDefault() ----------
Thread:main> default time zone is:America/Chicago
Thread:AWT-EventQueue-0> default time zone is:America/Chicago
---------- AFTER TimeZone.setDefault() ----------
Thread:main> default time zone is:Asia/Seoul
Thread:AWT-EventQueue-0> default time zone is:Asia/Seoul
ACTUAL -
For JDK 1.5 we get the following results (see TimeZoneTest):

---------- BEFORE TimeZone.setDefault() ----------
Thread:main> default time zone is:America/Chicago
Thread:AWT-EventQueue-0> default time zone is:America/Chicago
---------- AFTER TimeZone.setDefault() ----------
Thread:main> default time zone is:Asia/Seoul
Thread:AWT-EventQueue-0> default time zone is:America/Chicago

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/**
 * @(#)TimeZoneTest.java
 */

import java.util.TimeZone;

import javax.swing.SwingUtilities;

/**
 * TimeZoneTest tests a new "feature" of JDK 1.5 in that TimeZone.setDefault()
 * is limited in scope to the thread on which it is invoked and any child
 * threads created thence.
 */
public class TimeZoneTest
{
  /**
   * @param args not used
   */
  public static void main(String[] args)
  {
    TimeZoneTest timeZoneTest = new TimeZoneTest();
    timeZoneTest.runTest();
  }
  
  /**
   * Prints the default time zone on the main thread and the AWT event thread,
   * sets the default time zone on the main thread and then prints the default
   * time zone on the main thread and AWT event thread again.
   */
  public void runTest()
  {
    try
    {
      // Print the default time zone on the main thread and AWT event thread
      System.out.println("---------- BEFORE TimeZone.setDefault() ----------");
      printDefaultTimeZone();
      Runnable swingWorker = new Runnable()
      {
        public void run()
        {
          printDefaultTimeZone();
        }
      };
      SwingUtilities.invokeAndWait(swingWorker);
      
      // Now set the main thread's default time zone to Seoul.
      // In JDK 1.4, this would set the default time zone in the
      // AWT event thread too.
      TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
      
      // Now you will see that the two threads have different values
      System.out.println("---------- AFTER TimeZone.setDefault() ----------");
      printDefaultTimeZone();
      SwingUtilities.invokeAndWait(swingWorker);
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }
  }
  
  /**
   * Prints the default time zone ID and the ID of the current thread.
   */
  public void printDefaultTimeZone()
  {
    System.out.println("Thread:" + Thread.currentThread().getName()
      + "> default time zone is:" + TimeZone.getDefault().getID());
  }

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
We were able to work around this problem by adding the following code in our main application immediately after TimeZone.setDefault() was called on the main thread:

      System.out.println("Default time zone is: " + TimeZone.getDefault().getID());
      Runnable swingWorker = new Runnable()
      {
        public void run()
        {
          TimeZone.setDefault(getApplicationUserDefaults().getTimeZone());
          System.out.println("AWT Default time zone is: " + TimeZone.getDefault().getID());
        }
      };
      SwingUtilities.invokeLater(swingWorker);


(This handles the AWT event thread but are there any others out there we forgot?)

Release Regression From : 1.4.2
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

Comments
EVALUATION This incompatible behavior has been fixed in Mustang b53 and 5.0u7 b01. TimeZone doesn't use thread local when the user code has a permission to set the user.timezone property. Closing this bug as a duplicate of 6242673.
18-11-2005