diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 02236d6676ea7..ef1b8621c7cf0 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2525,16 +2525,14 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts) zend_long sec; int usec; - if (UNEXPECTED(isnan(sec_dval) - || sec_dval >= (double)TIMELIB_LONG_MAX - || sec_dval < (double)TIMELIB_LONG_MIN - )) { - zend_throw_error( + if (UNEXPECTED(isnan(sec_dval) || !PHP_DATE_DOUBLE_FITS_LONG(sec_dval))) { + zend_argument_error( date_ce_date_range_error, - "Seconds must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ", %g given", + 1, + "must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ".999999, %g given", TIMELIB_LONG_MIN, TIMELIB_LONG_MAX, - sec_dval + ts ); return false; } @@ -2543,6 +2541,18 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts) usec = (int)(fmod(ts, 1) * 1000000); if (UNEXPECTED(usec < 0)) { + if (UNEXPECTED(sec == TIMELIB_LONG_MIN)) { + zend_argument_error( + date_ce_date_range_error, + 1, + "must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ".999999, %g given", + TIMELIB_LONG_MIN, + TIMELIB_LONG_MAX, + ts + ); + return false; + } + sec = sec - 1; usec = 1000000 + usec; } diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 97f4bcc0a843f..f1b7c892001a9 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -20,6 +20,23 @@ #include "lib/timelib.h" #include "Zend/zend_hash.h" +/* Same as SIZEOF_ZEND_LONG but using TIMELIB_LONG_MAX/MIN */ +#if TIMELIB_LONG_MAX == INT32_MAX +# define PHP_DATE_SIZEOF_LONG 4 +#elif TIMELIB_LONG_MAX == INT64_MAX +# define PHP_DATE_SIZEOF_LONG 8 +#else +# error "Unknown TIMELIB LONG SIZE" +#endif + +/* Same as ZEND_DOUBLE_FITS_LONG but using TIMELIB_LONG_MAX/MIN */ +#if PHP_DATE_SIZEOF_LONG == 4 +# define PHP_DATE_DOUBLE_FITS_LONG(d) (!((d) > (double)TIMELIB_LONG_MAX || (d) < (double)TIMELIB_LONG_MIN)) +#elif PHP_DATE_SIZEOF_LONG == 8 + /* >= as (double)TIMELIB_LONG_MAX is outside signed range */ +# define PHP_DATE_DOUBLE_FITS_LONG(d) (!((d) >= (double)TIMELIB_LONG_MAX || (d) < (double)TIMELIB_LONG_MIN)) +#endif + #include "php_version.h" #define PHP_DATE_VERSION PHP_VERSION diff --git a/ext/date/tests/createFromTimestamp.phpt b/ext/date/tests/createFromTimestamp.phpt index 492a831731e12..474a55bc6db6d 100644 --- a/ext/date/tests/createFromTimestamp.phpt +++ b/ext/date/tests/createFromTimestamp.phpt @@ -1,9 +1,5 @@ --TEST-- Tests for DateTime[Immutable]::createFromTimestamp ---SKIPIF-- - --INI-- date.timezone=Europe/London --FILE-- @@ -12,10 +8,6 @@ date.timezone=Europe/London class MyDateTime extends DateTime {}; class MyDateTimeImmutable extends DateTimeImmutable {}; -define('MAX_32BIT', 2147483647); -// -2147483648 may not be expressed in a literal due to parsing peculiarities. -define('MIN_32BIT', -2147483647 - 1); - $timestamps = array( 1696883232, -1696883232, @@ -26,9 +18,6 @@ $timestamps = array( 0, 0.0, -0.0, - MAX_32BIT, - MIN_32BIT, - MIN_32BIT - 0.5, PHP_INT_MAX + 1024.0, PHP_INT_MIN - 1025.0, NAN, @@ -212,64 +201,16 @@ DateTimeImmutable::createFromTimestamp(-0.0): object(DateTimeImmutable)#%d (3) { ["timezone"]=> string(6) "+00:00" } -DateTime::createFromTimestamp(2147483647): object(DateTime)#%d (3) { - ["date"]=> - string(26) "2038-01-19 03:14:07.000000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTimeImmutable::createFromTimestamp(2147483647): object(DateTimeImmutable)#%d (3) { - ["date"]=> - string(26) "2038-01-19 03:14:07.000000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTime::createFromTimestamp(-2147483648): object(DateTime)#%d (3) { - ["date"]=> - string(26) "1901-12-13 20:45:52.000000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTimeImmutable::createFromTimestamp(-2147483648): object(DateTimeImmutable)#%d (3) { - ["date"]=> - string(26) "1901-12-13 20:45:52.000000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTime::createFromTimestamp(-2147483648.5): object(DateTime)#%d (3) { - ["date"]=> - string(26) "1901-12-13 20:45:51.500000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTimeImmutable::createFromTimestamp(-2147483648.5): object(DateTimeImmutable)#%d (3) { - ["date"]=> - string(26) "1901-12-13 20:45:51.500000" - ["timezone_type"]=> - int(1) - ["timezone"]=> - string(6) "+00:00" -} -DateTime::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given -DateTimeImmutable::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given -DateTime::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given -DateTimeImmutable::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given -DateTime::createFromTimestamp(NAN): DateRangeError: Seconds must be a finite number between %i and %i, NAN given -DateTimeImmutable::createFromTimestamp(NAN): DateRangeError: Seconds must be a finite number between %i and %i, NAN given -DateTime::createFromTimestamp(INF): DateRangeError: Seconds must be a finite number between %i and %i, INF given -DateTimeImmutable::createFromTimestamp(INF): DateRangeError: Seconds must be a finite number between %i and %i, INF given -DateTime::createFromTimestamp(-INF): DateRangeError: Seconds must be a finite number between %i and %i, -INF given -DateTimeImmutable::createFromTimestamp(-INF): DateRangeError: Seconds must be a finite number between %i and %i, -INF given +DateTime::createFromTimestamp(%f): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given +DateTimeImmutable::createFromTimestamp(%f): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given +DateTime::createFromTimestamp(%f): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given +DateTimeImmutable::createFromTimestamp(%f): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given +DateTime::createFromTimestamp(NAN): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, NAN given +DateTimeImmutable::createFromTimestamp(NAN): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, NAN given +DateTime::createFromTimestamp(INF): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, INF given +DateTimeImmutable::createFromTimestamp(INF): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, INF given +DateTime::createFromTimestamp(-INF): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, -INF given +DateTimeImmutable::createFromTimestamp(-INF): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, -INF given MyDateTime::createFromTimestamp(0): object(MyDateTime)#%d (3) { ["date"]=> string(26) "1970-01-01 00:00:00.000000" diff --git a/ext/date/tests/createFromTimestamp_32bit.phpt b/ext/date/tests/createFromTimestamp_32bit.phpt new file mode 100644 index 0000000000000..8052acfca135a --- /dev/null +++ b/ext/date/tests/createFromTimestamp_32bit.phpt @@ -0,0 +1,126 @@ +--TEST-- +Tests for DateTime[Immutable]::createFromTimestamp 32bit variant +--SKIPIF-- + +--INI-- +date.timezone=Europe/London +--FILE-- +getMessage() . "\n"; + } + + echo 'DateTimeImmutable::createFromTimestamp(' . var_export($ts, true) . '): '; + try { + var_dump(DateTimeImmutable::createFromTimestamp($ts)); + } catch (Throwable $e) { + echo get_class($e) . ': ' . $e->getMessage() . "\n"; + } +} + +?> +--EXPECTF-- +DateTime::createFromTimestamp(2147483647): object(DateTime)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:07.000000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTimeImmutable::createFromTimestamp(2147483647): object(DateTimeImmutable)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:07.000000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTime::createFromTimestamp(-2147483647-1): object(DateTime)#%d (3) { + ["date"]=> + string(26) "1901-12-13 20:45:52.000000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTimeImmutable::createFromTimestamp(-2147483647-1): object(DateTimeImmutable)#%d (3) { + ["date"]=> + string(26) "1901-12-13 20:45:52.000000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTime::createFromTimestamp(2147483647.5): object(DateTime)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:07.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTimeImmutable::createFromTimestamp(2147483647.5): object(DateTimeImmutable)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:07.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTime::createFromTimestamp(-2147483647.5): object(DateTime)#%d (3) { + ["date"]=> + string(26) "1901-12-13 20:45:52.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTimeImmutable::createFromTimestamp(-2147483647.5): object(DateTimeImmutable)#%d (3) { + ["date"]=> + string(26) "1901-12-13 20:45:52.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTime::createFromTimestamp(2147483646.5): object(DateTime)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:06.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTimeImmutable::createFromTimestamp(2147483646.5): object(DateTimeImmutable)#%d (3) { + ["date"]=> + string(26) "2038-01-19 03:14:06.500000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+00:00" +} +DateTime::createFromTimestamp(-2147483648.5): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given +DateTimeImmutable::createFromTimestamp(-2147483648.5): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given +DateTime::createFromTimestamp(2147484671.0): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, 2.14748e+9 given +DateTimeImmutable::createFromTimestamp(2147484671.0): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, 2.14748e+9 given +DateTime::createFromTimestamp(-2147484673.0): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given +DateTimeImmutable::createFromTimestamp(-2147484673.0): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given