WEB Advent 2008 / What Time Is It?

As of PHP 5.1, PHP has advanced date/time support, so you can finally write code that doesn’t have to deal with timestamps and time zone abbreviations such as PST and CEST. Before I explain how to avoid these annoyances, let me explain why timestamps and time zone abbreviations suck.

A timestamp—also called Unix time—is the number of seconds since January 1, 1970 00:00 UTC. Because it is based on UTC, a timestamp always gives a unique point in time, which is useful for calculating elapsed time between two timestamps, but makes it impossible to tell what time it is from the timestamp alone. This also means that a timestamp has many different local times associated with it. For example, 0 is January 1, 1970 00:00 in London, but December 31, 1969 19:00 in New York.

Timestamps are also limited in range. On many 32-bit platforms, the range that a timestamp can represent is December 13, 1901 to January 19, 2038. On Windows, only positive integers are allowed, restricting this to January 1, 1970 to January 19, 2038. This is not a problem on 64-bit platforms or when using 64-bit integers, because timestamps can range from more than twenty times the age of the universe to approximately 293 billion years in the future. Perhaps this isn’t enough to represent every moment in time until the end of the universe, but it is certainly adequate for most practical purposes—unless you’re trying to write down the time of The Second Coming of the Great Prophet Zarquon. To recap, there are two issues with timestamps: they’re not local, and the range can be limited.

In order to make sense of a timestamp and render local time, you need to associate a time zone with it. It’s very common to identify time zones by acronyms such as PST, MST, EDT, GMT, and CEST. Unfortunately, these acronyms are not always unique. PST could be Pacific Standard Time or Pakistan Standard Time. MST could be Magrathea Standard Time or Mountain Standard Time. Time zone names are inconsistent as well and often have variations, such as MEST (Middle European Summer Time) as an alternative to CEST (Central European Summer Time). Additionally, names can be localized. For example, the French HEAC (Heure AvancĂ©e d’Europe Centrale) instead of CEST.

Even if the same abbreviation is used, there can be problems. This is because the “time zone” associated with a specific area might change. There are places in Indiana, US, that have shifted time zone at least 5 times in the last 50 years. In order to do things properly, you need to define a time zone for a specific area. Each time zone can have multiple UTC offsets, even when not considering the satanic Daylight Saving Time.

For Vincennes, Indiana, US, the time zone history is an absolute mess:

  • 1953 to 1964: Central Time with DST
  • 1964 to 1969: Eastern Time without DST
  • 1969 to 1970: Eastern Time with DST
  • 1970 to 2006: Eastern Time without DST
  • 2006 to 2007: Central Time with DST
  • 2007 to present: Eastern Time with DST

To handle UTC offsets properly for every geographic area, you need to collect all of these rules. The Olson database does exactly this, and it assigns identifiers to each area, such as America/Indiana/Vincennes for the example above.

To solve both the timestamp range issues and the time zone abbreviation issues, PHP’s date/time support provides a DateTime object since PHP 5.1 that encapsulates this information properly. Every DateTime object contains a time zone reference as well as a representation of Unix time (as a 64-bit value, independent of platform). Creating such an object is done by either the date_create() function or with new DateTime(). In either case, you get an object back, as demonstrated by the following example:

<?php
$d = new DateTime('2008-12-22 09:15');
?>

You might wonder how PHP knows which time zone to choose. There are a few different steps that PHP uses to determine the time zone. First, it checks whether a value has been set with date_default_time zone_set(). If not, it will check the php.ini setting date.time zone. Lastly, it attempts to guess by using information provided by the OS—this is very unreliable and throws a notice.

You can determine which time zone has been chosen by running the getName() method on the time zone object—fetched from the DateTime object with getTimezone() like:

<?php
$d = new DateTime('2008-12-22 12:52');
echo $d->getTimezone()->getName();
?>

In my setup, this returns Europe/Oslo. A list of supported time zones can be found in the PHP manual. There are many more things that PHP now supports, but those are outside the scope of this article. Keep an eye on my blog for an announcement that my new book on this subject is released. Until then, simply ask me, “What time is it?”

Other posts