Thursday, August 26, 2010

A Date with Flex

Date Objects
So in computing we always need to deal with dates.  All my tables for example have created date and modified date columns so each record is timestamped.  Then there are dates for reports, dates for events and schedules and calendar dates for people to deal with.  But why is it there is always so much trouble dealing with dates?
A Date is Unique
At its heart the Date object is an unique instant in time.  It is universal and absolute.
What does that mean?
It means that, say in Java when I say Date myDate = new Date();  I am creating an object that stores the moment of its own creation.  The date doesn't matter what timezone I'm in, it doesn't matter what language I speak or anything like that.  Deep inside the Date object, and this holds true regardless of programming language or database, there is a long integer that is the milliseconds since an epoch.  The usual epoch is 1/1/1970 which is often called Unix time, but it could also be UTC which was in 1972.  It isn't all that important when the epoch was.  The important point is there is no timezone offset.  Timezone and locale are human decorators added to a Date to make it relative to the human's location.  Regardless of where the computer is or the person programming it the number of milliseconds since epoch is the same.  If it is 10am for me in Australia and I call my friend in Greenwich (who won't be pleased) they would tell me it was 2am.  It is actually the same Date, the same time, the same instant in existence and it is that which the Date object captures.  We then apply formatting to that Date to produce something relative to where we are.
So What?
This is important because it means that if I need to create a date for my friend in Greenwich, for example I need to set an alarm for them to wake up at 8am things get twisted.  In Australia, if I just create a Date and set the hour to 8am and save it the alarm will go off at midnight!  So I need to see a Greenwich clock and select 8am on that.  In Java I can do something like this:
Calendar c = GregorianCalendar.getInstance(zone);
Where zone is Greenwich timezone.  Then I can set the hour on the calendar to 8am and get the Date from the calendar and bingo it's done.
Flex has a Date object but no Calendar.  So when I create a Date object in flex it automagically get the client computer's timezone/locale settings.  Inside the Date it is still milliseconds since epoch and you can get that by using the various UTC related methods to get the universal time.  The date picker and date formatter all by default work of locale dates / times.  That means you need to write your own code to display dates in other timezones.  In the above example where I need to set an alarm for my friend in Greenwich I would need to make sure that I was viewing the Date in the correct timezone.
There are a number of ways you can do it but I think it boils down to two ways which are both correct depending on your exact situation.

  1. Enter and send the date / time as a string.  It is pretty easy for me to simply type 8am and send that as a string to be stored and seen by my friend.  And if all I wanted to do was tell him when I'd set his alarm that would be fine.  However what if the server and/or client needed to operate on that time?  For example I actually needed to tell the computer what time to play the alarm and also how long the snooze lasts and so on?  I'd need to start parsing the string and I'd have to tell the computer what timezone the string was in.  If I was setting lots of alarms for people all over the world things would get very messy very quickly. So...
  2. On my client I have a timezone picker that selects what timezone to display dates to me in.  When I want to set the 8am alarm for my friend in Greenwich I tell my client I want to choose dates in Greenwich time, pick the date and send it.  Behind the scenes I will have selected the date that locally would look something like 4pm to me, but would equate to 8am to my friend.  Either way it is the same number of milliseconds since epoch.
Yeah it can get confusing.  Because all the date stuff in Flex hides the timezone you need to do some actual work if you want to operate in timezones other than what is set on your computer.  To do the modification might look like this:
var selectedDate : Date = new Date(2010,7,27,8); // 8am 27th Aug 2010 (month is 0 based index)
But that date is in my timezone which is no good.  But what if I do this:
selectedDate.minutes -= selectedDate.timezoneOffset;
selectedDate.minutes += myFriendsTimezoneOffset;
In the first line I subtract my timezone offset.  The timezone offset is the number of minutes difference between my timezone and UTC (or +0 hours).  So if I subtract the timezone offset to the selected date's minutes I will get a Date that is the time I selected (8am) as it would be in UTC, that is 8am UTC which, since I'm in Australia at +8hrs means selectedDate would be 4pm my time.
In the second line I add my friend's timezone offset.  This will make the Date 8am in his timezone (which just happens to be +0 since he is at Greenwich).  The important thing to note is that the selectedDate object now has a UTC time (milliseconds since epoch) that is equivalent to 8am Greenwich and 4pm Australia time.  It can be confusing but stick with it.
Over The Wire
When you serialize the date and send it to the server you are actually sending a UTC time.  There is no timezone sent with the Date.  If you remember a Date is an instance in time that is unique and absolute, a timezone / locale is something we decorate that Date with so it is relative to us humans.  Computers need a single representation of Date so they can compare them and sort them and perform calculations on them.  If you have chosen a Date and it is important that other people know what timezone you were in when you chose that Date you need to communicate this separately.
I hope this has helped someone.