Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.4.27

- Calendar:
. Fixed bug GH-23915 (easter_date() reports inconsistent year ranges and
returns -1 after the year 3000 on Windows). (Marc Bennewitz)

- CLI
. Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during
request activation). (matyhtf)
Expand Down
47 changes: 23 additions & 24 deletions ext/calendar/easter.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,29 @@ static void _cal_easter(INTERNAL_FUNCTION_PARAMETERS, bool gm)
struct tm te;
zend_long year, golden, solar, lunar, pfm, dom, tmp, easter, result;
zend_long method = CAL_EASTER_DEFAULT;
const zend_long max_year = (zend_long)(ZEND_LONG_MAX / 5) * 4;
zend_long min_year, max_year;
bool year_is_null = 1;

if (gm) {
/* Timestamps only start after 1970 and are calculated by mktime() */
min_year = 1970;
#ifdef ZEND_ENABLE_ZVAL_LONG64
# ifdef PHP_WIN32
/* mktime() on Windows only goes up to the year 3000 */
max_year = 3000;
# else
/* Timestamps only go up to the year 2.000.000.000 */
max_year = 2000000000;
# endif
#else
max_year = 2037;
#endif
} else {
/* Keep the calculations below from overflowing */
min_year = 1;
max_year = (ZEND_LONG_MAX / 5) * 4;
}

if (zend_parse_parameters(ZEND_NUM_ARGS(),
"|l!l", &year, &year_is_null, &method) == FAILURE) {
RETURN_THROWS();
Expand All @@ -54,31 +74,10 @@ static void _cal_easter(INTERNAL_FUNCTION_PARAMETERS, bool gm)
}
}

if (year <= 0 || year > max_year) {
zend_argument_value_error(1, "must be between 1 and " ZEND_LONG_FMT, max_year);
RETURN_THROWS();
}

#ifdef ZEND_ENABLE_ZVAL_LONG64
/* Compiling for 64bit, allow years between 1970 and 2.000.000.000 */
if (gm && year < 1970) {
/* timestamps only start after 1970 */
zend_argument_value_error(1, "must be a year after 1970 (inclusive)");
RETURN_THROWS();
}

