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:
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:
…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!