[php-src] master: Add support for -1 as valid year for idate() output. (#22657)

Derick Rethans via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Derick Rethans (derickr)
Committer: GitHub (web-flow)
Pusher: derickr
Date: 2026-07-10T13:37:11+01:00

Commit: https://github.com/php/php-src/commit/3c15cf1e4f3ec9919c0ccbc67afa74e76ccd161f
Raw diff: https://github.com/php/php-src/commit/3c15cf1e4f3ec9919c0ccbc67afa74e76ccd161f.diff

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

Changed paths:
  M  NEWS
  M  UPGRADING.INTERNALS
  M  ext/date/php_date.c
  M  ext/date/php_date.h


Diff:

diff --git a/NEWS b/NEWS
index 919fda31c992..6d8d5df08ed8 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 1c602548b1ed..bdf874b382d0 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 1dbcf51e72df..e14aef1e0855 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 f26617c15ea9..651cc28225fd 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.