PEAR is archived and read-only

This mirror preserves historical PEAR package releases and metadata so existing references remain available.

Home » Date and Time » Date » Bug #2378

getDate(DATE_FORMAT_UNIXTIME) doesn't convert to GMT

Details

Submitted2004-09-23 10:32 UTC
Fromscragz at hotmail dot com
StatusOpen
PackageDate
PHP Version4.3.8
OSDebian Sarge
Roadmaps(Not assigned)

Comments

[2004-09-23 10:32 UTC] scragz at hotmail dot com

Description:
------------
If you have a Date in another TZ and ask use getDate(DATE_FORMAT_UNIXTIME) or getTime() the timestamp returned will be wrong since it is only using mktime.

Trying to fix this caused me some problems, but I have a patch (below) that seems to work.

Reproduce code:
---------------
<?php
$date =& new Date();
echo $date->getTime()."\n";
$date->convertTZbyID('America/Los_Angeles');
echo $date->getTime()."\n";
?>

This patch corrects the faulty behavior. I'm not sure if DATE_FORMAT_TIMESTAMP is expected to be GMT, but this could be adapted to that as well.

Index: Date.php
===================================================================
--- Date.php (revision 111)
+++ Date.php (working copy)
@@ -231,7 +231,16 @@
return $this->format("%Y%m%d%H%M%S");
break;
case DATE_FORMAT_UNIXTIME:
- return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
+ $date =& new Date($this);
+ if ($date->tz->offset) {
+ $offset = ($date->tz->offset + $date->tz->getDSTSavings()) / 1000;
+ if ($offset > 0) {
+ $date->subtractSeconds(intval($offset));
+ } else {
+ $date->addSeconds(intval(abs($offset)));
+ }
+ }
+ return mktime($date->hour, $date->minute, $date->second, $date->month, $date->day, $date->year);
break;
}
}

Expected result:
----------------
1095935549
1095935549

Actual result:
--------------
1095935549
1095910349

[2004-09-24 09:54 UTC] scragz at hotmail dot com

Forgot DST.

--- Date.php (revision 100)
+++ Date.php (working copy)
@@ -231,7 +231,17 @@
return $this->format("%Y%m%d%H%M%S");
break;
case DATE_FORMAT_UNIXTIME:
- return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
+ $date =& new Date($this);
+ if ($date->tz->offset) {
+ $dst = ($date->inDaylightTime()) ? $date->tz->getDSTSavings() : 0;
+ $offset = ($date->tz->offset + $dst) / 1000;
+ if ($offset > 0) {
+ $date->subtractSeconds(intval($offset));
+ } else {
+ $date->addSeconds(intval(abs($offset)));
+ }
+ }
+ return mktime($date->hour, $date->minute, $date->second, $date->month, $date->day, $date->year);
break;
}
}

[2004-09-25 07:11 UTC] scragz at hotmail dot com

That whole patch could be consolidated to a single call to $date->toUTC(). Also, I just realized that Date_TimeZone::inDaylightTime() uses Date::getTime(), which causes recursion that segfaults PHP. I kluged through this by using strtotime(), which seems to be working since the TZ env variable is set right before there.

[2005-03-14 19:27 UTC] mjs15451 at hotmail dot com

So what is the final patch for this since no one seems to be maintaining this?

[2005-03-14 20:20 UTC] scragz at hotmail dot com

Below are all of my changes to Date. They involve this bug, bug 2217 (compare), 2401 (more lenient regexp), and maybe something else I'm missing.

--- Date.dist.php 2005-03-14 12:07:42.172448280 -0800
+++ Date.php 2005-02-13 14:08:33.000000000 -0800
@@ -153,7 +153,7 @@
{

if (
- preg_match('/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)?$/i', $date, $regs)
+ preg_match('/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)?$/i', $date, $regs)
&& $format != DATE_FORMAT_UNIXTIME) {
// DATE_FORMAT_ISO, ISO_BASIC, ISO_EXTENDED, and TIMESTAMP
// These formats are extremely close to each other. This regex
@@ -231,7 +231,9 @@
return $this->format("%Y%m%d%H%M%S");
break;
case DATE_FORMAT_UNIXTIME:
- return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
+ $date =& new Date($this);
+ $date->toUTC();
+ return mktime($date->hour, $date->minute, $date->second, $date->month, $date->day, $date->year);
break;
}
}
@@ -731,6 +733,8 @@
*/
function compare($d1, $d2)
{
+ $d1 =& new Date($d1);
+ $d2 =& new Date($d2);
$d1->convertTZ(new Date_TimeZone('UTC'));
$d2->convertTZ(new Date_TimeZone('UTC'));
$days1 = Date_Calc::dateToDays($d1->day, $d1->month, $d1->year);
@@ -1222,4 +1226,3 @@
//
// END
?>
-

--- Date/TimeZone.dist.php 2005-03-14 12:11:59.604192760 -0800
+++ Date/TimeZone.php 2005-02-13 14:08:33.000000000 -0800
@@ -255,7 +255,9 @@
$env_tz = getenv("TZ");
}
putenv("TZ=".$this->id);
- $ltime = localtime($date->getTime(), true);
+ //$ltime = localtime($date->getTime(), true);
+ $time = strtotime($date->getYear().'-'.$date->getMonth().'-'.$date->getDay().' '.$date->getHour().':'.$date->getMinute().':'.$date->getSecond());
+ $ltime = localtime($time, true);
putenv("TZ=".$env_tz);
return $ltime['tm_isdst'];
}
@@ -3643,4 +3645,3 @@
//
// END
?>
-

[2005-03-14 21:37 UTC] mjs15451 at hotmail dot com

I found that the best thing to do is to remove these two lines in timezone.php as they make absolutely no sense to use since all they do is screw up the conversion and it seems to perform just fine without it:

putenv("TZ=".$this->id);
putenv("TZ=".$env_tz);

[2005-03-15 19:48 UTC] mjs15451 at hotmail dot com

Better yet, I did more testing and I found it's better to leave the putenv functions in. Just make sure you set putenv(TZ=YourTimeZone) before calling any Pear Date functions because PHP doesn't seem to set the server default Timezone in this variable. A null TZ variable is what is the most problematic. Obviously this variable setting needs to be allowed in safe mode for any date calculation to actually work correctly.

[2005-03-15 22:04 UTC] scragz at hotmail dot com

That's the thing, in my app the TZ gets set so I haven't noticed any problems the way I have it.