From 0e16bd9b2f21046f184556f0638a8d791ee4dfe1 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 4 Jun 2024 14:06:44 +0900 Subject: [PATCH 1/2] Fixed a problem where usec was carry up If round to the fractional part of a timestamp, a carry will occur in cases such as 999 999 9. In that case, set usec to 0 and add 1 to sec. --- ext/date/php_date.c | 5 +++++ ext/date/tests/gh14383.phpt | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 ext/date/tests/gh14383.phpt diff --git a/ext/date/php_date.c b/ext/date/php_date.c index bbcaf27a4542f..36ced301dfb18 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2540,6 +2540,11 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts) sec = (zend_long)sec_dval; usec = (int) round(fmod(ts, 1) * 1000000); + if (UNEXPECTED(usec == 1000000)) { + sec++; + usec = 0; + } + if (UNEXPECTED(usec < 0)) { if (UNEXPECTED(sec == TIMELIB_LONG_MIN)) { zend_argument_error( diff --git a/ext/date/tests/gh14383.phpt b/ext/date/tests/gh14383.phpt new file mode 100644 index 0000000000000..542eacdf0967a --- /dev/null +++ b/ext/date/tests/gh14383.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug GH-14383 (DateTime::createFromTimestamp overflowed microseconds value) +--INI-- +date.timezone=UTC +--FILE-- +format('s.u'), "\n"; +} +?> +--EXPECT-- +0.999_999_0: 00.999999 +0.999_999_1: 00.999999 +0.999_999_8: 01.000000 +0.999_999_9: 01.000000 +1.000_000_0: 01.000000 +1.000_000_1: 01.000000 +1.000_000_8: 01.000001 +1.000_000_9: 01.000001 +1.000_001_0: 01.000001 +1.000_001_0: 01.000001 +1.000_001_8: 01.000002 +1.000_001_9: 01.000002 From e25c4df869dc07e59c48c94356660ed077b143d0 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 4 Jun 2024 20:54:08 +0900 Subject: [PATCH 2/2] Added processing when usec is a negative number --- ext/date/php_date.c | 4 ++-- ext/date/tests/gh14383.phpt | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 36ced301dfb18..de617101fdd1f 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2540,8 +2540,8 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts) sec = (zend_long)sec_dval; usec = (int) round(fmod(ts, 1) * 1000000); - if (UNEXPECTED(usec == 1000000)) { - sec++; + if (UNEXPECTED(abs(usec) == 1000000)) { + sec += usec > 0 ? 1 : -1; usec = 0; } diff --git a/ext/date/tests/gh14383.phpt b/ext/date/tests/gh14383.phpt index 542eacdf0967a..dd145da9a24d7 100644 --- a/ext/date/tests/gh14383.phpt +++ b/ext/date/tests/gh14383.phpt @@ -14,17 +14,25 @@ $cases = [ [1.000_000_8, '1.000_000_8'], [1.000_000_9, '1.000_000_9'], [1.000_001_0, '1.000_001_0'], - [1.000_001_1, '1.000_001_0'], + [1.000_001_1, '1.000_001_1'], [1.000_001_8, '1.000_001_8'], [1.000_001_9, '1.000_001_9'], ]; +echo "plus:\n"; foreach ($cases as [$usec, $label]) { echo "{$label}: "; echo DateTime::createFromTimestamp($usec)->format('s.u'), "\n"; } + +echo "\nminus:\n"; +foreach ($cases as [$usec, $label]) { + echo "-{$label}: "; + echo DateTime::createFromTimestamp(-$usec)->format('s.u'), "\n"; +} ?> --EXPECT-- +plus: 0.999_999_0: 00.999999 0.999_999_1: 00.999999 0.999_999_8: 01.000000 @@ -34,6 +42,20 @@ foreach ($cases as [$usec, $label]) { 1.000_000_8: 01.000001 1.000_000_9: 01.000001 1.000_001_0: 01.000001 -1.000_001_0: 01.000001 +1.000_001_1: 01.000001 1.000_001_8: 01.000002 1.000_001_9: 01.000002 + +minus: +-0.999_999_0: 59.000001 +-0.999_999_1: 59.000001 +-0.999_999_8: 59.000000 +-0.999_999_9: 59.000000 +-1.000_000_0: 59.000000 +-1.000_000_1: 59.000000 +-1.000_000_8: 58.999999 +-1.000_000_9: 58.999999 +-1.000_001_0: 58.999999 +-1.000_001_1: 58.999999 +-1.000_001_8: 58.999998 +-1.000_001_9: 58.999998