ID:               14391
 Comment by:       lehresma at css dot tayloru dot edu
 Reported By:      pilots at farlep dot net
 Status:           No Feedback
 Bug Type:         Date/time related
 Operating System: Windows 2000 Server
 PHP Version:      4.0.6
 New Comment:

Posted this on php-dev and was advised to put it here as well.
--
A co-worker and I have been working on some intensive
timestamp/timezone manipulation code for the past week or so, and ran
across what we believed to be a bug in the way PHP handled gmmktime(). 
Browsing through the code, sure enough I found the source of the
problem.  I fixed it, and have attached a patch.

As I was searching around, I discovered that this has been an open bug
since 4.0.6 (2 years!).  I believe that this patch should fix most of
the issues related to the following problem:
    http://bugs.php.net/bug.php?id=14391


The Problem:
============
The function gmmktime() is supposed to take a date in GMT as a paramter
and return a timestamp in GMT.  No manipulation of the time should
occur. The only circumstance in which manipulation of the time should
happen is if the seventh  parameter (is_dst) is set to 1.  This is the
expected behavior.  However, in reality I noticed that when I didn't
specify a seventh parameter, it was applying a daylight savings time
offset when I tried to get the epoch.  See the example code.

<?php

    // Get a timestamp for the epoch (jan 1, 1970)
    echo "<pre>";
    echo gmmktime(0,0,0,1,1,1970,1)."\n";
    echo gmmktime(0,0,0,1,1,1970,0)."\n";
    echo gmmktime(0,0,0,1,1,1970)."\n";

    // Now get timestamp for June 1
    echo "\n";
    echo gmmktime(0,0,0,6,1,1970,1)."\n";
    echo gmmktime(0,0,0,6,1,1970,0)."\n";
    echo gmmktime(0,0,0,6,1,1970)."\n";

?>

I expected the first set of outputs to look like this:

    -3600
    0
    0

Which it does, so this is fine.  However, The second block of outputs
is in June which if I had been using simply mktime(), I would have
expected a DST offset (note: DST = daylight savings time) to be applied
by default (on the 3rd command).  However, since I'm using gmmktime()
by default no DST offset should be applied.  The odd behavior manifests
 itself in the second block of output:

    13046400
    13050000
    13046400

This is pretty meaningless until you realize what is actually going on.
The last output, which is generated by a gmmktime() that has no 7th
parameter, is applying an offset for daylight savings time.

The Solution
============
I had a moment of panic and was sincerely hoping that GMT did not
actually use daylight savings time, so I did some research and found
that while the timezone that contains Greenwich actually does use
daylight savings time, the GMT standard time reference does not.  So
right there I knew something was wrong on the PHP side.  I loaded up
the source of PHP (like a good open source enthusiast), and sure
enough, I discovered the source of the problem (no pun intended).

PHP is using the C function php_mktime() for both mktime() and
gmmktime(). There is a flag as a parameter called "gm" which is 1 or 0
depending on whether it was called from mktime() or gmmktime().  The
problem is that there is no check before applying the server's daylight
savings information to the timestamp.

As a fix for this, I changed the behavior so that by default if there
is no 7th parameter for gmmktime, it will not apply any DST offset. 
This differs from mktime which by default will appy the server's DST
offset.

I have attached patches for both PHP php-5.0.0b1 and php-4.3.2.  I
would imagine the same code would work on most recent versions of PHP
as it doesn't seem that this function has had much recent development. 
The patch can be applied by using the following:

    $ cd /usr/local/php-4.3.2
    $ patch -p2 < ~/gmmktime-php_4.3.2.patch

Feel free to contact me if you have any questions.
--




PATCH for php4.3.2
---
diff -u clean/php-4.3.2/ext/standard/datetime.c
php-4.3.2/ext/standard/datetime.c
--- clean/php-4.3.2/ext/standard/datetime.c     2003-05-04
07:22:00.000000000 -0400
+++ php-4.3.2/ext/standard/datetime.c   2003-08-18 17:18:19.000000000
-0400
@@ -116,7 +116,20 @@
        /* Let DST be unknown. mktime() should compute the right value
        ** and behave correctly. Unless the user overrides this.
        */
