#native_company# #native_desc#
#native_cta#

On User-Defined Timezones in PHP Page 4

By David Whittington
on September 11, 2003

Our Solution

Our solution to these problems was to write wrapper functions around the
existing PHP functions. These functions act as mediators between PHP’s date
handling functions and our portal framework. Since we are dealing with user
timezones and not necessarily the server’s timezone, the functions take a
timezone offset as a parameter. This offset need not be the same as the
server’s offset, which is what makes these functions useful. Now, by using
these functions, each user of our portal can have a different timezone offset.
They are designed to work for both incoming and outgoing date formats. For
example, when saving to the database, a user enters a date in their local
time, and it is converted to GMT using their offset. The GMT timestamp is
stored in the database or otherwise used in the application. When reading
date fields from the database, we know that they will all be relative to
GMT, so we can safely apply the user’s timezone again to prepare the date
for display to the user. See the diagram below.
User to Database back to User
As mentioned earlier, by definition Unix timestamps are always measured
relative to GMT. However, with the interesting things we are doing with
timezone shifting, there is a point during our conversion from the user’s
local time when the timestamp returned from PHP is not actually in GMT. It is
necessary to shield the developers from this by encapsulating all of that in
the internals of these functions.
Notice the call to ‘gmmktime’ in our function, ‘new_mktime’. This function is
a useful alternative to the normal ‘mktime’. Essentially, it expects the input
to be date information relative to GMT, and it outputs a timestamp also
relative to GMT. We take advantage of the fact that this function applies no
server offset to the timestamp and treat the input and output as if it is
relative to the user’s timezone. Now we are dealing with a timestamp that is
not in GMT. Before anyone can notice, we quickly apply the user’s offset and
return the GMT timestamp. The same thing happens with ‘new_date’, only in
reverse.

<?php

    
/*

    **  new_mktime()

    **

    **  Same as PHP's mktime but with a $tz parameter which

    **  is the timezone for converting the timestamp to GMT.

    **  If the user is on the east coast of the USA, this

    **  would be "-0400" in summer, and "-0500" in winter.

    */

    
function new_mktime($hr,$mi,$se,$mo,$dy,$yr,$tz) {

        
$timestamp gmmktime($hr,$mi,$se,$mo,$dy,$yr);

        
$offset = (60 60) * ($tz 100); // Seconds from GMT

        
$timestamp $timestamp $offset;

        return 
$timestamp;

    }

    /*

    **  new_date()

    **

    **  This is also the same format as PHP's date function,

    **  but with the additional timezone parameter which

    **  specifies the user's timezone.

    */

    
function new_date($format$timestamp$tz) {

        
$offset = (60 60) * ($tz 100); // Seconds from GMT

        
$timestamp $timestamp $offset;

        return 
gmdate($format$timestamp);

    }

?>



BEWARE: The PHP function, ‘gmmktime’ takes an optional 7th parameter
called ‘is_dst.’ This does not do specifically what the documentation says it
does. The use of this flag tells PHP whether or not to apply the
server’s daylight savings time offset to the GMT timestamp. You will
have unexpected results by not using the default (-1). In a perfect world,
‘gmmktime’ would not allow this parameter to be specified.
You may have noticed that throughout this article, we have glossed over a
rather important issue. We are ignoring the fact that users will often need
a daylight savings time offset applied to their timezone. In the functions
above, the timezone parameter is assuming that the daylight savings time
offset has already been applied if necessary. In reality, to fully
implement this solution, it will be necessary to write other functions that
detect if the timezone is in daylight savings time, and if so, to apply the
1-hour offset manually. This could be handled in new_mktime and new_date,
but for simplicity, we opted to leave that as an exercise for the reader.