Ben Northrop


Decisions and software development


Time Zones and GWT's DateTimeFormat


(5 comments)
August 6th 2012


Let’s assume you wanted to display the user’s birth date in your GWT application. Sounds easy, right? You write a few simple lines of code in your GWT view class:

  DateTimeFormat f = DateTimeFormat.getFormat("MMM dd yyyy");
  lblDate.setText(f.format(user.getBirthDate());

After testing this feature locally, everything works as expected, and so you ship it. Voila! A few days later, however, the help desk calls and says that a user is complaining that their birth date is one day off. You assume it must be a data issue, so you check the database. The user’s birth date is:

1977-06-02 00:00:00.0

You then check for yourself within the application, and the users birthdate shows up as June 2nd, as it should. However, the user claims the apps says his birthdate is June 1st, and he has a screen shot to prove it. What the heck?

Well, upon further digging, you find that the user is in California, 3 time zones away from you, in New York. Seems like maybe this is the problem. You throw a few debug statements in your app, set the Time Zone on your local machine to Pacific, and sure enough, you recreate the problem. GWT logs that the user’s birth date is:

06/01/1977 21:00 -0700

In other words, 9pm the day before. What’s happening is that GWT by default uses the client’s local time zone when formatting dates with DateTimeFormat, so if a date is stored as midnight in the database for a given time zone (EST, in this case), for any time zone after the birth date will display as the day before.

Part of the root problem is that that storing a birth date as a Java Date is really overkill for most cases, since often all that’s needed is the day, month, and year. What the Java Date class gives us, instead, is some number of milliseconds relative to 1970 (plus if after, minus if before). On the server side everything would be fine, since formatting this date would use the timezone of the server, which is what the date is stored in. On the client side, however, GWT will use the timezone of the client machine, and so there could be a discrepancy.

Ok, back to the scenario. Armed with this knowledge, you then decide to hard-code the TimeZone when calling DateTimeFormat.format(). Knowing that EST is -5:00 hours from UTC/GMT, you would pass +300 minutes as an offset (note: it would seem more intuitive to pass -300, but this is not the case). So…

  DateTimeFormat f = DateTimeFormat.getFormat("MMM dd yyyy");
  TimeZone est = TimeZone.createTimeZone(+300);
  lblDate.setText(f.format(user.getBirthDate(), est);

Now, you test the change locally, this time checking for different time zones, and again feel confident that the bug is slayed and you ship the code to production. A few months later this time, you’d get another call from the help desk with the same exact problem: the user complains that his birth date is displaying one day prior to his actual birth date. Argh. What changed?

Thinking about it, you realize that what’s different is the season! A few months ago it was summer, but now it’s winter. Daylight Savings Time! What you didn’t account for is that user was born in June, which is really only -4:00 hours away from UTC/GMT, but you’re still offsetting -5:00 hours, and so on the client side the user’s birthdate is really:

06/01/1977 23:00 -0500

What you need is to take into account Daylight Savings Time in your format method:

  DateTimeFormat f = DateTimeFormat.getFormat("MMM dd yyyy");
  TimeZoneConstants t = (TimeZoneConstants) GWT
     .create(TimeZoneConstants.class)
  TimeZone est = TimeZone.createTimeZone(
     t.americaNewYork());
  int offset = est.isDaylightTime(date) ? +240 : +300;
  TimeZone tz = TimeZone.createTimeZone(offset);
  lblBirthDate.setText(f.format(date, tz));

Now, you think you’d be out of the woods, but unfortunately not. A few months later, you’d get yet another call from the help desk about the same freaking problem. You’re ready to lose it. What’s going on this time? Well, this user, you realize, is older than the others. He was born on:

1957-08-22 00:00:00.0

…but again his birthdate was displaying as the day before. Well, it turns out that GWT’s TimeZoneConstants class doesn’t consider Daylight Savings Time prior to 1970, and so the isDaylightTime() always returns false, and the -5:00 hour offset is always used, causing the same exact problem.

At this point, there are a few viable solutions. First, you could just format the date on the server side and pass it to the client as a String. Alternatively, you could create your own custom Date class (either as a subclass to Java’s Date or otherwise) that either takes into account TimeZones or just codifies dates as day/month/year. There are a few other possible solutions, but one of these two might do the trick.

Anyway, I hope this post has helped you avoid this problem. I’d love to hear any strategies you’ve come up with. Thanks!

I believe that software development is fundamentally about making decisions, and so this is what I write about (mostly). I've been building software for about 20 years now, as a developer, tech lead, and architect. I have two degrees from Carnegie Mellon University, most recently one in philosophy (thesis here). I live in Pittsburgh, PA with my wife, 3 energetic boys, and dog. Subscribe here or write me at ben dot northrop at gmail dot com.

Got a Comment?


Sign up to hear about the next post!

If you liked this article and want to hear about the next one, enter your email below. I don't spam - you'll only receive an email when there's a new post (which is about once a month, tops). It's all low-key, straight from me.

Comments (5)

Ashton
August 10, 2012
This is pretty awesome post. I haven’t had the good fortune to run into your last issue with pre 1970 daylight savings so that just gives even more reason to look forward to client side Date handling :)
This post will definitely help to build a manageable solution.
Thanks!
Andrey
October 04, 2012
Thanks a lot! You save my brains!
Mike Dee
December 28, 2012
Why not just store the birth date (in the DB) in GMT. Do all date calculation in GMT and only convert it to local time when needed, such as at time of display. This won’t handle the last problem though of DST pre 1970.
ashish
July 20, 2013
I found an interesting solution here.
http://techidiocy.com/java-server-and-client-time-zone-difference-problem-solved
Thanks
sreejesh
October 30, 2013
I did the below fix, so that instead of saving a date at 12 am, date will be saved at 6 am which is before business hours of server. Even hours are deducted, it won’t affect dob.
dob.setHours(6);