Saturday, March 07, 2009

Date and TimeZone in Java

The date handling in Java is much more trickier than what it looks on surface, setting TimeZone in Calendar object does not make any difference. Date and Calendar objects are always in machine’s time zone, irrespective of what one tries to tell these classes.

In web application at times we need to convert the dates in user preferred time zone while displaying on the browser. Usually all dates are stored in one timezone to make it easy to change them in user's preferred or some other timezone. The general practice is to store dates in GMT. Take an example of a web application where a user creates an event say Delhi Daredevils vs. Rajasthan Royals and sets its start and end date / time (April 10, 2009 07:00 PM to 11:30 PM). The browser will send date / time as String e.g. servelet_url?start=04/10/200919:00&end=04/10/200923:30.

The venue of the event is in IST while the time zone of the server where web application is running is PST. When a date is constructed from the request parameters then it will be always in JVM's timezone. A date can be changed to another timezones for storing in DB or displaying on browser using following utility methods-

    // Change a Date to GMT
    public static Date toGMT(Date date) {
        return changeTimeZone(date, "GMT");
    }

    // Change a date to GMT from a given timezone
    public static Date toGmtFromZone(Date date, String fromZone) {
        TimeZone pst = TimeZone.getTimeZone(fromZone);
        return new Date(date.getTime() - pst.getRawOffset());
    }

    // Change a date in another timezone
    public static Date changeTimeZone(Date date, TimeZone zone) {
        Calendar first = Calendar.getInstance(zone);
        first.setTimeInMillis(date.getTime());

        Calendar output = Calendar.getInstance();
        output.set(Calendar.YEAR, first.get(Calendar.YEAR));
        output.set(Calendar.MONTH, first.get(Calendar.MONTH));
        output.set(Calendar.DAY_OF_MONTH, first.get(Calendar.DAY_OF_MONTH));
        output.set(Calendar.HOUR_OF_DAY, first.get(Calendar.HOUR_OF_DAY));
        output.set(Calendar.MINUTE, first.get(Calendar.MINUTE));
        output.set(Calendar.SECOND, first.get(Calendar.SECOND));
        output.set(Calendar.MILLISECOND, first.get(Calendar.MILLISECOND));

        return output.getTime();
    }

Internally Date class holds the TimeZone reference but that is always machine time zone that’s why while invoking Date.toString() it prints local time zone, which can be overlooked safely. Take this example where we have a date in IST, which later converted in GMT but Date.toString() always prints 'IST' with it because my machine's timezone is IST.

IST: Sat Mar 07 22:38:16 IST 2009
GMT: Sat Mar 07 17:08:16 IST 2009

5 Comments:

pik said...

probably pst.getRawOffset() should be pst.getOffset(date.getTime());

chitgoks said...

i created a calendar object with timezone America/Los_Angeles dated dec 18, 2008 3:54pm. if changing it to Asia/Hong_Kong, result is not jan 1, 2009. any idea what could be wrong?

Anonymous said...

You are mixing up the concepts of Calendar/Date/Timezone. The reason Date.toString is always your local timezone is because your local machine's timezone is being used when formatting the output. In your example:
IST: Sat Mar 07 22:38:16 IST 2009
GMT: Sat Mar 07 17:08:16 IST 2009
are 2 different times and it is not a good idea to "overlook safely".

In fact, the same Date object can be output to any timezone's time using DateFormat with timezone set.
For example: with the same Date object, you can output to the following strings with DateFormat.

May 29, 2009 10:40:18 PM GMT
May 29, 2009 3:40:18 PM PDT
May 30, 2009 4:10:18 AM IST

Anonymous said...

The issue with above code is you took output object from Calendar.getInstance(). That would give a instance in local timezone (e.g. IST in this case). Then date/time is set on this local calendar instance by several output.set(x,x) statements. When you print the date you see change in time and you assume that you successfully changed the timezone. Actually the 'first' object is giving you the other time.

tech said...

well if anyone's interested, i used jodatime. 3rd party java library. much accurate than java's Date and Calendar classes.

you can check it out
http://tech.chitgoks.com/2009/07/06/convert-timezone-using-joda-time/