ZF-2071: Zend_Date Constructor should respect timezone offsets in ISO 8601 date strings

Description

Currently the Zend_Date constructor ignores timezone offsets specified in ISO 8601 date strings. For example:


date_default_timezone_set('America/Chicago');

$stringInDefaultTimezone = '2007-10-15T10:32:00-05:00';
$stringInCustomTimezone  = '2007-10-15T10:32:00+09:00';

$dateInDefaultTimezone = new Zend_Date($stringInDefaultTimezone, Zend_Date::ISO_8601);
$dateInCustomTimezone  = new Zend_Date($stringInCustomTimezone, Zend_Date::ISO_8601);

echo $dateInDefaultTimezone->get(Zend_Date::TIMESTAMP) . PHP_EOL;
echo $dateInCustomTimezone->get(Zend_Date::TIMESTAMP) . PHP_EOL;

The above code, given Zend_Date 1.0.2, produces identical timestamps for both date objects, even though the original strings referred to two different moments in time. The only way to produce the correct timestamps given the current functionality of Zend_Date is to set the timezone offset prior to setting the rest of the date, like so:


$dateInCustomTimezone = new Zend_Date();
$dateInCustomTimezone->set('+09:00', Zend_Date::GMT_DIFF_SEP);
$dateInCustomTimezone->set($stringInCustomTimezone, Zend_Date::ISO_8601);

This is much less convenient to the developer than simply specifying the date string in the constructor, and becomes particularly difficult when the developer is trying to process a dynamic collection of ISO 8601 dates in a variety of valid formats from a variety of timezones. Without knowing the timezone offset in advance, the developer will have to extract it from the string using regular expressions, which is an extra complexity many developers could do without.

It would be much more convenient to the developer if the constructor simply respected the timezone offset provided in the date string itself. Beyond that, it would be much more intuitive, as developers specifying timezone offsets in their date strings are probably expecting it to "just work"; I know that was my experience :)

Thanks for reading!

Comments

Aligned equal signs in code example for better readability.

Integrated with SVN-7768

Timezone settings are now used and set as internal timezone if given. Also an additional method getTimezoneFromString() has been added.

I ran into this problem today using Zend Framework 1.0.3. So I tried it with the code from SVN trunk, but I'm not satisfied with the behavior. I'm using a PostgreSQL database with timestamps with timezone in it. Those have to be displayed on a website to people in different timezones (they choose their timezone in a config panel).


date_default_timezone_set('Europe/Brussels');
$date = new Zend_Date('2008-02-10 10:00:00+00', Zend_Date::ISO_8601);
echo $date;

The result is : 10 feb 2008 10:00:00

While I expected it to be : 10 feb 2008 11:00:00 Since Brussels is GMT+1.

Why should the timezone be +01 ? You set the timezone within your ISO String to +00 which is equal to GMT.

If you do not set any timezone than the timezone from default will be used... as the command says... "date_DEFAULT_timezone"... default if no timezone can be detected.

This is the same behaviour as if you would have done


date_default_timezone_set('Europe/Brussels');
$date = new Zend_Date('2008-02-10 10:00:00', Zend_Date::ISO_8601);
echo $date; // note the set timezone Brussels here
$date->setTimezone('+00:00');
echo $date; // oops... timezone changed !!

You can't really say that this is unexpected behaviour... if you set a timezone within your inputstring then it will be used... if you suppress the timezone then the timezone will be detected.

So you have 2 ways: You can eigther suppress the timezone before creating the date object... or if you are not able to change your input then you have to do some date-maths.

Sorry, my mistake.

So if I want to display a certain date in a different timezone than it was created in, I do :


$date = new Zend_Date('2008-02-10 10:00:00+00', Zend_Date::ISO_8601);
// $date->setTimezone('+01:00'); is rejected with message 'timezone_open() [function.timezone-open]: Unknown or bad timezone (+01:00)' in ...\Zend\Date\DateObject.php:1016
$date->setTimezone('Europe/Brussels');
echo $date;

That is indeed the behavior to expect.

But then I think I noticed a bug :


date_default_timezone_set('Europe/Brussels');
$date = new Zend_Date('2008-02-10 10:00:00+00', Zend_Date::ISO_8601);
$date->setTimezone('Europe/Brussels');
echo $date->get(Zend_Date::ISO_8601);

Outputs : 2008-02-10T10:00:00+01:00 Instead of : 2008-02-10T11:00:00+01:00

I think this is because in Zend_Date_DateObject on line 1009 the old timezone is fetched with :


$oldzone = @date_default_timezone_get();

Where it gets 'Europe/Brussels' as old timezone, so it doesn't make any changes.

It does work when I do :


date_default_timezone_set('Europe/London');
$date = new Zend_Date('2008-02-10 10:00:00+00', Zend_Date::ISO_8601);
$date->setTimezone('Europe/Brussels');
echo $date->get(Zend_Date::ISO_8601);

But that would mean I have to detect the timezone from the input string myself, and set the date_default_timezone accordingly.

You should use the latest trunk version... Also to mention +00 is not a timezone.

When you are having problems with the latest version from trunk please give * PHP Version * OS * ZF SVN Version * Reproducable code

Otherwise we are not able to reproduce the behaviour. Within SVN7910 I found no problems with the code you gave for reproduction.

Btw: The codelines you are referring to have nothing to do with the recognition of the timezone.

PHP Version 5.2.0 in Zend Studio 5.5.0 Windows XP SP2 ZF SVN 7919

Code :


date_default_timezone_set('Europe/Brussels');
$date = new Zend_Date('2008-02-10 10:00:00+00', Zend_Date::ISO_8601);
$date->setTimezone('Europe/Brussels');
echo $date->get(Zend_Date::ISO_8601);

Doesn't work correct here. But I was indeed wrong about the reason. I can make it work by using :


as input instead of :

PostgreSQL doesn't give the minutes in the offset. If I may believe Wikipedia (http://en.wikipedia.org/wiki/ISO_8601) it doesn't have to.