Zend Framework

"fix_dst" problem

Details

  • Type: New Feature New Feature
  • Status: Resolved Resolved
  • Priority: Minor Minor
  • Resolution: Fixed
  • Affects Version/s: 1.0.1
  • Fix Version/s: 1.0.3
  • Component/s: Zend_Date
  • Labels:
    None

Description

This problem seems to occure only when the timezone is "UTC" ...
The code that I think is problematic is in Zend/Date.php, about the 1290th row ...

// dst-correction if 'fix_dst' = true and dst !== false
        if ((self::$_Options['fix_dst'] === true) and ($dst !== false)) {
            $hour = $this->get(Zend_Date::HOUR);
            if ($hour != $dst) {
                if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) {
                    $value += 3600;
                } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) {
                    $value -= 3600;
                }
                $this->setUnixTimestamp($value);
            }
        }

The problem occured when I tried this method of creating a date

// Set a default timezone... this has to be done within the bootstrap file or php.ini
// We do this here just for having a complete example
date_default_timezone_set('UTC'); 
$Y = 2007; $m = 1; $d = 1; $H = 20; $i = 45; $s = 37;

$arr = array('year' => $Y, 'month' => $m, 'day' => $d, 'hour' => $H, 'minute' => $i, 'second' => $s);

$date = new Zend_Date($arr);
echo $date->toString('r');

The out put is "Mon, 01 Jan 2007 20:45:37 +0000" and it's OK

but if the $H variable is:
$H = 1; // the output is "Mon, 01 Jan 2007 00:45:37 +0000"
$H = 23; // the output is "Tue, 02 Jan 2007 00:45:37 +0000"

This does not happen if the timezone is different from "UTC" ... and as far as I know DST doesn'y apply to UTC/GMT

This thing fix the above problem