-       ta->tm_isdst = -1;
+       /*
+       **  If we are using gmmktime(), do not use the local
+       **  server's setting of DST.  GMT does not use daylight
+       **  savings, so unless the user overrides us, this should
+       **  be set to 0.
+       **  - Luke Ehresman, Aug 2003, <[EMAIL PROTECTED]>
+       */
+       if (gm) {
+               ta->tm_isdst = 0;
+               is_dst = 0;
+       } else {
+               ta->tm_isdst = -1;
+               is_dst = -1;
+       }
         
        /*
        ** Now change date values with supplied parameters.


Previous Comments:
------------------------------------------------------------------------

[2003-04-18 22:31:09] keath at keathmilligan dot net

This is not just a Windows bug. It happens on Linux as well. It looks
like gmmktime() is getting confused trying to apply some sort of
daylight-savings logic. For me, gmmktime returns the correct results
except for 2AM, April 6th which happens to be the start of daylight
savings time in the US.

The following code:

$t=gmmktime(1,0,0,4,6,2003);
echo "<br>inside DST: ",gmdate("Y-m-d H:i",$t);
$t=gmmktime(2,0,0,4,6,2003);
echo "<br>inside DST: ",gmdate("Y-m-d H:i",$t);
$t=gmmktime(3,0,0,4,6,2003);
echo "<br>inside DST: ",gmdate("Y-m-d H:i",$t);

Yields:

inside DST: 2003-04-06 01:00
inside DST: 2003-04-06 03:00
inside DST: 2003-04-06 03:00

------------------------------------------------------------------------

[2003-04-07 10:52:26] f dot vulto at re-base dot com

Same problem with PEAR Date TimeZone.  When converting datetimes to
local timezones in Europe, PHP calculates a Daylight Savings Time (DST)
starting at 2003-04-06 (= USA standard = first Sunday of April).  What
should've been calculated is a DST starting at 2003-03-30 (= Europe
standard = last Sunday of March).

I think this can be deduced to 'mktime()' or 'localtime()'  not working
correctly with 'TZ' environment variable on Windows:

<?php
$tzBackup = (getenv('TZ')) ? getenv('TZ') : '';
putenv('TZ=CET+1CEST');

print '<pre>';
$aTime = localtime(mktime(12, 0, 0, 3, 23, 2003, -1), 1);
print_r($aTime);
$aTime = localtime(mktime(12, 0, 0, 3, 30, 2003, -1), 1);
print_r($aTime);
$aTime = localtime(mktime(12, 0, 0, 4, 6, 2003, -1), 1);
print_r($aTime);
print '</pre>';

putenv('TZ=' . $tzBackup);

?>

'tm_isdst' should've been '1' for the second date (March 30), but is
'0' on my pc.  This is probably because the Microsoft '_tzset' routine
doesn't know about regional DST differences.  You can specify 'dzn' in
'set TZ=tzn[+|-]hh[:mm[:ss]][dzn]', but Microsoft manual says:

  "The C run-time library assumes the United States's rules for
implementing the calculation of Daylight Saving Time (DST)"  (see:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_crt__tzset.asp)

which implies the exact value of 'dzn' ('CEST' in the example above) is
ignored, which is what I'm experiencing.  Windows seems to calculate
the Daylight Saving Time with a fixed algorithm which is only true for
the US but not for Europe.  The result is the DST will start at wrong
days.

Does this mean - using Windows - you can *only* calculate the exact
local time of a UTC time in a different timezone if this timezone is:
1.  the local timezone of the server or 
2.  a timezone following US DST rules?

If so, instead of TZ, PHP has to use Windows API functions and registry
information to perform timezone conversions on a Windows platform.  I
don't have PHP core coding experience so here are some useful links
:-)

http://archive.devx.com/premier/mgznarch/vbpj/1999/08aug99/mt0899.pdf
http://17slon.com/gp/gp/files/gptimezone.htm
http://www.thedelphimagazine.com/samples/1175/1175.htm


Freddy Vulto

------------------------------------------------------------------------

[2003-02-27 08:06:43] nospam at no-spam dot com

The gmmktime() & windows problem still persists on 4.3.0. 

We now go into the third year of this bug being around. Could this
become a record???
Testcase: 
   gmmktime(0,0,0,1,1,1970);
MUST return 0, also on a win box that is not in GMT-Zone.

Or then, PLEASE introduce URGENTLY a new function that allows the
manual correction of the TZ, e.g.:


  int gmmktime ( int hour, int minute, int second, int month, int day,
int year [, int is_dst] [, int tz_offset])

because it boils down to the fact that the TZ-offset is the thing that
is not detected correctly.

------------------------------------------------------------------------

[2003-01-12 05:41:11] frodo at interport dot net

this bug has been around for over 2 years and it's a big one!!!

for time based apps lets face it, the gmmktime function is extremely
important.

any idea when this will be fixed?

------------------------------------------------------------------------

[2003-01-08 02:13:31] napetune at hotmail dot com

I use Win2K.I got this problem too. But after I updated to php 4.2.1
version, they are gone. It works well on php 4.2.1 (or higher version,
I guess).

Kae

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/14391

-- 
Edit this bug report at http://bugs.php.net/?id=14391&edit=1

Reply via email to