diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 6f12a26e465e8..ef32452cfab39 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -4265,6 +4265,7 @@ PHP_FUNCTION(timezone_transitions_get) uint64_t begin = 0; bool found; zend_long timestamp_begin = ZEND_LONG_MIN, timestamp_end = INT32_MAX; + zend_long last_transition_ts = ZEND_LONG_MIN; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ll", &object, date_ce_timezone, ×tamp_begin, ×tamp_end) == FAILURE) { RETURN_THROWS(); @@ -4275,13 +4276,13 @@ PHP_FUNCTION(timezone_transitions_get) RETURN_FALSE; } -#define add_nominal() \ +#define add_by_index(i,ts) \ array_init_size(&element, 5); \ - add_assoc_long(&element, "ts", timestamp_begin); \ - add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, timestamp_begin, 0)); \ - add_assoc_long(&element, "offset", tzobj->tzi.tz->type[0].offset); \ - add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[0].isdst); \ - add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx]); \ + add_assoc_long(&element, "ts", ts); \ + add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, ts, 0)); \ + add_assoc_long(&element, "offset", tzobj->tzi.tz->type[i].offset); \ + add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[i].isdst); \ + add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[i].abbr_idx]); \ add_next_index_zval(return_value, &element); #define add(i,ts) \ @@ -4293,14 +4294,13 @@ PHP_FUNCTION(timezone_transitions_get) add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); \ add_next_index_zval(return_value, &element); -#define add_by_index(i,ts) \ - array_init_size(&element, 5); \ +#define upd_prev(i,ts) \ + element = *ZEND_HASH_ELEMENT(Z_ARRVAL_P(return_value), Z_ARRVAL_P(return_value)->nNumUsed-1); \ add_assoc_long(&element, "ts", ts); \ add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, ts, 0)); \ - add_assoc_long(&element, "offset", tzobj->tzi.tz->type[i].offset); \ - add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[i].isdst); \ - add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[i].abbr_idx]); \ - add_next_index_zval(return_value, &element); + add_assoc_long(&element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \ + add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \ + add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); #define add_from_tto(to,ts) \ array_init_size(&element, 5); \ @@ -4311,6 +4311,7 @@ PHP_FUNCTION(timezone_transitions_get) add_assoc_string(&element, "abbr", (to)->abbr); \ add_next_index_zval(return_value, &element); +#define add_nominal() add_by_index(0, timestamp_begin) #define add_last() add(tzobj->tzi.tz->bit64.timecnt - 1, timestamp_begin) array_init(return_value); @@ -4327,8 +4328,10 @@ PHP_FUNCTION(timezone_transitions_get) if (tzobj->tzi.tz->trans[begin] > timestamp_begin) { if (begin > 0) { add(begin - 1, timestamp_begin); + last_transition_ts = timestamp_begin; } else { add_nominal(); + last_transition_ts = timestamp_begin; } found = 1; break; @@ -4344,24 +4347,32 @@ PHP_FUNCTION(timezone_transitions_get) timelib_time_offset *tto = timelib_get_time_zone_info(timestamp_begin, tzobj->tzi.tz); add_from_tto(tto, timestamp_begin); timelib_time_offset_dtor(tto); + last_transition_ts = timestamp_begin; } else { add_last(); + last_transition_ts = timestamp_begin; } } else { add_nominal(); + last_transition_ts = timestamp_begin; } } else { for (uint64_t i = begin; i < tzobj->tzi.tz->bit64.timecnt; ++i) { - if (tzobj->tzi.tz->trans[i] < timestamp_end) { + if (tzobj->tzi.tz->trans[i] > timestamp_end) { + return; + } + + if (tzobj->tzi.tz->trans[i] > timestamp_begin) { add(i, tzobj->tzi.tz->trans[i]); + last_transition_ts = tzobj->tzi.tz->trans[i]; } else { - return; + upd_prev(i, timestamp_begin); } } } + if (tzobj->tzi.tz->posix_info && tzobj->tzi.tz->posix_info->dst_end) { timelib_sll start_y, end_y, dummy_m, dummy_d; - timelib_sll last_transition_ts = tzobj->tzi.tz->trans[tzobj->tzi.tz->bit64.timecnt - 1]; /* Find out year for last transition */ timelib_unixtime2date(last_transition_ts, &start_y, &dummy_m, &dummy_d); diff --git a/ext/date/tests/bug80963.phpt b/ext/date/tests/bug80963.phpt index c7f02a7397fcf..c0e5753606334 100644 --- a/ext/date/tests/bug80963.phpt +++ b/ext/date/tests/bug80963.phpt @@ -27,7 +27,7 @@ array(5) { ["abbr"]=> string(3) "GMT" } -int(237) +int(23%d) array(5) { ["ts"]=> int(2140668000) @@ -40,7 +40,7 @@ array(5) { ["abbr"]=> string(3) "EST" } -int(144) +int(14%d) array(5) { ["ts"]=> int(2140045200) diff --git a/ext/date/tests/gh18051-32bit.phpt b/ext/date/tests/gh18051-32bit.phpt new file mode 100644 index 0000000000000..aedfaea059fb4 --- /dev/null +++ b/ext/date/tests/gh18051-32bit.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bug GH-18051 (Interger overflow on 32bit) +--SKIPIF-- +getTransitions(), 0, 3)); + +--EXPECT-- +array(3) { + [0]=> + array(5) { + ["ts"]=> + int(-2147483648) + ["time"]=> + string(25) "1901-12-13T20:45:52+00:00" + ["offset"]=> + int(-18000) + ["isdst"]=> + bool(false) + ["abbr"]=> + string(3) "EST" + } + [1]=> + array(5) { + ["ts"]=> + int(-1633280400) + ["time"]=> + string(25) "1918-03-31T07:00:00+00:00" + ["offset"]=> + int(-14400) + ["isdst"]=> + bool(true) + ["abbr"]=> + string(3) "EDT" + } + [2]=> + array(5) { + ["ts"]=> + int(-1615140000) + ["time"]=> + string(25) "1918-10-27T06:00:00+00:00" + ["offset"]=> + int(-18000) + ["isdst"]=> + bool(false) + ["abbr"]=> + string(3) "EST" + } +} diff --git a/ext/date/tests/gh18051.phpt b/ext/date/tests/gh18051.phpt new file mode 100644 index 0000000000000..e0f4b4234e1f3 --- /dev/null +++ b/ext/date/tests/gh18051.phpt @@ -0,0 +1,95 @@ +--TEST-- +Bug GH-18051 (DateTimeZone->getTransitions can return first transition twice) +--FILE-- +getTransitions(922582799, 941331599)); +var_dump($tzBln->getTransitions(922582800, 941331600)); +var_dump($tzBln->getTransitions(922582801, 941331601)); + +--EXPECT-- +array(2) { + [0]=> + array(5) { + ["ts"]=> + int(922582799) + ["time"]=> + string(25) "1999-03-28T00:59:59+00:00" + ["offset"]=> + int(3600) + ["isdst"]=> + bool(false) + ["abbr"]=> + string(3) "CET" + } + [1]=> + array(5) { + ["ts"]=> + int(922582800) + ["time"]=> + string(25) "1999-03-28T01:00:00+00:00" + ["offset"]=> + int(7200) + ["isdst"]=> + bool(true) + ["abbr"]=> + string(4) "CEST" + } +} +array(2) { + [0]=> + array(5) { + ["ts"]=> + int(922582800) + ["time"]=> + string(25) "1999-03-28T01:00:00+00:00" + ["offset"]=> + int(7200) + ["isdst"]=> + bool(true) + ["abbr"]=> + string(4) "CEST" + } + [1]=> + array(5) { + ["ts"]=> + int(941331600) + ["time"]=> + string(25) "1999-10-31T01:00:00+00:00" + ["offset"]=> + int(3600) + ["isdst"]=> + bool(false) + ["abbr"]=> + string(3) "CET" + } +} +array(2) { + [0]=> + array(5) { + ["ts"]=> + int(922582801) + ["time"]=> + string(25) "1999-03-28T01:00:01+00:00" + ["offset"]=> + int(7200) + ["isdst"]=> + bool(true) + ["abbr"]=> + string(4) "CEST" + } + [1]=> + array(5) { + ["ts"]=> + int(941331600) + ["time"]=> + string(25) "1999-10-31T01:00:00+00:00" + ["offset"]=> + int(3600) + ["isdst"]=> + bool(false) + ["abbr"]=> + string(3) "CET" + } +}