Monday, January 24, 2011

Agony with Dates, Timezone and Java Date Utilities

Am not a guru when it comes to dealing with dates and timezone and it just got worse when I was dealing with Java Library to get done with a date related task. Job was to display current date in IST (India) timezone. As it may sound to be quite simple, it did give me few sweaty hours with my system on EST timezone. Let's talk code:

public class DateConversionTest {  
public static void main(String[] args) {
  Calendar cal = Calendar.getInstance();
  DateFormat dateFormat = new SimpleDateFormat();
  dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));

  //L1: Convert date to IST timezone 
  String dateString = dateFormat.format(cal.getTime());
  System.out.println(dateString);

  //L2: Use parse method to get Date object, as advised by java.util.Date
  try{
      System.out.println("Date using parse method: "+

                              dateFormat.parse(dateString));
  }catch (ParseException e) { 
       e.printStackTrace();
  }

  //L3: Use date constructor

   System.out.println("Date using constructor: " +new Date(dateString));
  }
}

SimpleDateFormat provides the capability to convert date to particular timezone. I could have used Calendar for it, but let me get to it later why I opted out . The string "dateString" from DateFormat(//L1) provided the correct date in IST timezone. I had a mandate of returning "Date" object so the next step was to convert this to a Date object.
I looked up the javadoc and the constructor of java.util.Date with String parameter was deprecated and said "Deprecated. As of JDK version 1.1, replaced by DateFormat.parse(String s)."
As per the doc, I used the alternative but to my surprise the above method returned back my system date in EST timezone(look at output for details). Instead of using DateFormat I tried to replace the program with Calendar object but Calendar.getTime() would also return date in my system timezone inspite of converted dated already provided to it. This was the reason I did not use it in my test code.
After much of experiments, I tried to use the date constructor with String parameter(//L3) which finally returned the expected date in IST as stored in "dateString" object.

Output:
String date from formatter: 1/25/11 2:35 AM
Date using parse method: Mon Jan 24 16:05:00 EST 2011
Date using constructor: Tue Jan 25 02:35:00 EST 2011


Why should a deprecated constructor work as expected and DateFormat worked so strangely?
With my research it looked like DateFormat.parse internally works using Calendar object which returns date (Calendar.getTime()) based on the timezone of the JVM. (TimeZone.getDefault()). If I alter this, I get date in expected timezone. Note, JavaDoc does state that it returns date in UTC format but it always return in default timezone of JVM.
When a certain date is already provided by developer, shouldn't this auto conversion avoided by Java? I would like to hear how other developers would solve it in more effective way as it clearly doesn't leave me satisfied.

11 comments:

  1. Hmm, maybe I missed the point, but to display the current date in another timezone, i.e. different from your system timezone, I'd do this- (I am on IST, but tried displaying the date in GMT (London) so I knew whether my result was right or not)-
    Calendar londonCal=new GregorianCalendar(TimeZone.getTimeZone("Europe/London"));

    DateFormat formatter=new SimpleDateFormat(); //Format it however
    String formattedDate=formatter.format(londonCal.getTime());


    Seems to work, though I am not sure if I am solving your problem :-)

    ReplyDelete
  2. It may help to always remember that Date doesn't have any timezone information attached to it. It represents a specific moment in time. That moment is the same value irrespective of which time zone you are in. That is why when you parse back at L2 it resolves to the same moment value.
    The Date constructor, on the other hand, seems to work inconsistently - maybe that is why it is deprecated.

    ReplyDelete
  3. @Luanne, the string object returns correct data, try to parse String and create Date object. Let me know if you can still see date in London timezone.
    @Aldrin, that makes sense if we try to get default/system date from these objects. If Calendar object is created with particular millisecons and a timezone, what would you expect Calendar.getTime() to return?

    ReplyDelete
  4. Actually Aldrin, Calendar does have timezone attached to it. It works out of "Timezone.defaultTimezone()", which usually is the timezone of system on which JVM is running.We need to alter this for Calendar to return date in particular timezone. I tried it out and also found this blog http://blog.sarathonline.com/2009/01/javas-calendar-date-and-timezone-what.html . Let me know your thoughts.

    ReplyDelete
  5. Oh indeed- the Date is always represented in your system timezone- irrespective of the calendar it was created from- THAT I did not realize!!
    Perhaps someone has created a library to deal with this api? Dates and times have always been yucky in Java.
    On a side note- why do you specifically need a Date object? :-)

    ReplyDelete
  6. That's how UI beans are designed wherever we deal with dates, need to dig in details but I guess Calendar widget for us works with Date.

    ReplyDelete
  7. that was interesting information about timezoneoffset.
    If it is a matter of UI display, then I believe that the UI should take care of displaying it in the specified timezone. I guess you are persisting only a single Date value and formatting it as per the user's timezone. The UI widget should be able to take a Date and Timezone and display it accordingly. hope I make sense :)

    ReplyDelete
  8. Oh yeah, server layer should ideally just be dealing with UTC dates and such magic should happen in UI layer picking up timezone provided to it or fetching it from browser. Interesting part of this was these confusing utility methods of Date and Calendar classes. Thanks for helping me uderstand this better.

    ReplyDelete
  9. I have found the whole Date and Time API's in Java to be extremely confusing :-)

    This is my limited understanding of it though. A Date is a specific point in time, regardless of TimeZone. Think of it like this. At one point of time it was 1 Jan 1970 in Greenwich. If you create a Date() object right now, what it represents is the number of milliseconds that have elapsed since that point. Think of these 2 points of time as if you were in space and not earth. Then you will understand that they have timezone agnostic.

    Now coming to your specific question, what you really want is a Date object which will resolve to a certain time in a certain timezone. Unfortunately, I do not have a good answer to that problem. I tried what seemed intuitive to me, but it did not yeild the right answer.

    I think what you should be doing is probably to store the date object without changing it, but also adding a timezone, so when you want to convert the date to a timezone specific value, you can use the timezone with SimpleDateFormat to yeild the value you want.

    I am not sure if I answered your question, or created more doubts...

    ---
    Parag

    ReplyDelete
  10. I agree with the AGONY part of this! I have not been so angry coding for 10-15 years!

    My problem required me to see if a date crossed midnight from one time zone to another. So I wanted to compare hours and minutes. I thought getting them from Calendars with different time zones would do that. Wrong! They both gave me the same values and ignored the time zone.

    My solution: brute force. I added the time zone offset.

    ReplyDelete
  11. You should not directly convert data from one local time zone to another local time zone.

    One routine should convert the input data to UTC, and stored data should use UTC.

    Another routine should fetch the stored UTC data and apply the reader's time zone to it for display.

    Remember that local time zones change by one hour in Summer in some countries. This is roughly April to October in the North, and October to April in the South, and that not all places change on the same day.

    http://www.timeanddate.com/time/dst/2011a.html

    http://www.timeanddate.com/time/dst/2011b.html

    has a useful list.

    ReplyDelete