// dst-correction if 'fix_dst' = true and dst !== false
         $zone = @date_default_timezone_get();
        if ((self::$_Options['fix_dst'] === true) and ($dst !== false) and ($zone != 'UTC')) {

but I'm not sure how this interact with the rest of the ZF code and i'm not sure that this is the most elegant way to fix the problem ... if it's a real problem anyway ... I hope this is not my mistake or misunderstanding of the way the fix_dst works

EDIT:
As I get deep into the code I found something that may be the problem ... The fix above probably in not the right way to fix it ...

So I followed the path of the arrat throw the functions in Zend_Date:
1. Line 160 the constructor
2. Line 201 $this->set()
3. Line 1154 function set(....)
4. Line 1156 return $result = $this->_calculate('set', $date, $part, $locale);
5. Line 1315 function _calculate ....

this is almost the end of the journey ...

Line 1338: $hour = $this->get(Zend_Date::HOUR_SHORT); //as I tested ... it's alway 0 ... because the $_unixTimestamp in the Date_Object is 0 ...

Line 1466: return $this->_assign($calc, $this->mktime($hours, $minutes, $seconds, $months, $days, $years, true),
$this->mktime($hour, $minute, $second, $month, $day, $year, true), $hour);
$hour is used as last argument in _assign ...

private function _assign($calc, $date, $comp = 0, $dst = false)

so $hour is $dst in _assign function ... and $dst is used in "dst_fix" ...

$dst is 0 ... and when $hour is 23 or 1 ... an hour is added or substracted ....

Activity

Hide
Thomas Weidner added a comment -

If you do not want to take DST in account you have to use

Zend_Date::setOptions(array('fix_dst' => false));

See http://framework.zend.com/manual/en/zend.date.overview.html - DST and Date Math
for detailed information.

Show
Thomas Weidner added a comment - If you do not want to take DST in account you have to use
Zend_Date::setOptions(array('fix_dst' => false));
See http://framework.zend.com/manual/en/zend.date.overview.html - DST and Date Math for detailed information.
Hide
NiKi Zh added a comment -

Well I want to take DST in account ... but not in this particular situation

Show
NiKi Zh added a comment - Well I want to take DST in account ... but not in this particular situation
Hide
Thomas Weidner added a comment -

Then turn off DST for this particular situation.

You can't say "I want DST" and then say "but I dont want DST"... either with or without but not both the same time.

Actually DST must be switched off /on per hand. This is not done automatically because DST handling is per definition a static attribute.

This may change in the future...
But for now this is not a bug but a feature request.

Show
Thomas Weidner added a comment - Then turn off DST for this particular situation. You can't say "I want DST" and then say "but I dont want DST"... either with or without but not both the same time. Actually DST must be switched off /on per hand. This is not done automatically because DST handling is per definition a static attribute. This may change in the future... But for now this is not a bug but a feature request.
Hide
NiKi Zh added a comment -

Well, anyway there is something that is not working ...

date_default_timezone_set('America/New_York'); 
        $Y = 2007; $m = 1; $d = 1; $H = 20; $i = 45; $s = 37;
        $arr = array('year' => $Y, 'month' => $m, 'day' => $d, 'hour' => $H, 'minute' => $i, 'second' => $s);
        $date = new Zend_Date($arr);
        echo $date->toString('r');

//OUTPUT: Mon, 01 Jan 2007 19:45:37 -0500

Why?

On line 1334 to 1340 the date is initiated
We set the time zone to GMT-5
1970-01-01 00:00:00 - 5 hours = 1969-12-31 19:00:00

So the initiated $hour is = 19

The lines 1446-1447 are:

return $this->_assign($calc, $this->mktime($hours, $minutes, $seconds, $months, $days, $years, true),
                                     $this->mktime($hour,  $minute,  $second,  $month,  $day,  $year,  true), $hour);
// private function _assign($calc, $date, $comp = 0, $dst = false)

So $dst = $hour ... So $dst = 19

// dst-correction if 'fix_dst' = true and dst !== false
        if ((self::$_Options['fix_dst'] === true) and ($dst !== false)) {
            $hour = $this->get(Zend_Date::HOUR);
            if ($hour != $dst) {
                if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) { // @_if_1
                    $value += 3600;
                } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) { // @_if_2
                    $value -= 3600;
                }
                $this->setUnixTimestamp($value);
            }
        }
} else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) {

the magic is on this line ... for this example only ...

OUTPUT: Mon, 01 Jan 2007 19:45:37 -0500 // but we wanted ... $H = 20; $i = 45; $s = 37; ...

if the time zone is GMT+0 (UTC) and $H = 23 the magic will happen in @_if_1
OUTPUT: Tue, 02 Jan 2007 00:45:37 +0000 // but we wanted ... $H = 20; $i = 45; $s = 37; ...

if the time zone is GMT+1 (CET) and $H = 2 the magic will happen in @_if_2
OUTPUT: Mon, 01 Jan 2007 01:45:37 +0100 // but we wanted ... $H = 2; $i = 45; $s = 37; ...

if the time zone is GMT+2 (Europe/Sofia) and $H = 3 the magic will happen in @_if_2
OUTPUT: Mon, 01 Jan 2007 02:45:37 +0200 // but we wanted ... $H = 3; $i = 45; $s = 37; ...

and so on ...

But in the other cases ... where there is no magic ... everything is OK ...

if the time zone is GMT+2 (Europe/Sofia) and $H = 4 the magic will happen in @_if_2
OUTPUT: Mon, 01 Jan 2007 04:45:37 +0200 // but we wanted ... $H = 4; $i = 45; $s = 37; ...

And what is the conclusion ...

Every call to _assign with hour defined by the user (if the particlur function allows it) and with last argument ($dst) $hour (the initiated value between line 1334 to 1340) are buggy

Show
NiKi Zh added a comment - Well, anyway there is something that is not working ...
date_default_timezone_set('America/New_York'); 
        $Y = 2007; $m = 1; $d = 1; $H = 20; $i = 45; $s = 37;
        $arr = array('year' => $Y, 'month' => $m, 'day' => $d, 'hour' => $H, 'minute' => $i, 'second' => $s);
        $date = new Zend_Date($arr);
        echo $date->toString('r');
//OUTPUT: Mon, 01 Jan 2007 19:45:37 -0500 Why? On line 1334 to 1340 the date is initiated We set the time zone to GMT-5 1970-01-01 00:00:00 - 5 hours = 1969-12-31 19:00:00 So the initiated $hour is = 19 The lines 1446-1447 are:
return $this->_assign($calc, $this->mktime($hours, $minutes, $seconds, $months, $days, $years, true),
                                     $this->mktime($hour,  $minute,  $second,  $month,  $day,  $year,  true), $hour);
// private function _assign($calc, $date, $comp = 0, $dst = false)
So $dst = $hour ... So $dst = 19
// dst-correction if 'fix_dst' = true and dst !== false
        if ((self::$_Options['fix_dst'] === true) and ($dst !== false)) {
            $hour = $this->get(Zend_Date::HOUR);
            if ($hour != $dst) {
                if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) { // @_if_1
                    $value += 3600;
                } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) { // @_if_2
                    $value -= 3600;
                }
                $this->setUnixTimestamp($value);
            }
        }
} else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) {
the magic is on this line ... for this example only ... OUTPUT: Mon, 01 Jan 2007 19:45:37 -0500 // but we wanted ... $H = 20; $i = 45; $s = 37; ... if the time zone is GMT+0 (UTC) and $H = 23 the magic will happen in @_if_1 OUTPUT: Tue, 02 Jan 2007 00:45:37 +0000 // but we wanted ... $H = 20; $i = 45; $s = 37; ... if the time zone is GMT+1 (CET) and $H = 2 the magic will happen in @_if_2 OUTPUT: Mon, 01 Jan 2007 01:45:37 +0100 // but we wanted ... $H = 2; $i = 45; $s = 37; ... if the time zone is GMT+2 (Europe/Sofia) and $H = 3 the magic will happen in @_if_2 OUTPUT: Mon, 01 Jan 2007 02:45:37 +0200 // but we wanted ... $H = 3; $i = 45; $s = 37; ... and so on ... But in the other cases ... where there is no magic ... everything is OK ... if the time zone is GMT+2 (Europe/Sofia) and $H = 4 the magic will happen in @_if_2 OUTPUT: Mon, 01 Jan 2007 04:45:37 +0200 // but we wanted ... $H = 4; $i = 45; $s = 37; ... And what is the conclusion ... Every call to _assign with hour defined by the user (if the particlur function allows it) and with last argument ($dst) $hour (the initiated value between line 1334 to 1340) are buggy
Hide
NiKi Zh added a comment -

Ups ...
I was copy-pasting the final rows ... and made mistake ...
And could not find an option to edit the last comment ... so i'm writing a new one

So ...
EDIT:
But in the other cases ... where there is no magic ... everything is OK ...

if the time zone is GMT+2 (Europe/Sofia) and $H = 4 the magic will !!!NOT!!! happen
OUTPUT: Mon, 01 Jan 2007 04:45:37 +0200 // but we wanted ... $H = 4; $i = 45; $s = 37; !!! AND WE GET IT

Show
NiKi Zh added a comment - Ups ... I was copy-pasting the final rows ... and made mistake ... And could not find an option to edit the last comment ... so i'm writing a new one So ... EDIT: But in the other cases ... where there is no magic ... everything is OK ... if the time zone is GMT+2 (Europe/Sofia) and $H = 4 the magic will !!!NOT!!! happen OUTPUT: Mon, 01 Jan 2007 04:45:37 +0200 // but we wanted ... $H = 4; $i = 45; $s = 37; !!! AND WE GET IT
Hide
Thomas Weidner added a comment -

For me your code works...

I dont know which revision you are using but I recommend that you update to the latest SVN release.
If your example does not work with the latest SVN release please provide the following data:

SVN release version
PHP version
BCMath avaiable ?
standard locale
standard timezone

We will then try to reproduce your problem

Show
Thomas Weidner added a comment - For me your code works... I dont know which revision you are using but I recommend that you update to the latest SVN release. If your example does not work with the latest SVN release please provide the following data: SVN release version PHP version BCMath avaiable ? standard locale standard timezone We will then try to reproduce your problem
Hide
Thomas Weidner added a comment -

I integrated the requested feature.

Now, if the timezone is set to UTC or GMT, the DST will no longer be used and switched off.
Please check SVN 6292.

Show
Thomas Weidner added a comment - I integrated the requested feature. Now, if the timezone is set to UTC or GMT, the DST will no longer be used and switched off. Please check SVN 6292.
Hide
NiKi Zh added a comment -

Now with the latest changes ...
SVN 6294
PHP 5.2.3
BCMath enabled

This is my index.php for testing ...

<?php 
error_reporting(E_ALL|E_STRICT); 
date_default_timezone_set('CET'); 

include "Zend/Loader.php"; 
 
Zend_Loader::loadClass('Zend_Date');

$Y = 2007; $m = 1; $d = 1; $H = 1; $i = 45; $s = 37;
$arr = array('year' => $Y, 'month' => $m, 'day' => $d, 'hour' => $H, 'minute' => $i, 'second' => $s);
$date = new Zend_Date($arr);
        
echo $date->toString('r');

OUTPUT: Mon, 01 Jan 2007 00:45:37 +0100

So with the latest changes ... from lines 1364 to 1370 the date is initiated ...
Even the default timezone is set to CET the initial date is 1970-01-01 00:00:00

So on line 1496 the used $hour is 0 when passed to _assign .... and by default fix_dst is True ...

if ((self::$_Options['fix_dst'] === true) and ($dst !== false) and ($this->_dst === true)) {
            $hour = $this->get(Zend_Date::HOUR);
            if ($hour != $dst) {
                if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) { 
                    $value += 3600;
                } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) { //@_if_2
                    $value -= 3600;
                }
                $this->setUnixTimestamp($value);
            }
        }

