Creating GWT Date / Time Pickers That Work in Any Time Zone
We're working on new features for the next release of TeamPage that allow people to create events on a calendar. For the edit event dialog, we needed date and time pickers that allow people across different time zones to edit the dates and times of events. We ended up creating new GWT controls and adding them to our open source gwt-traction library .
Edit Event Dialog
It seems easy enough to implement using our own secret sauce of HTML/XML/SDL, some GWT, and a bit of CSS to make it look nice. I had almost everything working in an hour or so. Most of the fields use widgets provided by GWT or controls we've already written ourselves. I saved the Start Date and End Date fields for last.
I've used a bunch of different calendar applications and looked around the web for inspiration. Could there be a time control that I could just use? I found a lot of people with similar questions and a few decent solutions. I found this question on Stack Overflow. Eventually I decided that I was on my own. Not a big deal. I've written widgets before and we like to share them with the GWT community, so everyone wins.
I'd need two fields: a date input and a time input that together specify a time, independent of timezone. On the server we store them as the number of milliseconds since January 1, 1970, 00:00:00 GMT, the same value used by the Java Date object.
There's a nice date picker control that is part of GWT called a DateBox.
However, we noticed that it doesn't handle time zones very well, so we created the UTCDateBox. It makes it so that whatever Date you choose, you get a Long value as midnight in UTC of the date selected. The GWT DateBox control returns Date values and they are different values depending on the TimeZone in which you select the date. In the places we choose dates, we want a date independent of timezone.
For example, New Years Day is always Jan 1st even though it's still Dec 31st in the US when people start celebrating it in Japan. So using the UTCDateBox, if I'm in JST (Japan) and choose Jan 1st 2013, that will be the same as choosing Jan 1st 2013 in EST (US Eastern). They'll both have the same selected value (midnight Jan 1st 2013 UTC), independent of the time zone in which they were selected. We're happy with this solution for Dates and have been using it for years.
Time however, cannot be specified independent of TimeZone. I can't just say let's meet at 2pm on Dec 2nd and expect people around the world to show up at the right time. A time like 2pm is only meaningful in an associated TimeZone.
Since 2003, TeamPage has allowed users to create and edit articles, comments, files, tasks, etc. in their own TimeZone and Locale. It can be a little tricky to make sure everything is properly parsed and formatted, but we're used to the issues involved. It's important to keep the data that you store in a normalized format that you can query and sort.
Here's the UTCTimeBox that I decided to build. It's a TextBox with some special parsing that allows time to be entered in the user's preferred time format or other common formats. It's pretty lenient about parsing, so 6 is 6:00 AM, 6p is 6:00 PM, 645pm is 6:45 PM, etc. When you click the box, you get a drop-down list of a possible times in 30 minute intervals, formatted in your Locale.
Even though we have two separate controls for date and time, we still store a single Long value on the server side as milliseconds since January 1, 1970, 00:00:00 GMT. Ideally, we'd just have a single composite widget with these two controls implementing HasValue<Long>. setValue would split the value into separate date and time values and set the value of each controls, getValue would combine them, and this would all work in the user's preferred TimeZone which might be different than the server or browser TimeZone.
Since we can't properly split a single Long value into date and time parts in the browser, we'll have to do it on the server. I decided that the UTCTimeBox would just edit a value of milliseconds since midnight, independent of time zone (e.g. 12am is 0, 12pm is 12*60*60*1000).
When a user submits the form, the server, which knows the user's time zone, creates the corresponding Date value. When we edit the form, the server will split the values into appropriate values for the user's time zone. Nice and symmetric and the client code is much simpler. We made the server-side processing code available here.
Now we can edit events and the value is properly stored on the server.
The final piece is creating some interaction between the start and end dates to make it easier to create and maintain proper ranges. When you move the start date forward, the end date should move forward, maintaining a fixed duration. When you move the end date, the duration should adjust unless you move it before the start date. In that case it should move the start date back and maintain duration. It's the kind of interaction that you don't really notice but appreciate. This behavior is implemented by the UTCDateTimeRangeController.
HTML5 date/time inputs for iOS
HTML5 has new inputs for date, time, month, datetime, and more. iOS has particularly nice controls for selecting dates and times. Using deferred binding in GWT, we created a separate HTML5 implementation of the UTCDateBox and UTCTimeBox widgets. Currently we only present these to versions of Safari that support the "datetime" input (which is currently only iOS). While Opera supports these inputs, we think our text based controls work better than their dedicated date and time inputs.
As a developer using the UTCDateBox and UTCTimeBox, you don't need to do anything special to use the HTML5 versions in iOS. They will be presented automatically and use the same Long values described above instead of the standard HTML5 values.