- Fixed bug #30096 (gmmktime does not return the corrent time).

- Re-implemented mktime and gmmktime with new date time library.
- Added testcase for bug #30096, updated test cases for E_STRICT warning of
  is_dst parameter usage for mktime/gmmktime.
This commit is contained in:
Derick Rethans 2005-07-03 14:27:31 +00:00
parent f4b5a51952
commit ed02f202f0
10 changed files with 161 additions and 209 deletions

1
NEWS
View File

@ -19,6 +19,7 @@ PHP NEWS
- Fixed bug #30828 (debug_backtrace() reports incorrect class in overridden
methods). (Dmitry)
- Fixed bug #30519 (Interface not existing says Class not found). (Dmitry)
- Fixed bug #30096 (gmmktime does not return the corrent time). (Derick)
- Fixed bug #30052 (Crash on shutdown after odbc_pconnect()). (Edin)
- Fixed bug #28377 (debug_backtrace is intermittently passing args). (Dmitry)
- Fixed bug #27268 (Bad references accentuated by clone). (Dmitry)

View File

@ -31,6 +31,8 @@
function_entry date_functions[] = {
PHP_FE(date, NULL)
PHP_FE(gmdate, NULL)
PHP_FE(mktime, NULL)
PHP_FE(gmmktime, NULL)
PHP_FE(strtotime, NULL)
PHP_FE(date_timezone_set, NULL)
PHP_FE(date_timezone_get, NULL)
@ -400,6 +402,105 @@ PHP_FUNCTION(strtotime)
}
/* }}} */
PHPAPI static void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
{
long hou, min, sec, mon, day, yea, dst = -1;;
timelib_time *now;
timelib_tzinfo *tzi;
long ts, adjust_seconds = 0;
int error;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lllllll", &hou, &min, &sec, &mon, &day, &yea, &dst) == FAILURE) {
RETURN_FALSE;
}
/* Initialize structure with current time */
now = timelib_time_ctor();
if (gmt) {
timelib_unixtime2gmt(now, (timelib_sll) time(NULL));
} else {
tzi = get_timezone_info(TSRMLS_C);
timelib_unixtime2local(now, (timelib_sll) time(NULL), tzi);
}
/* Fill in the new data */
switch (ZEND_NUM_ARGS()) {
case 7:
/* break intentionally missing */
case 6:
now->y = yea;
/* break intentionally missing again */
case 5:
now->d = day;
/* break missing intentionally here too */
case 4:
now->m = mon;
/* and here */
case 3:
now->s = sec;
/* yup, this break isn't here on purpose too */
case 2:
now->i = min;
/* last intentionally missing break */
case 1:
now->h = hou;
break;
default:
php_error_docref(NULL TSRMLS_CC, E_STRICT, "You should be using the time() function instead.");
}
/* Update the timestamp */
if (gmt) {
timelib_update_ts(now, NULL);
} else {
timelib_update_ts(now, tzi);
}
/* Support for the deprecated is_dst parameter */
if (dst != -1) {
php_error_docref(NULL TSRMLS_CC, E_STRICT, "The is_dst parameter is deprecated.");
if (gmt) {
/* GMT never uses DST */
if (dst == 1) {
adjust_seconds = -3600;
}
} else {
/* Figure out is_dst for current TS */
timelib_time_offset *tmp_offset;
tmp_offset = timelib_get_time_zone_info(now->sse, tzi);
if (dst == 1 && tmp_offset->is_dst == 0) {
adjust_seconds = -3600;
}
if (dst == 0 && tmp_offset->is_dst == 1) {
adjust_seconds = +3600;
}
}
}
/* Clean up and return */
ts = timelib_date_to_int(now, &error);
ts += adjust_seconds;
timelib_time_dtor(now);
if (error) {
RETURN_FALSE;
} else {
RETURN_LONG(ts);
}
}
/* {{{ proto int mktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a date */
PHP_FUNCTION(mktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto int gmmktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a GMT date */
PHP_FUNCTION(gmmktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
PHP_FUNCTION(date_timezone_set)
{
char *zone;

View File

@ -27,6 +27,10 @@ extern zend_module_entry date_module_entry;
PHP_FUNCTION(date);
PHP_FUNCTION(gmdate);
PHP_FUNCTION(strtotime);
PHP_FUNCTION(mktime);
PHP_FUNCTION(gmmktime);
PHP_FUNCTION(date_timezone_set);
PHP_FUNCTION(date_timezone_get);
@ -49,5 +53,6 @@ ZEND_END_MODULE_GLOBALS(date)
/* Backwards compability wrapper */
signed long php_parse_date(char *string, signed long *now);
PHPAPI static void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt);
#endif /* PHP_DATE_H */

View File

@ -0,0 +1,48 @@
--TEST--
Bug #30096 (gmmktime does not return the corrent time)
--INI--
error_reporting=2047
--FILE--
<?php
echo "no dst --> dst\n";
$ts = -1;
gm_date_check(01,00,00,03,27,2005);
gm_date_check(02,00,00,03,27,2005);
gm_date_check(03,00,00,03,27,2005);
gm_date_check(04,00,00,03,27,2005);
echo "\ndst --> no dst\n";
$ts = -1;
gm_date_check(01,00,00,10,30,2005);
gm_date_check(02,00,00,10,30,2005);
gm_date_check(03,00,00,10,30,2005);
gm_date_check(04,00,00,10,30,2005);
function gm_date_check($hour, $minute, $second, $month, $day, $year) {
global $ts, $tsold;
echo "gmmktime($hour,$minute,$second,$month,$day,$year): ";
$tsold = $ts;
$ts = gmmktime($hour, $minute, $second, $month, $day, $year);
echo $ts, " | gmdate('r', $ts):", gmdate('r', $ts);
if ($tsold > 0) {
echo " | Diff: " . ($ts - $tsold);
}
echo "\n";
}
?>
--EXPECT--
no dst --> dst
gmmktime(1,0,0,3,27,2005): 1111885200 | gmdate('r', 1111885200):Sun, 27 Mar 2005 01:00:00 +0000
gmmktime(2,0,0,3,27,2005): 1111888800 | gmdate('r', 1111888800):Sun, 27 Mar 2005 02:00:00 +0000 | Diff: 3600
gmmktime(3,0,0,3,27,2005): 1111892400 | gmdate('r', 1111892400):Sun, 27 Mar 2005 03:00:00 +0000 | Diff: 3600
gmmktime(4,0,0,3,27,2005): 1111896000 | gmdate('r', 1111896000):Sun, 27 Mar 2005 04:00:00 +0000 | Diff: 3600
dst --> no dst
gmmktime(1,0,0,10,30,2005): 1130634000 | gmdate('r', 1130634000):Sun, 30 Oct 2005 01:00:00 +0000
gmmktime(2,0,0,10,30,2005): 1130637600 | gmdate('r', 1130637600):Sun, 30 Oct 2005 02:00:00 +0000 | Diff: 3600
gmmktime(3,0,0,10,30,2005): 1130641200 | gmdate('r', 1130641200):Sun, 30 Oct 2005 03:00:00 +0000 | Diff: 3600
gmmktime(4,0,0,10,30,2005): 1130644800 | gmdate('r', 1130644800):Sun, 30 Oct 2005 04:00:00 +0000 | Diff: 3600

View File

@ -169,8 +169,6 @@ function_entry basic_functions[] = {
PHP_FE(time_sleep_until, NULL)
#endif
PHP_FE(time, NULL)
PHP_FE(mktime, NULL)
PHP_FE(gmmktime, NULL)
#if HAVE_STRPTIME
PHP_FE(strptime, NULL)
#endif

View File

@ -77,210 +77,6 @@ PHP_FUNCTION(time)
}
/* }}} */
/* {{{ php_mktime
*/
PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm)
{
pval **arguments[7];
struct tm *ta, tmbuf;
time_t t, seconds;
int i, gmadjust, arg_count = ZEND_NUM_ARGS();
int is_dst = -1, chgsecs = 0;
long val;
if (arg_count > 7 || zend_get_parameters_array_ex(arg_count, arguments) == FAILURE) {
WRONG_PARAM_COUNT;
}
/* convert supplied arguments to longs */
for (i = 0; i < arg_count; i++) {
convert_to_long_ex(arguments[i]);
}
t = time(NULL);
#ifdef HAVE_TZSET
tzset();
#endif
/*
** Set default time parameters with local time values,
** EVEN when some GMT time parameters are specified!
** This may give strange result, with PHP gmmktime(0, 0, 0),
** which is assumed to return GMT midnight time
** for today (in localtime), so that the result time may be
** AFTER or BEFORE the current time.
** May be we should initialize tn using gmtime(), so that
** default parameters for PHP gmmktime would be the current
** GMT time values...
*/
ta = php_localtime_r(&t, &tmbuf);
/* Let DST be unknown. mktime() should compute the right value
** and behave correctly. Unless the user overrides this.
*/
ta->tm_isdst = -1;
/*
** Now change date values with supplied parameters.
*/
switch(arg_count) {
case 7: /* daylight saving time flag */
#ifdef PHP_WIN32
if (daylight > 0) {
ta->tm_isdst = is_dst = Z_LVAL_PP(arguments[6]);
} else {
ta->tm_isdst = is_dst = 0;
}
#else
ta->tm_isdst = is_dst = Z_LVAL_PP(arguments[6]);
#endif
/* fall-through */
case 6: /* year */
/* special case:
a zero in year, month and day is considered illegal
as it would be interpreted as 30.11.1999 otherwise
*/
if ( ( Z_LVAL_PP(arguments[5])==0)
&&(Z_LVAL_PP(arguments[4])==0)
&&(Z_LVAL_PP(arguments[3])==0)
) {
RETURN_LONG(-1);
}
/*
** Accept parameter in range 0..1000 interpreted as 1900..2900
** (if 100 is given, it means year 2000)
** or in range 1001..9999 interpreted as is (this will store
** negative tm_year for years in range 1001..1899)
** This function is then Y2K ready, and accepts a wide range of
** dates including the whole gregorian calendar.
** But it cannot represent ancestral dates prior to year 1001.
** Additionally, input parameters of 0..70 are mapped to 100..170
*/
if (Z_LVAL_PP(arguments[5]) < 70)
ta->tm_year = Z_LVAL_PP(arguments[5]) + 100;
else
ta->tm_year = Z_LVAL_PP(arguments[5])
- ((Z_LVAL_PP(arguments[5]) > 1000) ? 1900 : 0);
/* fall-through */
case 5: /* day in month (1-based) */
val = (*arguments[4])->value.lval;
if (val < 1) {
chgsecs += (1-val) * 60*60*24;
val = 1;
}
ta->tm_mday = val;
/* fall-through */
case 4: /* month (zero-based) */
val = (*arguments[3])->value.lval - 1;
while (val < 0) {
val += 12; ta->tm_year--;
}
ta->tm_mon = val;
/* fall-through */
case 3: /* second */
val = (*arguments[2])->value.lval;
if (val < 1) {
chgsecs += (1-val); val = 1;
}
ta->tm_sec = val;
/* fall-through */
case 2: /* minute */
val = (*arguments[1])->value.lval;
if (val < 1) {
chgsecs += (1-val) * 60; val = 1;
}
ta->tm_min = val;
/* fall-through */
case 1: /* hour */
val = (*arguments[0])->value.lval;
/*
We avoid midnight and a couple of hours after midnight here to work around
various OS-level bugs in mktime and specifically daylight savings time issues
in many mktime implementation.
See bugs #27533 and #27719 for more info.
*/
if (val < 4) {
chgsecs += (4-val) * 60*60; val = 4;
}
ta->tm_hour = val;
/* fall-through */
case 0:
break;
}
t = mktime(ta);
#ifdef PHP_WIN32
if (t - chgsecs < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Windows does not support negative values for this function");
RETURN_LONG(-1);
}
#endif
seconds = t - chgsecs;
/*
Here we check to see if the chgsecs fuzz factor we applied caused us to
move from dst to non-dst or vice-versa. If so we adjust accordingly to
avoid being off by an hour on the dst changeover date.
*/
if (is_dst == -1) {
struct tm t1, t2;
t1 = *localtime(&t);
t2 = *localtime(&seconds);
if (t1.tm_isdst != t2.tm_isdst) {
seconds += (t1.tm_isdst == 1) ? 3600 : -3600;
ta = localtime(&seconds);
}
/*
If the user didn't specify whether the timestamp passed in was dst or not
then we fill it in based on the dst setting at the evaluated timestamp
at the current TZ
*/
is_dst = ta->tm_isdst;
}
if (gm) {
#if HAVE_TM_GMTOFF
/*
** mktime(ta) very nicely just filled ta->tm_gmtoff with
** the exactly right value for adjustment if we want GMT.
*/
gmadjust = ta->tm_gmtoff;
#else
/*
** If correcting for daylight savings time, we set the adjustment to
** the value of timezone - 3600 seconds.
*/
#ifdef __CYGWIN__
gmadjust = -(is_dst ? _timezone - 3600 : _timezone);
#else
gmadjust = -(is_dst ? timezone - 3600 : timezone);
#endif
#endif
seconds += gmadjust;
}
RETURN_LONG(seconds);
}
/* }}} */
/* {{{ proto int mktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a date */
PHP_FUNCTION(mktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto int gmmktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a GMT date */
PHP_FUNCTION(gmmktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ php_idate
*/
PHPAPI int php_idate(char format, int timestamp, int gm)

View File

@ -23,8 +23,6 @@
#define DATETIME_H
PHP_FUNCTION(time);
PHP_FUNCTION(mktime);
PHP_FUNCTION(gmmktime);
PHP_FUNCTION(idate);
PHP_FUNCTION(localtime);
PHP_FUNCTION(getdate);
@ -39,7 +37,6 @@ PHP_FUNCTION(gmstrftime);
PHPAPI int php_idate(char format, int timestamp, int gm);
PHPAPI char *php_std_date(time_t t TSRMLS_DC);
PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm);
#if HAVE_STRFTIME
PHPAPI void _php_strftime(INTERNAL_FUNCTION_PARAMETERS, int gm);
#endif

View File

@ -1,5 +1,7 @@
--TEST--
Check for mktime with out-of-range parameters
--INI--
error_reporting=2047
--FILE--
<?php
# MacOS/X libc implementation doesn't treat out-of-range values

View File

@ -1,5 +1,7 @@
--TEST--
Bug #27719: mktime returns incorrect timestamp for dst days
--INI--
error_reporting=2047
--FILE--
<?php /* $Id$ */
putenv("TZ=EST"); // No DST

View File

@ -1,5 +1,7 @@
--TEST--
mktime()
--INI--
error_reporting=2047
--FILE--
<?php /* $Id$ */
$timezones = array(