So when the passed $hour ... the $dst = 0 .... and we want to set the date hour to 1 ... In @_if_2 $dst == ($hour -1) // 0 == (1 - 1) ... then $value -=3600

That's why the produced output is -1 hour ...

Well ... This is what happens on my test ... on my system ....

Show
NiKi Zh added a comment - Now with the latest changes ... SVN 6294 PHP 5.2.3 BCMath enabled This is my index.php for testing ...
<?php 
error_reporting(E_ALL|E_STRICT); 
date_default_timezone_set('CET'); 

include "Zend/Loader.php"; 
 
Zend_Loader::loadClass('Zend_Date');

$Y = 2007; $m = 1; $d = 1; $H = 1; $i = 45; $s = 37;
$arr = array('year' => $Y, 'month' => $m, 'day' => $d, 'hour' => $H, 'minute' => $i, 'second' => $s);
$date = new Zend_Date($arr);
        
echo $date->toString('r');
OUTPUT: Mon, 01 Jan 2007 00:45:37 +0100 So with the latest changes ... from lines 1364 to 1370 the date is initiated ... Even the default timezone is set to CET the initial date is 1970-01-01 00:00:00 So on line 1496 the used $hour is 0 when passed to _assign .... and by default fix_dst is True ...
if ((self::$_Options['fix_dst'] === true) and ($dst !== false) and ($this->_dst === true)) {
            $hour = $this->get(Zend_Date::HOUR);
            if ($hour != $dst) {
                if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) { 
                    $value += 3600;
                } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) { //@_if_2
                    $value -= 3600;
                }
                $this->setUnixTimestamp($value);
            }
        }
