Friday, September 6, 2013

Java Time Zones and Daylight Saving Time - DST

Hi Everybody,

The story starts when the Israeli government decided to change the date of the Time change between IST and IDT from 08/09/2013 to 27/10/2013.

There is no problem with that fact, unless you don't update your Java Version to the latest update like us. (Usually this happens when you work in a closed network not open the Internet).

Again, there would have been no problem if we had only one version of JVM in our organization, but unfortunately I found out that the situation is not so simple.

We had Java installations running with different Vendors (Oracle, IBM, HP, OpenJDK) and Multiple Versions (1.2 – 7.0) and different Operation Systems (Windows, Linux, Unix).
Many of the installations were inside Software products as folders, that the Outer products were running on these JVM's.

That was quite a Nightmare!
And the option of updating all the versions of the JRE/JDK's wasn't available!!

So there is a different way to update the Time zone configurations of the JRE/JDK,
But let's start with a little background:
The Time zones table that is used in the world is Olson Implementation.
I won't go into detail about the definitions. You can find more information in the following links: Wikipedia, Dalight Saving Time.

When the JVM takes the Time Zone that it should start with by default from the Operation system. (Or you can give it a JVM flag that is called "user.timezone" in the run command of the java process -> for example : "-Duser.timezone=Asia/Jerusalem").

The operation system defines when the Clock should switch between the IST and IDT time, but the Java Environment ignores it, and uses it's own Table that is based on the Same principle. All of the different vendor need to keep their JVM configuration up to date with updates.

So the problems we encountered (and the solutions of course):
1)      How do you find our which vendor and java version you are running?
Answer:
I wrote a little java program ("jvm-info") that prints out all the vital information of your installed JRE/JDK, here it is: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package demi.ben.ari.jvm.info;
  
public class Main {
                private static String properties[]
                                = {"java.home", "java.version", "java.vendor",
                               "java.vendor.url", "java.vendor.url.bug", "java.vm.name",
                               "java.vm.version", "java.vm.vendor", "java.vm.info",
                               "java.vm.specification.vendor", "java.vm.specification.version",
                               "java.vm.specification.name", "java.runtime.name", "java.runtime.version",
                               "java.specification.vendor", "java.specification.version", "java.class.version",
                               "java.library.path", "java.class.path",
                               "os.name", "os.version", "os.arch",
                               "user.name", "user.timezone", "user.home", "user.dir",
                               "user.country", "user.country.format", "user.language", "user.language.format",
                               "java.io.tmpdir", "path.separator", "file.encoding.pkg", "file.encoding",
                               "file.separator", "awt.toolkit", "java.awt.printerjob", "java.awt.graphicsenv"
                               };
        
        public static void main(String[] args) {
                System.out.println("JVM information tool: ");
                System.out.println("==================================");
                
                for (int index = 0 ; index < properties.length ; index++) {
                        System.out.println(properties[index] +  " = " + System.getProperty(properties[index]));
                }
                
                System.out.println("==================================");
        }
}

Some notes:
-          I zipped the code as an Executable JAR file to run it more easily on the destination JVM.
-          Be sure that the java execution version of the Project is 1.4!!
If not the JAR file won't run on lower JRE/JDK versions.
-          Within an OS that has the JAVA_HOME system variable in the PATH of the OS,
When you will run "java –jar jvm-info.jar", it will execute on that java installation, if you want to execute it on a different installation, just enter the relevant directory ("[JAVA_INSTALL_DIR]\bin") and run it from there.

2)      Where do you find the update for each of the different vendors and JRE/JDK versions?
Answer:
After finding out all the different vendors and versions of the JVM's I wanted to update, I started searching all of them, and these are the findings with link's of usage and downloads of the Updaters:
Important Notes:
-          All of the updates work on JRE/JDK version: 1.4.x/5.0.x/6.0.x/7.0.x.
I couldn't find earlier JVM versions update tools.
-          All of the update tools need to run in "Administrator" or "root" privileges, it won't work otherwise.
-          You need to restart the running JVM, the updates are made locally on the Disk and running JVM's are not influenced.

Oracle:
HP (Running on HP-UX):
5)      Download The tzupdater. (go to the middle of the page and download the " tzupdater Tool 1.3.56 – June 2013" – this is the latest update when I wrote this article)
6)      HP usage explanation.

Notes:
-           I ran both of oracle and hp tzupdaters with the next command:
"java –jar tzupdater.jar –u –f –v –bc"
-          The JRockit JVM implementation uses the same tzupdater of Oracle.
-          To download the HP updater you'll need to open and free HP user account.

IBM:
IBM uses a little different tool, it's called  "JTZU".
1)      JTZU download page. (go to the middle of the page and download the zip file)
2)      IBM Explanation page.
The tool is different from HP and Oracle, the usage is explained in the README file inside the zip, you'll need to modify the relevant "runjtzuenv" file according to your OS. And then run the "runjtzu" file.
Note:
-          You'll need to define the JAVA_HOME for the tool to run from.
-          The tool searches for all IBM Java installation on your OS and updates it.
-          Change the flags in the runjtzuenv file according to your wishes.

OpenJDK (Running on Red Hat 6 Linux)
The updater tool for this kind of Java installation is not the same one as the oracle JVM, Red Hat Released an official RPM that updates the time zone table of the OpenJDK in it's Linux distributions, it could be found in the next link.
(RPM change log link)
Note:
Be sure to install the RPM with the "update" flag and not the "install", because it will fail otherwise.

3)      How can I check that the update was successful and the time won't just change at the wrong time?!?!
Answer:
With the Oracle and HP tools you can check the version currently installed with command: "java –jar tzupdater.jar -V"


For example:
This is the output, you can see that the current JRE and the updater version are not the same.

And here you can see that after the update the output is like this:

I wrote another little Java program ("tz-tester"), and ran it just like with the first program, as an Executable JAR.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package demi.ben.ari.tz.tester;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class Main {
    private static final int ONE_SECOND = 1000;
    private static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
    private static DateFormat formater = new SimpleDateFormat(DATE_FORMAT);
     
    public static void main(String[] args) {
        while (true) {
            printTime();
             
            try {
                Thread.sleep(ONE_SECOND);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
     
    private static void printTime() {
        System.out.println("========================================================");
        Calendar defaultUsedCalendar = Calendar.getInstance();
        Date time = defaultUsedCalendar.getTime();
        TimeZone defaultTimezone = TimeZone.getDefault();
        System.out.println("Timezone: " + defaultTimezone.getID() + " - " + defaultTimezone.getDisplayName());
        System.out.println("Use DST : " + defaultTimezone.useDaylightTime());
        System.out.println("Date    : " + time);
        System.out.println("Formated: " + formater.format(time));
    }
}

It's an infinite loop that prints out the relevant data every second.
I ran this program in one "shell" or "cmd", meanwhile I changed the date to after the IST and IDT change date, and I saw the time change to one hour backwards, and the writing changed from IDT to IST.

All of these actions should change the time zone table in the installed JRE/JDK and should prevent your system from behaving strangely and unexpectedly.


Good Luck!
I Hope this will prevent You the Headache I had in the past few days.
Demi Ben-Ari



No comments:

Post a Comment