Wrong Default TimeZone in Java on Red Hat Linux (Friday, June 20, 2008)

While I have the fortune of running Ubuntu and Debian at home, I have the misfortune of working with Red Hat (Fedora Core 9, specifically) at the office. Today, I spent the afternoon fighting Java, which insisted that the time was an hour earlier than reality. I found a solution.

So here's the environment. My computer lives in Eastern Daylight Time (UTC-04:00 at this time of year). Obviously, when Daylight Saving Time is not in effect, we're five hours behind UTC. My system is "properly" configured, with the time zone set to "America/New_York". My test program looks like this:

Listing:

import java.util.*;
public class Test {
    public static void main(String args[]) throws Exception {
        System.err.println("Default TZ="+TimeZone.getDefault());
        System.err.println("Now="+new Date());
    }
}

Output:

$ date
Fri Jun 20 17:17:09 EDT 2008

$ java Test
Default TZ=sun.util.calendar.ZoneInfo[id="GMT-05:00",offset=-18000000,
dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Now=Fri Jun 20 16:17:09 GMT-05:00 2008

As you can see, it reports the non-DST version, because it's really just guessing. And it is also off by an hour. After reviewing a ton of literature and discussion online, the most commonly accepted work-around for this issue is to specify the TZ environment variable, which should be totally unnecessary. But it works:

Output:

$ date
Jun 20 17:22:04 EDT 2008

$ TZ="America/New_York" java Test
Default TZ=sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18
000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=ja
va.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings
=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDa
y=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMo
nth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
Now=Fri Jun 20 17:22:04 GMT-04:00 2008

But I was not satisfied by this... if the OS is properly configured, and Java is anything better than brain-dead, it should work. So I pulled out my trusty "strace" tool, for following system calls. You have to remember to use the "-f" parameter, so that it actually digs down into the useful child processes that get spawned.

What I ended up finding was that it was trying to open the file $JRE_HOME/lib/zi/America/New York. Looking in that directory, I found a file named New_York — with an underscore, instead of a space. Now, I'm not sure where it is getting a time zone name with a space instead of an underscore, but the solution was immediately obvious to me. Create a symbolic link to the underscore version using a name with a space:

Output:

$ su -
Password:

# cd $JRE_HOME/lib/zi/America

# ln -s New_York New York

# logout

$ date
Fri Jun 20 17:31:17 EDT 2008

$ java Test
Default TZ=sun.util.calendar.ZoneInfo[id="America/New York",offset=-18
000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=ja
va.util.SimpleTimeZone[id=America/New York,offset=-18000000,dstSavings
=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDa
y=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMo
nth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
Now=Fri Jun 20 17:31:10 GMT-04:00 2008

I have to wonder if this only shows up for people with a space in their time zone name, like America/Los_Angeles and America/New_York. If that's the case, making that symbolic link might be the best solution for you, as it doesn't rely on system configuration. I hope this is useful for other folks, so they don't have to bash their heads against the wall for hours like I have. Good luck!

—Brian (06/20/2008 3:35 PM)