From 0747616f845dd68266b81319a5ba391979db28cb Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 22 Jun 2023 17:49:51 +0100 Subject: [PATCH] Fixed GH-11368: Date modify returns invalid datetime --- NEWS | 3 +++ ext/date/lib/timelib.h | 2 +- ext/date/php_date.c | 26 +++++++++++++------------- ext/date/tests/bug-gh11368.phpt | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 ext/date/tests/bug-gh11368.phpt diff --git a/NEWS b/NEWS index ab369d5d8a1..998c5c43014 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.22 +- Date: + . Fixed bug GH-11368 (Date modify returns invalid datetime). (Derick) + - PCRE: . Mangle PCRE regex cache key with JIT option. (mvorisek) diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index 03a83a4d290..a08a42f6694 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -378,7 +378,7 @@ typedef struct _timelib_tzdb { #define TIMELIB_OVERRIDE_TIME 0x01 #define TIMELIB_NO_CLONE 0x02 -#define TIMELIB_UNSET -99999 +#define TIMELIB_UNSET -9999999 /* An entry for each of these error codes is also in the * timelib_error_messages array in timelib.c. diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 92ebd4f9c68..7b0f223f92f 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2100,7 +2100,7 @@ static HashTable *date_object_get_properties_interval(zend_object *object) /* {{ PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior); PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of); PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert); - if (intervalobj->diff->days != -99999) { + if (intervalobj->diff->days != TIMELIB_UNSET) { PHP_DATE_INTERVAL_ADD_PROPERTY("days", days); } else { ZVAL_FALSE(&zv); @@ -2733,7 +2733,7 @@ void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time * array_init(return_value); #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \ - if (parsed_time->elem == -99999) { \ + if (parsed_time->elem == TIMELIB_UNSET) { \ add_assoc_bool(return_value, #name, 0); \ } else { \ add_assoc_long(return_value, #name, parsed_time->elem); \ @@ -2745,7 +2745,7 @@ void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time * PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute, i); PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second, s); - if (parsed_time->us == -99999) { + if (parsed_time->us == TIMELIB_UNSET) { add_assoc_bool(return_value, "fraction", 0); } else { add_assoc_double(return_value, "fraction", (double)parsed_time->us / 1000000.0); @@ -2880,21 +2880,21 @@ static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ dateobj->time->have_relative = tmp_time->have_relative; dateobj->time->sse_uptodate = 0; - if (tmp_time->y != -99999) { + if (tmp_time->y != TIMELIB_UNSET) { dateobj->time->y = tmp_time->y; } - if (tmp_time->m != -99999) { + if (tmp_time->m != TIMELIB_UNSET) { dateobj->time->m = tmp_time->m; } - if (tmp_time->d != -99999) { + if (tmp_time->d != TIMELIB_UNSET) { dateobj->time->d = tmp_time->d; } - if (tmp_time->h != -99999) { + if (tmp_time->h != TIMELIB_UNSET) { dateobj->time->h = tmp_time->h; - if (tmp_time->i != -99999) { + if (tmp_time->i != TIMELIB_UNSET) { dateobj->time->i = tmp_time->i; - if (tmp_time->s != -99999) { + if (tmp_time->s != TIMELIB_UNSET) { dateobj->time->s = tmp_time->s; } else { dateobj->time->s = 0; @@ -2905,7 +2905,7 @@ static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ } } - if (tmp_time->us != -99999) { + if (tmp_time->us != TIMELIB_UNSET) { dateobj->time->us = tmp_time->us; } @@ -3914,7 +3914,7 @@ static zval *date_interval_read_property(zend_object *object, zend_string *name, if (fvalue != -1) { ZVAL_DOUBLE(retval, fvalue); - } else if (value != -99999) { + } else if (value != TIMELIB_UNSET) { ZVAL_LONG(retval, value); } else { ZVAL_FALSE(retval); @@ -4039,7 +4039,7 @@ static int php_date_interval_initialize_from_hash(zval **return_value, php_inter do { \ zval *z_arg = zend_hash_str_find(myht, "days", sizeof("days") - 1); \ if (z_arg && Z_TYPE_P(z_arg) == IS_FALSE) { \ - (*intobj)->diff->member = -99999; \ + (*intobj)->diff->member = TIMELIB_UNSET; \ } else if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \ zend_string *str = zval_get_string(z_arg); \ DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \ @@ -4199,7 +4199,7 @@ static zend_string *date_interval_format(char *format, size_t format_len, timeli case 'f': length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) t->us); break; case 'a': { - if ((int) t->days != -99999) { + if ((int) t->days != TIMELIB_UNSET) { length = slprintf(buffer, sizeof(buffer), "%d", (int) t->days); } else { length = slprintf(buffer, sizeof(buffer), "(unknown)"); diff --git a/ext/date/tests/bug-gh11368.phpt b/ext/date/tests/bug-gh11368.phpt new file mode 100644 index 00000000000..da1eba866bd --- /dev/null +++ b/ext/date/tests/bug-gh11368.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug GH-11368: Date modify returns invalid datetime +--INI-- +date.timezone=UTC +--FILE-- +setTime(1,1,1,1 /* If set to any other number, it works fine */); +var_dump($datetime); + +$datetime->modify('-100 ms'); +var_dump($datetime); + +?> +--EXPECTF-- +object(DateTime)#1 (3) { + ["date"]=> + string(26) "2023-06-04 01:01:01.000001" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#1 (3) { + ["date"]=> + string(26) "2023-06-04 01:01:00.900001" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +}