Commit d83851dae86 for php.net

commit d83851dae86dbfbf4370ec42750b606e79de7ac3
Author: Derick Rethans <github@derickrethans.nl>
Date:   Thu Jul 9 15:29:05 2026 +0100

    Update timelib to 2022.17, which has fixes for PHP bugs

diff --git a/NEWS b/NEWS
index cdd6a48a487..d64ce32ab99 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,13 @@ PHP                                                                        NEWS
   . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with
     INT_MAX year). (arshidkv12)

+- Date:
+  . Update timelib to 2022.17. (Derick)
+  . Fixed bug GH-19803 (Parsing a string with a single white space does create
+    an error). (Derick)
+  . Fixed Unix timestamps in February of the year 0 are misparsed with
+    @-notation. (LukasGelbmann)
+
 - DBA:
   . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)

diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re
index f4a39275324..07d9c0e7348 100644
--- a/ext/date/lib/parse_date.re
+++ b/ext/date/lib/parse_date.re
@@ -555,7 +555,6 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l
 	int len = 0;

 	/* Skip over non-numeric chars */
-
 	while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
 		if (**ptr == '\0') {
 			add_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Found unexpected data");
@@ -569,6 +568,7 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l
 	str[0] = '+'; /* First position is the sign */
 	str_ptr = str + 1;

+
 	while ((**ptr == '+') || (**ptr == '-')) {
 		if (**ptr == '-') {
 			str[0] = str[0] == '+' ? '-' : '+';
@@ -2020,7 +2020,7 @@ timelib_time *timelib_strtotime(const char *s, size_t len, timelib_error_contain
 	in.errors->error_messages = NULL;

 	if (len > 0) {
-		while (isspace((unsigned char)*s) && s < e) {
+		while (isspace((unsigned char)*s) && s <= e) {
 			s++;
 		}
 		while (isspace((unsigned char)*e) && e > s) {
diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h
index acae8e32533..d08aa77ea5e 100644
--- a/ext/date/lib/timelib.h
+++ b/ext/date/lib/timelib.h
@@ -30,9 +30,9 @@
 # include "timelib_config.h"
 #endif

-#define TIMELIB_VERSION 202215
-#define TIMELIB_EXTENDED_VERSION 20221501
-#define TIMELIB_ASCII_VERSION "2022.15"
+#define TIMELIB_VERSION 202217
+#define TIMELIB_EXTENDED_VERSION 20221701
+#define TIMELIB_ASCII_VERSION "2022.17"

 #include <stdlib.h>
 #include <stdbool.h>
diff --git a/ext/date/lib/timelib_private.h b/ext/date/lib/timelib_private.h
index 3c5f9b22147..42cbaa68dbb 100644
--- a/ext/date/lib/timelib_private.h
+++ b/ext/date/lib/timelib_private.h
@@ -135,6 +135,7 @@
 #ifndef TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW
 # define TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 0
 #endif
+
 struct _ttinfo
 {
 	int32_t      offset;
@@ -165,6 +166,7 @@ extern "C" {
 #endif

 /* From unixtime2tm.c */
+void timelib_date_from_epoch_days(timelib_sll epoch_days, timelib_sll *y, timelib_sll *m, timelib_sll *d);
 int timelib_apply_localtime(timelib_time *t, unsigned int localtime);

 /* From parse_posix.c */
diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c
index f13a9412406..4c0252e244d 100644
--- a/ext/date/lib/tm2unixtime.c
+++ b/ext/date/lib/tm2unixtime.c
@@ -197,32 +197,6 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
 	do_range_limit(0, 12, 12, &rt->m, &rt->y);
 }

-static void magic_date_calc(timelib_time *time)
-{
-	timelib_sll y, ddd, mi, mm, dd, g;
-
-	/* The algorithm doesn't work before the year 1 */
-	if (time->d < -719498) {
-		return;
-	}
-
-	g = time->d + HINNANT_EPOCH_SHIFT - 1;
-
-	y = (10000 * g + 14780) / 3652425;
-	ddd = g - ((365*y) + (y/4) - (y/100) + (y/400));
-	if (ddd < 0) {
-		y--;
-		ddd = g - ((365*y) + (y/4) - (y/100) + (y/400));
-	}
-	mi = (100 * ddd + 52) / 3060;
-	mm = ((mi + 2) % 12) + 1;
-	y = y + (mi + 2) / 12;
-	dd = ddd - ((mi * 306 + 5) / 10) + 1;
-	time->y = y;
-	time->m = mm;
-	time->d = dd;
-}
-
 void timelib_do_normalize(timelib_time* time)
 {
 	if (time->us != TIMELIB_UNSET) do_range_limit(0, 1000000, 1000000, &time->us, &time->s);
@@ -232,8 +206,9 @@ void timelib_do_normalize(timelib_time* time)
 	do_range_limit(1, 13, 12, &time->m, &time->y);

 	/* Short cut if we're doing things against the Epoch */
-	if (time->y == 1970 && time->m == 1 && time->d != 1) {
-		magic_date_calc(time);
+	if (time->y == 1970 && time->m == 1) {
+		timelib_date_from_epoch_days(time->d - 1, &time->y, &time->m, &time->d);
+		return;
 	}

 	do {} while (do_range_limit_days(&time->y, &time->m, &time->d));
diff --git a/ext/date/lib/unixtime2tm.c b/ext/date/lib/unixtime2tm.c
index 30b3d00c7f4..9c4ba901a93 100644
--- a/ext/date/lib/unixtime2tm.c
+++ b/ext/date/lib/unixtime2tm.c
@@ -26,20 +26,16 @@
 #include "timelib.h"
 #include "timelib_private.h"

-void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d)
+/* Converts Unix epoch days (days since 1970-01-01) to a date. Algorithm from:
+ * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
+void timelib_date_from_epoch_days(timelib_sll epoch_days, timelib_sll *y, timelib_sll *m, timelib_sll *d)
 {
-	timelib_sll days, era, t;
+	timelib_sll days, era;
 	timelib_ull day_of_era, year_of_era, day_of_year, month_portion;

 	/* Calculate days since algorithm's epoch (0000-03-01) */
-	days = ts / SECS_PER_DAY + HINNANT_EPOCH_SHIFT;
-
-	/* Adjustment for a negative time portion */
-	t = ts % SECS_PER_DAY;
-	days += (t < 0) ? -1 : 0;
+	days = epoch_days + HINNANT_EPOCH_SHIFT;

-	/* Calculate year, month, and day. Algorithm from:
-	 * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
 	era = (days >= 0 ? days : days - DAYS_PER_ERA + 1) / DAYS_PER_ERA;
 	day_of_era = days - era * DAYS_PER_ERA;
 	year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / DAYS_PER_YEAR;
@@ -49,6 +45,19 @@ void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timel
 	*d = day_of_year - (153 * month_portion + 2) / 5 + 1;
 	*m = month_portion + (month_portion < 10 ? 3 : -9);
 	*y += (*m <= 2);
+}
+
+void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d)
+{
+	timelib_sll epoch_days, t;
+
+	epoch_days = ts / SECS_PER_DAY;
+
+	/* Adjustment for a negative time portion */
+	t = ts % SECS_PER_DAY;
+	epoch_days += (t < 0) ? -1 : 0;
+
+	timelib_date_from_epoch_days(epoch_days, y, m, d);

 	TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, *y, *m, *d););
 }