Travelling through Date and Time, part III

Until now, we looked at the basics and started to work with our dates. But we didn’t deal with timezones, which means all we did was pretty useless. In this part, we’re going to change that.

Okay, so what exactly are timezones? According to Wikipedia, a timezone “is a region of the earth that has uniform standard time, usually referred to as the local time”. And “local time” could be defined like this: “local time is UTC plus the current timezone offset for the considered location”. Note that it says “current offset”, not just “offset”, because a location can have different offsets at different times (e.g. because of daylight savings time). By the way: UTC stands for “Coordinated Universal Time” (actually: “Universal Time, Coordinated”) and is roughly equivalent to the formerly used GMT (Greenwich Mean Time). It is considered to be the “world time”, on which basis all other timezones are calculated. More on this topic can be found on Wikipedia, but I think this will do for our task ;)

In part I and II we completely omitted the fact that we live in timezones. Let’s have a closer look at what happened when we didn’t specify a timezone along with our dates. Or did we? Since PHP 5.3, there is a php.ini setting for the default timezone, e.g.: date.timezone = 'Europe/Berlin'. Actually, if you haven’t set that and are using something like date(), you’ll get a warning. But in case this is set, all DateTime objects without any specific timezone are implicit in the timezone set in the php.ini. So, in my setup, my dates would have been in Europe/Berlin, but the very same script could produce completely different results in your setup. Which could cause problems.

But it’s not all bad. Remember the definiton in the beginning? Every local time is computed by UTC plus current offset. So all things are in relation to UTC. And the unix timestamp, which we got to know in the first part of this series, is always in UTC. This proves really helpful. Let’s see why:
Say we create a DateTime object, this time with a specific timezone, like this:
$dateInBerlin = new DateTime('2010-04-20 10:00:00', new DateTimeZone('Europe/Berlin'));
And another date:
$dateInMoscow = new DateTime('2010-04-20 12:00:00', new DateTimeZone('Europe/Moscow'));
On April 20th, the timezone offset from Berlin to UTC is 2 hours, and from Moscow to UTC is 4 hours, so the difference between Berlin and Moscow timezone is 2 hours. Note that our dates are not exactly the same: One is at 10:00, the other at 12:00. Now, if we call getTimestamp() on both objects, we’ll get the same timestamp (1271750400) for both dates. Why? Because the timestamp consists of seconds elapsed since January 1st 1970 in UTC timezone. And because of the time difference between Berlin and Moscow, the dates are the same when expressed in UTC time.

OK, what does that mean for us? As we already know, FLOW3 stores the DateTime objects in the database as unix timestamps. To convert the DateTime object to an integer, FLOW3 uses the getTimestamp() method. Therefore, whatever timezone our date is in, we’ll get the same timestamp. What happens on reconstitution? The timestamp in the database is set via setTimestamp(). At this point, we’ll have a DateTime which value is the timestamp (UTC) and which expresses this in the default timezone specified in the php.ini.
But because I want to store a timezone along with my events, we need to do something before displaying them. We need to make sure that format() uses my timezone and not the default timezone. It turns out this is actually quite easy to do. DateTime objects have the method setTimezone(), which expects a valid timezone identifier as an argument. It then modifies the timezone, but not the timestamp. This is very important, as format() will output something different now, but getTimestamp() will still be the same. Mission accomplished.

Which means all is well. Or is it? No, not quite. Because if we want to associate a timezone with an event, we’ll probably have a form which allows the input of a date, along with a dropdown of possible timezones. Of course, the user enters the date corresponding to the timezone he selected. So before saving the date, we have to convert the entered date to express the correct timestamp (which is: an UTC expression of the combination of entered date and selected timezone). Note that just setting the timezone via setTimezone() doesn’t work here, because we actually want to modify the timestamp inside the DateTime object. Of course, this conversion has only to take place if we create the DateTime object before we have the timezone information available. On the other hand, if we change the timezone after we set the date, we have to modify the date again.

The last bit shows that timezone handling can get a bit complicated, but what it comes down to is basically to make sure that all dates in the database are in UTC. Once that is ensured, we’re safe. Additionaly, we also solved the problem of different php.ini settings, as our solution is now independent of this setting.

Next up: Localization.

filed under , , posted on April 22, 2010

One Response to “Travelling through Date and Time, part III”

  1. Thanks for this, it was actually an useful stuff and I will look back here for some more soon ;).