if (gm && year > 2000000000) {
/* timestamps only go up to the year 2.000.000.000 */
zend_argument_value_error(1, "must be a year before 2.000.000.000 (inclusive)");
RETURN_THROWS();
}
#else
/* Compiling for 32bit, allow years between 1970 and 2037 */
if (gm && (year < 1970 || year > 2037)) {
zend_argument_value_error(1, "must be between 1970 and 2037 (inclusive)");
if (year < min_year || year > max_year) {
zend_argument_value_error(1, "must be between " ZEND_LONG_FMT " and " ZEND_LONG_FMT, min_year, max_year);
RETURN_THROWS();
}
#endif


golden = (year % 19) + 1; /* the Golden number */
Expand Down
2 changes: 1 addition & 1 deletion ext/calendar/tests/easter_date_32bit.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ try {
2000-04-23
2001-04-15
2002-03-31
easter_date(): Argument #1 ($year) must be between 1970 and 2037 (inclusive)
easter_date(): Argument #1 ($year) must be between 1970 and 2037
66 changes: 33 additions & 33 deletions ext/calendar/tests/easter_date_64bit.phpt
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
--TEST--
Test easter_date() on 64bit systems
--SKIPIF--
<?php if (PHP_INT_SIZE != 8) die("skip 64-bit only"); ?>
--INI--
date.timezone=UTC
--ENV--
TZ=UTC
--EXTENSIONS--
calendar
--FILE--
<?php
putenv('TZ=UTC');
echo date("Y-m-d", easter_date(2000))."\n";
echo date("Y-m-d", easter_date(2001))."\n";
echo date("Y-m-d", easter_date(2002))."\n";
echo date("Y-m-d", easter_date(2045))."\n";
echo date("Y-m-d", easter_date(2046))."\n";
echo date("Y-m-d", easter_date(2047))."\n";
try {
easter_date(1492);
} catch (ValueError $ex) {
echo "{$ex->getMessage()}\n";
}
?>
--EXPECT--
2000-04-23
2001-04-15
2002-03-31
2045-04-09
2046-03-25
2047-04-14
easter_date(): Argument #1 ($year) must be a year after 1970 (inclusive)
--TEST--
Test easter_date() on 64bit systems
--SKIPIF--
<?php if (PHP_INT_SIZE != 8) die("skip 64-bit only"); ?>
--INI--
date.timezone=UTC
--ENV--
TZ=UTC
--EXTENSIONS--
calendar
--FILE--
<?php
putenv('TZ=UTC');
echo date("Y-m-d", easter_date(2000))."\n";
echo date("Y-m-d", easter_date(2001))."\n";
echo date("Y-m-d", easter_date(2002))."\n";
echo date("Y-m-d", easter_date(2045))."\n";
echo date("Y-m-d", easter_date(2046))."\n";
echo date("Y-m-d", easter_date(2047))."\n";
try {
easter_date(1492);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
?>
--EXPECTF--
2000-04-23
2001-04-15
2002-03-31
2045-04-09
2046-03-25
2047-04-14
ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ try {
}
?>
--EXPECT--
easter_date(): Argument #1 ($year) must be between 1970 and 2037 (inclusive)
easter_date(): Argument #1 ($year) must be between 1970 and 2037
7 changes: 5 additions & 2 deletions ext/calendar/tests/easter_date_checks_upper_bound_64bit.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
--TEST--
Test easter_date() on 64bit systems checks the upper year limit
--SKIPIF--
<?php if (PHP_INT_SIZE != 8) die("skip 64-bit only"); ?>
<?php
if (PHP_INT_SIZE != 8) die("skip 64-bit only");
if (PHP_OS_FAMILY === "Windows") die("skip not for Windows, see gh23915_windows.phpt");
?>
--INI--
date.timezone=UTC
--ENV--
Expand All @@ -18,4 +21,4 @@ try {
}
?>
--EXPECT--
easter_date(): Argument #1 ($year) must be a year before 2.000.000.000 (inclusive)
easter_date(): Argument #1 ($year) must be between 1970 and 2000000000
2 changes: 1 addition & 1 deletion ext/calendar/tests/gh16228.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ try {
--EXPECTF--
easter_days(): Argument #1 ($year) must be between 1 and %d
easter_days(): Argument #1 ($year) must be between 1 and %d
easter_date(): Argument #1 ($year) must be between 1 and %d
easter_date(): Argument #1 ($year) must be between 1970 and %d
27 changes: 27 additions & 0 deletions ext/calendar/tests/gh23915.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-23915 (easter_date() reports inconsistent year ranges in errors and returns -1 for years after 3000 on Windows)
--INI--
date.timezone=UTC
--ENV--
TZ=UTC
--EXTENSIONS--
calendar
--FILE--
<?php
putenv('TZ=UTC');
foreach ([PHP_INT_MIN, 0, 1, 1969, 1970, PHP_INT_MAX] as $year) {
echo $year, ': ';
try {
echo date('Y-m-d', easter_date($year)), "\n";
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
}
?>
--EXPECTF--
%i: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
0: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
1: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
1969: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
1970: 1970-03-29
%d: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and %d
30 changes: 30 additions & 0 deletions ext/calendar/tests/gh23915_windows.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
GH-23915 (easter_date() reports inconsistent year ranges in errors and returns -1 for years after 3000 on Windows)
--SKIPIF--
<?php
if (PHP_INT_SIZE != 8) die("skip 64-bit only");
if (PHP_OS_FAMILY !== "Windows") die("skip Windows only");
?>
--INI--
date.timezone=UTC
--ENV--
TZ=UTC
--EXTENSIONS--
calendar
--FILE--
<?php
putenv('TZ=UTC');
// mktime() on Windows only goes up to the year 3000
foreach ([3000, 3001, 293274701009] as $year) {
echo $year, ': ';
try {
echo date('Y-m-d', easter_date($year)), "\n";
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
}
?>
--EXPECT--
3000: 3000-04-13
3001: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and 3000
293274701009: ValueError: easter_date(): Argument #1 ($year) must be between 1970 and 3000
Loading