Commit 3c15cf1e4f3 for php.net
commit 3c15cf1e4f3ec9919c0ccbc67afa74e76ccd161f
Author: Derick Rethans <github@derickrethans.nl>
Date: Fri Jul 10 13:37:11 2026 +0100
Add support for -1 as valid year for idate() output. (#22657)
* Add support for -1 as valid year for idate() output.
Closes GH-13273
* Simplify error handling
* Add note in UPGRADING.INTERNALS about API change
diff --git a/NEWS b/NEWS
index 919fda31c99..6d8d5df08ed 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,7 @@ PHP NEWS
an error). (Derick)
. Fixed Unix timestamps in February of the year 0 are misparsed with
@-notation. (LukasGelbmann)
+ . Fixed bug GH-11368 (idate() doesn't work for the year -1). (Derick)
- DBA:
. Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 1c602548b1e..bdf874b382d 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -169,6 +169,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
3. Module changes
========================
+- ext/date:
+ . php_idate() now returns the result state, and moves the return value into an
+ out parameter.
+
- ext/mbstring:
. Added GB18030-2022 to default encoding list for zh-CN.
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 1dbcf51e72d..e14aef1e085 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -901,13 +901,13 @@ PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_
/* }}} */
/* {{{ php_idate */
-PHPAPI int php_idate(char format, time_t ts, bool localtime)
+PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result)
{
timelib_time *t;
timelib_tzinfo *tzi;
- int retval = -1;
timelib_time_offset *offset = NULL;
timelib_sll isoweek, isoyear;
+ bool success = true;
t = timelib_time_ctor();
@@ -947,46 +947,49 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime)
switch (format) {
/* day */
- case 'd': case 'j': retval = (int) t->d; break;
+ case 'd': case 'j': *result = (int) t->d; break;
- case 'N': retval = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break;
- case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break;
- case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break;
+ case 'N': *result = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break;
+ case 'w': *result = (int) timelib_day_of_week(t->y, t->m, t->d); break;
+ case 'z': *result = (int) timelib_day_of_year(t->y, t->m, t->d); break;
/* week */
- case 'W': retval = (int) isoweek; break; /* iso weeknr */
+ case 'W': *result = (int) isoweek; break; /* iso weeknr */
/* month */
- case 'm': case 'n': retval = (int) t->m; break;
- case 't': retval = (int) timelib_days_in_month(t->y, t->m); break;
+ case 'm': case 'n': *result = (int) t->m; break;
+ case 't': *result = (int) timelib_days_in_month(t->y, t->m); break;
/* year */
- case 'L': retval = (int) timelib_is_leap((int) t->y); break;
- case 'y': retval = (int) (t->y % 100); break;
- case 'Y': retval = (int) t->y; break;
- case 'o': retval = (int) isoyear; break; /* iso year */
+ case 'L': *result = (int) timelib_is_leap((int) t->y); break;
+ case 'y': *result = (int) (t->y % 100); break;
+ case 'Y': *result = (int) t->y; break;
+ case 'o': *result = (int) isoyear; break; /* iso year */
/* Swatch Beat a.k.a. Internet Time */
case 'B':
- retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
- if (retval < 0) {
- retval += 864000;
+ *result = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
+ if (*result < 0) {
+ *result += 864000;
}
/* Make sure to do this on a positive int to avoid rounding errors */
- retval = (retval / 864) % 1000;
+ *result = (*result / 864) % 1000;
break;
/* time */
- case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
- case 'H': case 'G': retval = (int) t->h; break;
- case 'i': retval = (int) t->i; break;
- case 's': retval = (int) t->s; break;
+ case 'g': case 'h': *result = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
+ case 'H': case 'G': *result = (int) t->h; break;
+ case 'i': *result = (int) t->i; break;
+ case 's': *result = (int) t->s; break;
/* timezone */
- case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break;
- case 'Z': retval = (int) (!localtime ? offset->offset : 0); break;
+ case 'I': *result = (int) (!localtime ? offset->is_dst : 0); break;
+ case 'Z': *result = (int) (!localtime ? offset->offset : 0); break;
- case 'U': retval = (int) t->sse; break;
+ case 'U': *result = (int) t->sse; break;
+
+ default:
+ success = false;
}
if (!localtime) {
@@ -994,7 +997,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime)
}
timelib_time_dtor(t);
- return retval;
+ return success;
}
/* }}} */
@@ -1018,7 +1021,7 @@ PHP_FUNCTION(idate)
zend_string *format;
zend_long ts;
bool ts_is_null = 1;
- int ret;
+ int ret = 0;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(format)
@@ -1035,8 +1038,8 @@ PHP_FUNCTION(idate)
ts = php_time();
}
- ret = php_idate(ZSTR_VAL(format)[0], ts, 0);
- if (ret == -1) {
+ bool ok = php_idate(ZSTR_VAL(format)[0], ts, 0, &ret);
+ if (!ok) {
php_error_docref(NULL, E_WARNING, "Unrecognized date format token");
RETURN_FALSE;
}
diff --git a/ext/date/php_date.h b/ext/date/php_date.h
index f26617c15ea..651cc28225f 100644
--- a/ext/date/php_date.h
+++ b/ext/date/php_date.h
@@ -124,7 +124,7 @@ PHPAPI time_t php_time(void);
/* Backwards compatibility wrapper */
PHPAPI zend_long php_parse_date(const char *string, zend_long *now);
PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, bool gmt);
-PHPAPI int php_idate(char format, time_t ts, bool localtime);
+PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result);
#define _php_strftime php_strftime