[PHP] Re: [PHP-DB] how to get consistent UTC from gmmktime (w/o dst-offset) ????
Hi Patrick, [I have put this back on the list, because greater minds might come up with a better explanation!?] There appears to be an issue with "gm" time functions. gmmktime() is supposed to take a GMT date and return a GMT UNIX TimeStamp (as per mktime() but with no DST parameter). Conversely date() and gmdate() take a UNIX TimeStamp and format a string representation of the date-time. The question is how gmmktime() should perform when working with date-times on both sides of the summer time discontinuity. Assuming your server is running in a timezone that will change to/from Summer Time over the coming weekend (others may be able to adjust dates/offsets to run 'backwards' to cover last weekend, where relevant), try running the following code; and explaining why the "gm" functions do not complement each other (see below (way on down) for Patrick's original observations): Time Key Value Local GMT Comment " . "Now TSproduces dates:" . "$tsNow" . "$dtLoc" . "$dtGMT" . "as expected" . "" ; $ss = date( "s", $tsNow ); $hh = substr($dtLoc, 15, 2); $mm = substr($dtLoc, 18, 2); $DD = substr($dtLoc, 4, 2); $MM = substr($dtLoc, 7, 2); $ = substr($dtLoc, 10, 4); $DST = substr($dtLoc, 21, 1); $mkLoc = mktime( $hh, $mm, $ss, $MM, $DD, $, $DST ); $gmmkLoc = gmmktime( $hh, $mm, $ss, $MM, $DD, $ ); $hh = substr($dtGMT, 15, 2); $mm = substr($dtGMT, 18, 2); $DD = substr($dtGMT, 4, 2); $MM = substr($dtGMT, 7, 2); $ = substr($dtGMT, 10, 4); $DST = substr($dtGMT, 21, 1); $mkGMT = mktime( $hh, $mm, $ss, $MM, $DD, $, $DST ); $gmmkGMT = gmmktime( $hh, $mm, $ss, $MM, $DD, $ ); echo "" . "Run above dates through mktime() to revert to TS" . "$mkLoc" . "$mkGMT" . "both correct" . "" ; echo "" . "Run above dates through gmmktime() to revert to TS" . "$gmmkLoc" . "$gmmkGMT" . "both incorrect???" . "" ; echo "" . "Run above (incorrect) TS through date()" . //$dtLoc = date( "D d.m.Y H:i I", $tsNow ); //$dtGMT = gmdate( "D d.m.Y H:i I", $tsNow ); "" . date( "D d.m.Y H:i I", $gmmkLoc ) . "" . "" . date( "D d.m.Y H:i I", $gmmkGMT ) . "" . "continue incorrect" . "" ; echo "" . "Run above (incorrect) TS through gmdate()" . "" . gmdate( "D d.m.Y H:i I", $gmmkLoc ) . "" . "" . gmdate( "D d.m.Y H:i I", $gmmkGMT ) . "" . "continue incorrect" . "" ; echo "" ; $ts1wk = $tsNow + ( 7 * 24 * 60 * 60 ); $dtLoc = date( "D d.m.Y H:i I", $ts1wk ); $dtGMT = gmdate( "D d.m.Y H:i I", $ts1wk ); echo "" . "TS in one week's timeproduces dates:" . "$ts1wk" . "$dtLoc" . "$dtGMT" . "local clock springs forwards" . "" ; $ss = date( "s", $ts1wk ); $hh = substr($dtLoc, 15, 2); $mm = substr($dtLoc, 18, 2); $DD = substr($dtLoc, 4, 2); $MM = substr($dtLoc, 7, 2); $ = substr($dtLoc, 10, 4); $DST = substr($dtLoc, 21, 1); $mkLoc = mktime( $hh, $mm, $ss, $MM, $DD, $, $DST ); $gmmkLoc = gmmktime( $hh, $mm, $ss, $MM, $DD, $ ); $hh = substr($dtGMT, 15, 2); $mm = substr($dtGMT, 18, 2); $DD = substr($dtGMT, 4, 2); $MM = substr($dtGMT, 7, 2); $ = substr($dtGMT, 10, 4); $DST = substr($dtGMT, 21, 1); $mkGMT = mktime( $hh, $mm, $ss, $MM, $DD, $, $DST ); $gmmkGMT = gmmktime( $hh, $mm, $ss, $MM, $DD, $ ); echo "" . "Run above dates through mktime() to revert to TS" . "$mkLoc" . "$mkGMT" . "both correct - using dst!" . "" ; echo "" . "Run above dates through gmmktime() to revert to TS" . "$gmmkLoc" . "$gmmkGMT" . "local incorrect- GMT correct" . "" ; echo "" . "Run above (correct) gmTS through date()" . "" . date( "D d.m.Y H:i I", $gmmkGMT ) . "" . "" . "continues correct" . "" ; echo "" . "Run above (correct) gmTS through gmdate()" . "" . "" . gmdate( "D d.m.Y H:i I", $gmmkGMT ) . "" . "continues correct" . "" ; echo "" . "Run above (incorrect) gmTS through date()" . "" . date( "D d.m.Y H:i I", $gmmkLoc ) . "" . "" . "continues incorrect" . "" ; echo "" . "Run above (incorrect) gmTS through gmdate()" . "" . "" . gmdate( "D d.m.Y H:i I", $gmmkLoc ) . "" . "continues incorrect" . "" ; echo "" ; $tsLoc = mktime( ); $tsGMT = gmmktime( ); echo "" . "*mktime( NOW)" . "" . date( 'D d.m.Y H:i I' ) . "" . "$tsLoc" . "$tsGMT" . "think they should be same?but GMT doesn't like it!" . "" ; $tsLoc = mktime( 0, 0, 0, 3, 31, 2002 ); $tsGMT = gmmktime( 0, 0, 0, 3, 31, 2002 ); echo "" . "put change-over date into *mktime to get TSs" . "Sun 31Mar2002 02:00" . "$tsLoc" . "$tsGMT" . "againnot same!?" . "" ; echo "" ; echo "" . "Count from midnight over GMT/BST changeover:" . "" ; echo "" . "by putting LocalTS ($tsLoc) into *date" . "Local time jumps..." . "GMT continues..." . "" ; for ( $iOffset = 0; $iOffset < 5; $iOffset ++ ) { $iHour = $iOffse
[PHP] Re: [PHP-DB] how to get consistent UTC from gmmktime (w/o dst-offset) ????
BTW for all who are following this, Patrick has cleverly illustrated that most of the northern hemisphere locations where summer time is observed will 'spring forward' this weekend. Most of the southern hemisphere locations that were in summer time did their 'fall back' last weekend! NB the two uses of the word "most"! Hi Patrick, References from the manual: - gmmktime -- Get UNIX timestamp for a GMT date - gmdate -- Format a GMT/CUT date/time Identical to the date() function except that the time returned is Greenwich Mean Time (GMT). For example, when run in Finland (GMT +0200), the first line below prints "Jan 01 1998 00:00:00", while the second prints "Dec 31 1997 22:00:00". echo date ("M d Y H:i:s", mktime (0,0,0,1,1,1998)); echo gmdate ("M d Y H:i:s", mktime (0,0,0,1,1,1998)); - The definition of the Unix Epoch is itself in GMT (and adjusted from there to local time using the +/-TZ difference): "the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)". When playing around with UNIX epoch seconds it is important to keep them time-zone separated. So try working your test backwards and putting in a data/time and then asking for the GMT and the local timestamp values. They will also be separated by the one hour/two hours. Question answered, or in my haste to get to my dinner...? Regards, =dn > i'm storing events in a mysql-db, using epoch timestamps to pinpoint the > exact date/time for an event. > > so far, I have been using localtime, being aware that there are > inconsistencies > in the number of epoch-seconds, when DST flips on and off. nevertheless, that > works fine as long as you stick to mktime() and date() for all the date-math > as in mktime(0,0,0,$month,$day+$offset,$year) > > but as soon as you leave php and have to do some calculation in javascript, > you will encounter functions that behave differently on the pc and mac > platforms. so you have to revert back to the 'add 86400-seconds' to calculate > the next day. > > so I thought it would be nice to have all the timestamps as linear, > non-dst-epoch-seconds (UTC) and I went into the gmmktime() and gmdate() > functions, assuming they would do the math in a linear second-based timespace > (in GMT, without DST). That seems to be the case for the output of 'gmdate', > that remains correct, if you count up seconds across the DST boundary (see > second example). > > but it seems that there is no way to calculate the gmt-epoch for a given > date using gmmktime without having to add/subtract the TZ manually. gmmktime > will always take your local DST-settings into calculation and upon conversion > back with gmdate, you will be off by the number of hours of your timezone. > I thought gmmktime() would handle this as if the computer would be in > Greenwich > (TZ=0), which would then leave us with the two functions encoding/decoding > consistently. > > anyone having solved this properly? > > - > print "count days:\n"; > for($day=0; $day<7; $day++){ > $timgmt = gmmktime(0,0,0,3,28+$day,2002); > $timloc = mktime(0,0,0,3,28+$day,2002); > print $timgmt . "= gmmktim epoch - " . gmdate("D d.m.Y H:i I",$timgmt) . > "\n"; > print $timloc . "= mktim epoch - " . date("D d.m.Y H:i I",$timloc) . > "\n"; > print "\n"; > } > > > count days: > 101727= gmmktim epoch - Wed 27.03.2002 23:00 0 > 101727= mktim epoch - Thu 28.03.2002 00:00 0 > > 1017356400= gmmktim epoch - Thu 28.03.2002 23:00 0 > 1017356400= mktim epoch - Fri 29.03.2002 00:00 0 > > 1017442800= gmmktim epoch - Fri 29.03.2002 23:00 0 > 1017442800= mktim epoch - Sat 30.03.2002 00:00 0 > > 1017529200= gmmktim epoch - Sat 30.03.2002 23:00 0 > 1017529200= mktim epoch - Sun 31.03.2002 00:00 0 > << 1017619200= gmmktim epoch - Mon 01.04.2002 00:00 0 <<< off by one hour due to > DST > 1017612000= mktim epoch - Mon 01.04.2002 00:00 1 > > 1017705600= gmmktim epoch - Tue 02.04.2002 00:00 0 > 1017698400= mktim epoch - Tue 02.04.2002 00:00 1 > > 1017792000= gmmktim epoch - Wed 03.04.2002 00:00 0 > 1017784800= mktim epoch - Wed 03.04.2002 00:00 1 > > > - > print "count seconds:\n"; > $baseepoch = $timloc = mktime(0,0,0,3,28,2002); > for($day=0; $day<7; $day++){ > $epochnow = $baseepoch + (60 * 60 * 24 * $day); > print $epochnow . "= gmmktim epoch - " . gmdate("D d.m.Y H:i I",$epochnow) . > "\n"; > print $epochnow . "= mktim epoch - " . date("D d.m.Y H:i I",$epochnow) . > "\n"; > print "\n"; > } > > count seconds: > 101727= gmmktim epoch - Wed 27.03.2002 23:00 0 <<< consistent, but off by > -1 (your TZ) > 101727= mktim epoch - Thu 28.03.2002 00:00 0 > > 1017356400= gmmktim epoch - Thu 28.03.2002 23:00 0 > 1017356400= mktim epoch - Fri 29.03.2002 00:00 0 > > 1017442800= gmmktim epoch - Fri 29.03.2002 23:00 0 > 1017442800= mktim epoch - Sat 30.03.2002 00:00 0 > > 1017529200= gmmktim epoch - Sat 30.03.2002