Timezones in MySQL and PHP
20 Jul 2007
Timezones in web applications are often dealt with the same way as character sets. Either incorrectly or ignored. The case of unicode is improving slowly as developers discover the need for international language support.
The problem with not handling timezones correctly is not apparent in the usual case of the webserver and the database server being in the same timezone. A lot of PHP code passes the task of timestamps on to MySQL, e.g. by calling NOW()
on inserting or updating records. It is nessesary to know in what timezone the timestamps are stored to process them correctly.
Often a specific timezone is assumed for displaying date and time. This could very well be incorrect for a large number of visitors. If the database server is in yet another timezone, things begin to get out of sync.
MySQL’s handling of timezones is - obviously - separate from that of PHP. Along with the fact that the MySQL timezone can only be changed by users with SUPER
privileges, this means that MySQL cannot be relied on for creation of timestamps in correct timezone. In other words, the usual call to NOW()
is not an option.
PHP 5.1 introduced the date_default_timezone_set()
method for setting the timezone PHP will use for functions like date()
(or alternatively the date.timezone
php.ini directive). This allows for a flexible and consistent way of creating timestamps in the correct timezone form within PHP. One user in London might start a thread while a response comes from New York. To handle this consistently and make sure both users see local time and the correct timespan between the posts, use date_default_timezone_set()
to create the timestamps from PHP in a fixed timezone like GMT.
Time for an example to illustrate the practical use of this. I created a simple class Timer
for dealing with these operations. The methods will be called statically as there is no need to instantiate the class.
class Timer
{
static function DatetimeInGMT() {
return date("Y-m-d H:i:s", time()-date("Z",time()));
}
static function GMTDatetimeToLocal($datetime) {
$time = strtotime($datetime);
return date("Y-m-d H:i:s", $time+date("Z",$time));
}
}
Timer::DatetimeInGMT()
produces a timestamp in GMT. I use it instead of MySQL’s NOW()
because it calculates the time relative to the timezone set in PHP rather than that of MySQL.
When reading the timestamp back in to PHP, another function call is needed to convert it back to whatever timezone PHP is in at that time (it might be a different user viewing with another timezone setting). That’s the job of Timer::GMTDatetimeToLocal($datetime)
.
These two very simple functions and a correct setting of PHP’s timezone will ensure that dates are stored in a consistent way and displayed correctly.