So when the passed $hour ... the $dst = 0 .... and we want to set the date hour to 1 ... In @_if_2 $dst == ($hour -1) // 0 == (1 - 1) ... then $value -=3600 That's why the produced output is -1 hour ... Well ... This is what happens on my test ... on my system ....
Hide
NiKi Zh added a comment -

and ... the bug occures ... on my system ... on my test ... on every set timezone ... without UTC of course

Show
NiKi Zh added a comment - and ... the bug occures ... on my system ... on my test ... on every set timezone ... without UTC of course
Hide
Thomas Weidner added a comment -

The example in your second reply differs from your first one you gave us...

Related to your problem:
It seems for me that gmmktime differs if it is called from commandline or from browser.

I integrated both of your examples within our testbed and they passed.
But within the browser it differed for one hour.
I love such behaviour grrr

Anyway... with SVN 6300 also the array initialion behaviour is fixed.
Give it a try and us a response.

Show
Thomas Weidner added a comment - The example in your second reply differs from your first one you gave us... Related to your problem: It seems for me that gmmktime differs if it is called from commandline or from browser. I integrated both of your examples within our testbed and they passed. But within the browser it differed for one hour. I love such behaviour grrr Anyway... with SVN 6300 also the array initialion behaviour is fixed. Give it a try and us a response.
Hide
NiKi Zh added a comment -

SVN 6300 is working fine ...

Show
NiKi Zh added a comment - SVN 6300 is working fine ...
Hide
Thomas Weidner added a comment -

Fixed as with SVN 6300

Show
Thomas Weidner added a comment - Fixed as with SVN 6300

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: