View Single Post
Old 11-11-2007, 03:18 PM   #1 (permalink)
Wildhoney
La Vida es Sueño
Advanced Programmer Top Contributor 
 
Wildhoney's Avatar
 
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
Wildhoney is on a distinguished road
Red face Timezone Class: Dealing with Timezones the Proper Way

I must admit that I was going to write you a simple timezone script and then tell you how easy it was. However, I came across some handy timezone_* functions and things got a little more difficult but a lot more practical. This script will allow you to display the time in any timezone you wish, without any ifs, buts or maybes along the way.

This is the code I was going to use:

php Code:
define('TIMEZONE_LONDON', 0);
define('TIMEZONE_AMSTERDAM', +1);
define('TIMEZONE_BRISBANE', +10);
define('TIMEZONE_ARIZONA', -7);

function get_timezone_time($szTimeFormat = 'H:i:s', $szTimezone)
{
    return date($szTimeFormat, strtotime($szTimezone . ' hours'));
}

You can quite clearly see for yourself that it's a small function, however there are a couple of glaring problems with it. I realised the problems as I was coding the revised version and, although it would not be too difficult to fix, if you can do it a better way, then why not! I think it takes a brave coder to analyse and shred their own code into millions of parts, but here goes. I have spotted two immediate problems with the above:
  1. If the locale time is not GMT 0 then other timezones would not be reporting the time accurately. To correct this you would need a little mathematical tweaking in that you would calculate how far the locale is from GMT, and then calculate the target timezone based on that value.
  2. You need to list all the timezones manually and, although there aren't that many, it is rather time consuming listing them all.

With that script safely cast aside, allow us to move onto the new all singing, all dancing, script that will revolutionise the way you deal with timezones. Really!

We're going to begin the script with our obligatory defines. Although these are not required in the least, they help us to visualise the system in an article, and that's the only real reason I use them. We will start off by supporting 3 timezones. These are as follows:

php Code:
define('TIMEZONE_EST', 'EST');
define('TIMEZONE_CST', 'CST');
define('TIMEZONE_GMT', 'GMT');

Once we have our defines in place we can begin our function. We're going to give our function two arguments: a string which accepts the format to return the time and date in, as well as the target timezone to convert to. Without further ado, let us slowly lower our function declaration into its rightful place:

php Code:
function get_timezone_time($szTimeFormat, $szTimezone)
{

}

Now it is time to begin filling in our function with lots of juicy bits of code. I'm assuming here that most people will want to feed in the timezone abbreviation, as that's what most people are familiar with. For example, if I lived in the UK, which luckily for this example I do, then I live in two different timezones at two different times in the year, in summer I live in BST and in winter I live in GMT. Whereas if I was presented with a list of timezones in the format: location/city, then I'd be frantically there looking for Nottingham. However, as there is no Nottingham, but rather a London, then it'd take me a little longer to realise that, and so listing it as GMT and BST, would be much easier, and also make the list a lot smaller because many cities share the same timezones. Using a function called timezone_name_from_abbr will obtain the timezone name from the abbreviation. So:
Quote:
GMT = Europe/London
CST = America/Chicago
Let's drop that into our script now:

php Code:
$szTargetTimezone = timezone_name_from_abbr($szTimezone);

Once we have the correct timezone name we can now call the DateTimeZone class, and subsequently the DateTime class, to get the timezone and the time as a DateTime object:

php Code:
$pLocalTimezone = new DateTimeZone(date_default_timezone_get());
$pLocalDate = new DateTime('now', $pLocalTimezone);

The date_default_timezone_get function as seen here uses a series of faive guesses to determine the default timezone. All that we are doing here is getting the timezone that the server we're using is currently in, and then calculating the date.

Next we are going to use a try...catch statement to see if you have supplied the function with a valid timezone. The DateTimeZone construct is already configured to throw an exception if the timezone is invalid, and so what we will be doing is catching it and returning false:

php Code:
try
{
    $pTargetTimezone = new DateTimeZone($szTargetTimezone);
}
catch(Exception $pEx)
{
    return false;
}

Naturally, if we wanted to, we could have it echo out a message by adding the following code before return false:

php Code:
echo $pEx->getMessage();

This would provide us with the following message if we handed the function an invalid timezone:

Quote:
DateTimeZone::__construct() [function.DateTimeZone---construct]: Unknown or bad timezone ()
All that's left to do now is to perform the calculations on our target timezone by obtaining the offset from our locale timezone and finally change the time and date accordingly. So, to compute the offset:

php Code:
$iOffset = $pTargetTimezone->getOffset($pLocalDate);

We are calling the getOffset function on our $pTargetTimezone object which has the $pLocalDate as its argument. This will allow the function to crack open the $pLocalDate object and compare it with the date in $pTargetTimezone. Seem confusing? It isn't really. As the DateTimeZone class goes about things the proper way, in pure OOP, it returns objects which are then fed into the functions.

The offset in the above line is returned as an amount in seconds. Let's write the following pseudo code to help you understand:
  1. Locale time is 14:30
  2. Target time is 15:30
  3. Offset is 3600 (1 hour)

You see how simple that is? That it is all our function does. All that's left to do now is to use that offset to modify the time and return it to the front-end. We will be using the strtotime function for this to save on over-complex functions that may end up confusing you further:

php Code:
return date($szTimeFormat, strtotime($iOffset . ' seconds'));

This takes the time and date format you passed into the function from the function call, and then calculates the unix timestamp from the amount of offset seconds. It is then returned.

As we have used the try and catch statement in our code, we will want to check if the function has returned false, and if so, inform the user that they have supplied an invalid timezone. As I am based in the GMT timezone, I will be converting the time to CST. Our function call looks like this:

php Code:
$szDateTime = get_timezone_time('jS M Y, H:i:s', TIMEZONE_CST);
echo $szDateTime ? $szDateTime : 'Invalid timezone';

We are catching the return from the function, whether that be the time and date or false. We then use the ternary operator to echo out either the date or a message depending on the value of $szDateTime. If $szDateTime contains a time and date then echo out the time and date, otherwise inform the end-user that they have supplied an invalid timezone.

The time in the CST timezone, according to our script, is now:

Quote:
8th Nov 2007, 08:29:52
Early! But that's really all there is to converting the time and date to another timezone! From our first example which was full up to the top with flaws and bugs, we created a timezone function that goes about things the proper way. This function doesn't require you to specify each and every offset, but instead it knows exactly the offsets of various timezones and calculates it based on our locale timezone.

Incidentally, I have attached the script so you can have a good and proper look at how it's done in all its glory!
Attached Files
File Type: php TimezoneConverter-TalkPHP.php (1.0 KB, 632 views)
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
Send a message via AIM to Wildhoney Send a message via MSN to Wildhoney Send a message via Yahoo to Wildhoney
Wildhoney is offline  
Reply With Quote