Commit 69b6debd808 for php.net

commit 69b6debd8089a3b66cb33d32e6d647befc08c01c
Author: Derick Rethans <github@derickrethans.nl>
Date:   Wed Aug 5 11:37:02 2026 +0100

    Update to timelib 2026.01

diff --git a/NEWS b/NEWS
index 16a4ae4e99b..7dd05a2b1c7 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ PHP                                                                        NEWS
   . Changed run-tests.php to run test subprocesses without a shell where
     possible. (NickSdot)

+- Date:
+  . Update timelib to 2026.01. (Derick, timwolla)
+
 - GMP:
   . Added optional $definitely_prime output parameter to gmp_prevprime().
     (Weilin Du)
diff --git a/ext/date/lib/duration.c b/ext/date/lib/duration.c
new file mode 100644
index 00000000000..b5b0c766850
--- /dev/null
+++ b/ext/date/lib/duration.c
@@ -0,0 +1,429 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2026 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+int timelib_duration_ctor_static(
+	timelib_duration *duration,
+	timelib_ull       seconds,
+	uint32_t          nanoseconds,
+	bool              negative
+) {
+	if (seconds > MAX_DURATION_SECONDS) {
+		return TIMELIB_ERROR_SECONDS_OUT_OF_RANGE;
+	}
+	if (nanoseconds >= NSECS_PER_SEC) {
+		return TIMELIB_ERROR_NANOSECONDS_OUT_OF_RANGE;
+	}
+
+	duration->seconds = seconds;
+	duration->nanoseconds = nanoseconds;
+
+	if (seconds != 0 || nanoseconds != 0) {
+		duration->negative = negative;
+	} else {
+		duration->negative = false;
+	}
+
+	return TIMELIB_ERROR_NO_ERROR;
+}
+
+timelib_duration *timelib_duration_ctor(
+	timelib_ull            seconds,
+	uint32_t               nanoseconds,
+	bool                   negative,
+	int                   *error_code
+) {
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_ctor_static(tmp, seconds, nanoseconds, negative);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+timelib_duration *timelib_duration_create_from_iso8601string(
+	const char *string,
+	int        *error_code
+) {
+	timelib_time            *b = NULL, *e = NULL;
+	timelib_rel_time        *p = NULL;
+	int                      r = -1;
+	timelib_error_container *errors;
+	timelib_duration        *new_duration = NULL;
+
+	timelib_strtointerval(string, strlen(string), &b, &e, &p, &r, &errors);
+
+	if (errors->error_count > 0 || errors->warning_count > 0) {
+		*error_code = TIMELIB_ERROR_ISO8601_DURATION_PARSE_FAILURE;
+		goto free_elements;
+	}
+
+	if (!p) {
+		*error_code = TIMELIB_ERROR_DURATION_MISSING_PERIOD;
+		goto free_elements;
+	}
+
+	if (b != NULL || e != NULL || r != -1) {
+		*error_code = TIMELIB_ERROR_DURATION_ONLY_PERIOD_ALLOWED;
+		goto free_elements;
+	}
+
+	if (p->y > 0 || p->m >0 || p->d > 0) {
+		*error_code = TIMELIB_ERROR_DURATION_DAYS_FOUND;
+		goto free_elements;
+	}
+
+	new_duration = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_ctor_static(new_duration, p->h * 3600 + p->i * 60 + p->s, 0, false);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(new_duration);
+		new_duration = NULL;
+	}
+
+free_elements:
+	if (b) {
+		timelib_time_dtor(b);
+	}
+	if (e) {
+		timelib_time_dtor(e);
+	}
+	if (p) {
+		timelib_rel_time_dtor(p);
+	}
+	timelib_error_container_dtor(errors);
+
+	return new_duration;
+}
+
+void timelib_duration_dtor(timelib_duration *duration)
+{
+	free(duration);
+}
+
+static int timelib_duration_add_abs_internal(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *additional
+) {
+	timelib_ull seconds = original->seconds + additional->seconds;
+	uint32_t nanoseconds = original->nanoseconds + additional->nanoseconds;
+
+	if (nanoseconds >= NSECS_PER_SEC) {
+		seconds++;
+		nanoseconds -= NSECS_PER_SEC;
+	}
+
+	return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
+}
+
+
+static int timelib_duration_sub_abs_internal(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *minus
+) {
+	timelib_ull seconds = original->seconds - minus->seconds;
+	timelib_sll nanoseconds = (timelib_sll)original->nanoseconds - (timelib_sll)minus->nanoseconds;
+
+	if (nanoseconds < 0) {
+		seconds--;
+		nanoseconds += NSECS_PER_SEC;
+	}
+
+	return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
+}
+
+static int timelib_duration_null_abs_internal(timelib_duration *new_duration)
+{
+	new_duration->negative = false;
+	new_duration->seconds = 0;
+	new_duration->nanoseconds = 0;
+
+	return TIMELIB_ERROR_NO_ERROR;
+}
+
+
+int timelib_duration_add_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *additional
+) {
+	int c = 0;
+
+	/* ++ / -- */
+	if (original->negative == additional->negative) {
+		return timelib_duration_add_abs_internal(new_duration, original, additional);
+	}
+
+	/* +- / -+ */
+	c = timelib_duration_abs_compare(original, additional);
+
+	switch (c)
+	{
+		case -1:
+			/* additional has the larger value */
+			return timelib_duration_sub_abs_internal(new_duration, additional, original);
+
+		case 0:
+			return timelib_duration_null_abs_internal(new_duration);
+
+		case 1:
+			/* original has the larger value */
+			return timelib_duration_sub_abs_internal(new_duration, original, additional);
+	}
+
+	/* Should not be reachable due to semantics of timelib_duration_abs_compare() */
+	return TIMELIB_ERROR_NO_ERROR;
+}
+
+timelib_duration *timelib_duration_add(
+	const timelib_duration *original,
+	const timelib_duration *additional,
+	int                    *error_code
+) {
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_add_static(tmp, original, additional);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+
+int timelib_duration_sub_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *minus
+) {
+	int c = 0;
+
+	/* +- / -+ */
+	if (original->negative != minus->negative) {
+		return timelib_duration_add_abs_internal(new_duration, original, minus);
+	}
+
+	/* ++ / -- */
+	c = timelib_duration_abs_compare(original, minus);
+
+	switch (c)
+	{
+		case -1: {
+			/* minus has the larger value */
+			timelib_duration tmp_duration;
+
+			int result = timelib_duration_sub_abs_internal(&tmp_duration, minus, original);
+			if (result != TIMELIB_ERROR_NO_ERROR) {
+				return result;
+			}
+
+			return timelib_duration_negate_static(new_duration, &tmp_duration);
+		}
+
+		case 0:
+			return timelib_duration_null_abs_internal(new_duration);
+
+		case 1:
+			/* original has the larger value */
+			return timelib_duration_sub_abs_internal(new_duration, original, minus);
+	}
+
+	/* Should not be reachable due to semantics of comparisons above */
+	return TIMELIB_ERROR_NO_ERROR;
+}
+
+timelib_duration *timelib_duration_sub(
+	const timelib_duration *original,
+	const timelib_duration *minus,
+	int                    *error_code
+) {
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_sub_static(tmp, original, minus);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+int timelib_duration_mul_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	uint64_t                factor
+) {
+	uint64_t seconds, extra_seconds, nanoseconds;
+
+	if (factor == 0) {
+		return timelib_duration_null_abs_internal(new_duration);
+	}
+
+	if (original->seconds > UINT64_MAX / factor) {
+		return TIMELIB_ERROR_OVERFLOW;
+	}
+
+	seconds = original->seconds * factor;
+
+	/* Calculate the number of whole seconds in the nanoseconds product.
+	 *
+	 * extra_seconds is guaranteed to be smaller than factor, because
+	 * original->nanoseconds is smaller than NSECS_PER_SEC. */
+	extra_seconds = original->nanoseconds * (factor / NSECS_PER_SEC);
+
+	/* This cannot overflow either, because NSECS_PER_SEC * NSECS_PER_SEC fits uint64_t.
+	 *
+	 * (nanoseconds * factor) % NSECS_PER_SEC is mathematically equivalent
+	 * to (nanoseconds * (factor % NSECS_PER_SEC)) % NSECS_PER_SEC. */
+	nanoseconds = original->nanoseconds * (factor % NSECS_PER_SEC);
+	extra_seconds += nanoseconds / NSECS_PER_SEC;
+	nanoseconds %= NSECS_PER_SEC;
+
+	if (UINT64_MAX - extra_seconds < seconds) {
+		return TIMELIB_ERROR_OVERFLOW;
+	}
+
+	seconds += extra_seconds;
+
+	return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
+}
+
+timelib_duration *timelib_duration_mul(
+	const timelib_duration *original,
+	uint64_t                factor,
+	int                    *error_code
+) {
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_mul_static(tmp, original, factor);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+int timelib_duration_div_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	uint64_t                divisor
+) {
+	timelib_ull seconds;
+	uint32_t nanoseconds;
+
+	if (divisor < 1) {
+		return TIMELIB_ERROR_DIVISION_BY_ZERO;
+	}
+
+	seconds = original->seconds / divisor;
+	nanoseconds = original->nanoseconds + ((original->seconds % divisor) * NSECS_PER_SEC);
+	nanoseconds /= divisor;
+
+	return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
+}
+
+timelib_duration *timelib_duration_div(
+	const timelib_duration *original,
+	uint64_t                divisor,
+	int                    *error_code
+) {
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_div_static(tmp, original, divisor);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+int timelib_duration_negate_static(timelib_duration *new_duration, const timelib_duration *original)
+{
+	return timelib_duration_ctor_static(new_duration, original->seconds, original->nanoseconds, !original->negative);
+}
+
+timelib_duration *timelib_duration_negate(const timelib_duration *original, int *error_code)
+{
+	timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
+
+	*error_code = timelib_duration_negate_static(tmp, original);
+
+	if (*error_code != TIMELIB_ERROR_NO_ERROR) {
+		free(tmp);
+		return NULL;
+	}
+
+	return tmp;
+}
+
+int timelib_duration_abs_compare(const timelib_duration *one, const timelib_duration *two)
+{
+	if (one->seconds < two->seconds) {
+		return -1;
+	}
+	if (one->seconds > two->seconds) {
+		return 1;
+	}
+
+	if (one->nanoseconds < two->nanoseconds) {
+		return -1;
+	}
+	if (one->nanoseconds > two->nanoseconds) {
+		return 1;
+	}
+
+	return 0;
+}
+
+int timelib_duration_compare(const timelib_duration *one, const timelib_duration *two)
+{
+	if (one->negative && !two->negative) {
+		return -1;
+	}
+	if (!one->negative && two->negative) {
+		return 1;
+	}
+
+	if (one->negative && two->negative) {
+		return timelib_duration_abs_compare(two, one);
+	}
+
+	return timelib_duration_abs_compare(one, two);
+}
diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c
index da9262da999..fb10963d757 100644
--- a/ext/date/lib/interval.c
+++ b/ext/date/lib/interval.c
@@ -201,7 +201,10 @@ int timelib_diff_days(timelib_time *one, timelib_time *two)
 			days--;
 		}
 	} else {
-		days = fabs(floor(one->sse - two->sse) / 86400);
+		/* FIXME: This truncates the range of days to INT_MAX to avoid an
+		 * overflow later on. Ideally, all APIs have better error handling. */
+		double ddays = fabs(floor(one->sse - two->sse) / 86400);
+		days = ddays <= INT_MAX ? ddays : INT_MAX;
 	}

 	return days;
diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c
index eb324f8430c..3380ba73265 100644
--- a/ext/date/lib/parse_date.c
+++ b/ext/date/lib/parse_date.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 1.0.3 on Wed Jan 14 14:21:53 2026 */
+/* Generated by re2c 1.0.3 on Wed Aug  5 11:34:24 2026 */
 #line 1 "ext/date/lib/parse_date.re"
 /*
  * The MIT License (MIT)
@@ -34,16 +34,6 @@
 #include <assert.h>
 #include <limits.h>

-#if defined(_MSC_VER)
-# define strtoll(s, f, b) _atoi64(s)
-#elif !defined(HAVE_STRTOLL)
-# if defined(HAVE_ATOLL)
-#  define strtoll(s, f, b) atoll(s)
-# else
-#  define strtoll(s, f, b) strtol(s, f, b)
-# endif
-#endif
-
 #define EOI      257
 #define TIME     258
 #define DATE     259
@@ -557,7 +547,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");
@@ -571,6 +560,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] == '+' ? '-' : '+';
@@ -1028,11 +1018,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 std:
 	s->tok = cursor;
 	s->len = 0;
-#line 1161 "ext/date/lib/parse_date.re"
+#line 1151 "ext/date/lib/parse_date.re"



-#line 1036 "ext/date/lib/parse_date.c"
+#line 1026 "ext/date/lib/parse_date.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -1213,23 +1203,23 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(2, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(3, *YYCURSOR);
-#line 1994 "ext/date/lib/parse_date.re"
+#line 1984 "ext/date/lib/parse_date.re"
 	{
 		s->pos = cursor; s->line++;
 		goto std;
 	}
-#line 1222 "ext/date/lib/parse_date.c"
+#line 1212 "ext/date/lib/parse_date.c"
 yy4:
 	YYDEBUG(4, *YYCURSOR);
 	++YYCURSOR;
 yy5:
 	YYDEBUG(5, *YYCURSOR);
-#line 2000 "ext/date/lib/parse_date.re"
+#line 1990 "ext/date/lib/parse_date.re"
 	{
 		add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character");
 		goto std;
 	}
-#line 1233 "ext/date/lib/parse_date.c"
+#line 1223 "ext/date/lib/parse_date.c"
 yy6:
 	YYDEBUG(6, *YYCURSOR);
 	yyaccept = 0;
@@ -1244,11 +1234,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy58;
 yy8:
 	YYDEBUG(8, *YYCURSOR);
-#line 1989 "ext/date/lib/parse_date.re"
+#line 1979 "ext/date/lib/parse_date.re"
 	{
 		goto std;
 	}
-#line 1252 "ext/date/lib/parse_date.c"
+#line 1242 "ext/date/lib/parse_date.c"
 yy9:
 	YYDEBUG(9, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -1282,11 +1272,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(11, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(12, *YYCURSOR);
-#line 1984 "ext/date/lib/parse_date.re"
+#line 1974 "ext/date/lib/parse_date.re"
 	{
 		goto std;
 	}
-#line 1290 "ext/date/lib/parse_date.c"
+#line 1280 "ext/date/lib/parse_date.c"
 yy13:
 	YYDEBUG(13, *YYCURSOR);
 	yyaccept = 1;
@@ -1787,7 +1777,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy20:
 	YYDEBUG(20, *YYCURSOR);
-#line 1899 "ext/date/lib/parse_date.re"
+#line 1889 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("tzcorrection | tz");
@@ -1801,7 +1791,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_TIMEZONE;
 	}
-#line 1805 "ext/date/lib/parse_date.c"
+#line 1795 "ext/date/lib/parse_date.c"
 yy21:
 	YYDEBUG(21, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -3606,7 +3596,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy81:
 	YYDEBUG(81, *YYCURSOR);
-#line 1646 "ext/date/lib/parse_date.re"
+#line 1636 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("datenoyearrev");
 		TIMELIB_INIT;
@@ -3617,7 +3607,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_TEXT;
 	}
-#line 3621 "ext/date/lib/parse_date.c"
+#line 3611 "ext/date/lib/parse_date.c"
 yy82:
 	YYDEBUG(82, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -4132,7 +4122,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 	if (yych == '.') goto yy289;
 	YYDEBUG(114, *YYCURSOR);
-#line 1221 "ext/date/lib/parse_date.re"
+#line 1211 "ext/date/lib/parse_date.re"
 	{
 		timelib_ull i;

@@ -4157,7 +4147,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 4161 "ext/date/lib/parse_date.c"
+#line 4151 "ext/date/lib/parse_date.c"
 yy115:
 	YYDEBUG(115, *YYCURSOR);
 	++YYCURSOR;
@@ -5883,7 +5873,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy177:
 	YYDEBUG(177, *YYCURSOR);
-#line 1387 "ext/date/lib/parse_date.re"
+#line 1377 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("timetiny24 | timeshort24 | timelong24 | iso8601long");
@@ -5910,7 +5900,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_TIME24_WITH_ZONE;
 	}
-#line 5914 "ext/date/lib/parse_date.c"
+#line 5904 "ext/date/lib/parse_date.c"
 yy178:
 	YYDEBUG(178, *YYCURSOR);
 	yyaccept = 4;
@@ -6939,7 +6929,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy224:
 	YYDEBUG(224, *YYCURSOR);
-#line 1481 "ext/date/lib/parse_date.re"
+#line 1471 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("americanshort | american");
@@ -6954,7 +6944,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_AMERICAN;
 	}
-#line 6958 "ext/date/lib/parse_date.c"
+#line 6948 "ext/date/lib/parse_date.c"
 yy225:
 	YYDEBUG(225, *YYCURSOR);
 	yyaccept = 5;
@@ -7197,7 +7187,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy431;
 yy251:
 	YYDEBUG(251, *YYCURSOR);
-#line 1563 "ext/date/lib/parse_date.re"
+#line 1553 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("datefull");
@@ -7211,7 +7201,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_FULL;
 	}
-#line 7215 "ext/date/lib/parse_date.c"
+#line 7205 "ext/date/lib/parse_date.c"
 yy252:
 	YYDEBUG(252, *YYCURSOR);
 	yyaccept = 3;
@@ -7325,7 +7315,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych == 'e') goto yy440;
 yy260:
 	YYDEBUG(260, *YYCURSOR);
-#line 1968 "ext/date/lib/parse_date.re"
+#line 1958 "ext/date/lib/parse_date.re"
 	{
 		timelib_ull i;
 		DEBUG_OUTPUT("relative");
@@ -7340,7 +7330,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 7344 "ext/date/lib/parse_date.c"
+#line 7334 "ext/date/lib/parse_date.c"
 yy261:
 	YYDEBUG(261, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -7786,7 +7776,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy471;
 yy290:
 	YYDEBUG(290, *YYCURSOR);
-#line 1247 "ext/date/lib/parse_date.re"
+#line 1237 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll i;
 		timelib_ull us;
@@ -7825,7 +7815,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 7829 "ext/date/lib/parse_date.c"
+#line 7819 "ext/date/lib/parse_date.c"
 yy291:
 	YYDEBUG(291, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -7850,7 +7840,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy293:
 	YYDEBUG(293, *YYCURSOR);
-#line 1809 "ext/date/lib/parse_date.re"
+#line 1799 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("ago");
 		TIMELIB_INIT;
@@ -7870,7 +7860,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_AGO;
 	}
-#line 7874 "ext/date/lib/parse_date.c"
+#line 7864 "ext/date/lib/parse_date.c"
 yy294:
 	YYDEBUG(294, *YYCURSOR);
 	yyaccept = 7;
@@ -7909,7 +7899,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy295:
 	YYDEBUG(295, *YYCURSOR);
-#line 1889 "ext/date/lib/parse_date.re"
+#line 1879 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("monthtext");
 		TIMELIB_INIT;
@@ -7918,7 +7908,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_TEXT;
 	}
-#line 7922 "ext/date/lib/parse_date.c"
+#line 7912 "ext/date/lib/parse_date.c"
 yy296:
 	YYDEBUG(296, *YYCURSOR);
 	yyaccept = 7;
@@ -8493,7 +8483,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy315:
 	YYDEBUG(315, *YYCURSOR);
-#line 1830 "ext/date/lib/parse_date.re"
+#line 1820 "ext/date/lib/parse_date.re"
 	{
 		const timelib_relunit* relunit;
 		DEBUG_OUTPUT("daytext");
@@ -8510,7 +8500,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_WEEKDAY;
 	}
-#line 8514 "ext/date/lib/parse_date.c"
+#line 8504 "ext/date/lib/parse_date.c"
 yy316:
 	YYDEBUG(316, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -8778,7 +8768,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy325:
 	YYDEBUG(325, *YYCURSOR);
-#line 1632 "ext/date/lib/parse_date.re"
+#line 1622 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("datetextual | datenoyear");
@@ -8791,7 +8781,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_TEXT;
 	}
-#line 8795 "ext/date/lib/parse_date.c"
+#line 8785 "ext/date/lib/parse_date.c"
 yy326:
 	YYDEBUG(326, *YYCURSOR);
 	yyaccept = 10;
@@ -9485,7 +9475,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy351:
 	YYDEBUG(351, *YYCURSOR);
-#line 1178 "ext/date/lib/parse_date.re"
+#line 1168 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("now");
 		TIMELIB_INIT;
@@ -9493,7 +9483,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 9497 "ext/date/lib/parse_date.c"
+#line 9487 "ext/date/lib/parse_date.c"
 yy352:
 	YYDEBUG(352, *YYCURSOR);
 	yyaccept = 2;
@@ -10996,7 +10986,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy420:
 	YYDEBUG(420, *YYCURSOR);
-#line 1415 "ext/date/lib/parse_date.re"
+#line 1405 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("gnunocolon");
 		TIMELIB_INIT;
@@ -11018,7 +11008,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_GNU_NOCOLON;
 	}
-#line 11022 "ext/date/lib/parse_date.c"
+#line 11012 "ext/date/lib/parse_date.c"
 yy421:
 	YYDEBUG(421, *YYCURSOR);
 	yyaccept = 13;
@@ -11099,7 +11089,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy422:
 	YYDEBUG(422, *YYCURSOR);
-#line 1800 "ext/date/lib/parse_date.re"
+#line 1790 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("year4");
 		TIMELIB_INIT;
@@ -11107,7 +11097,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_CLF;
 	}
-#line 11111 "ext/date/lib/parse_date.c"
+#line 11101 "ext/date/lib/parse_date.c"
 yy423:
 	YYDEBUG(423, *YYCURSOR);
 	yyaccept = 3;
@@ -11714,7 +11704,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(456, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(457, *YYCURSOR);
-#line 1349 "ext/date/lib/parse_date.re"
+#line 1339 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
 		TIMELIB_INIT;
@@ -11731,7 +11721,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_TIME12;
 	}
-#line 11735 "ext/date/lib/parse_date.c"
+#line 11725 "ext/date/lib/parse_date.c"
 yy458:
 	YYDEBUG(458, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -13058,7 +13048,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy526:
 	YYDEBUG(526, *YYCURSOR);
-#line 1187 "ext/date/lib/parse_date.re"
+#line 1177 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("noon");
 		TIMELIB_INIT;
@@ -13069,7 +13059,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 13073 "ext/date/lib/parse_date.c"
+#line 13063 "ext/date/lib/parse_date.c"
 yy527:
 	YYDEBUG(527, *YYCURSOR);
 	yyaccept = 2;
@@ -14115,7 +14105,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy567:
 	YYDEBUG(567, *YYCURSOR);
-#line 1549 "ext/date/lib/parse_date.re"
+#line 1539 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("gnudateshort");
@@ -14128,7 +14118,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 14132 "ext/date/lib/parse_date.c"
+#line 14122 "ext/date/lib/parse_date.c"
 yy568:
 	YYDEBUG(568, *YYCURSOR);
 	yyaccept = 15;
@@ -14579,7 +14569,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy600:
 	YYDEBUG(600, *YYCURSOR);
-#line 1618 "ext/date/lib/parse_date.re"
+#line 1608 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("datenodayrev");
@@ -14592,7 +14582,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_NO_DAY;
 	}
-#line 14596 "ext/date/lib/parse_date.c"
+#line 14586 "ext/date/lib/parse_date.c"
 yy601:
 	YYDEBUG(601, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -15967,7 +15957,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(696, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(697, *YYCURSOR);
-#line 1604 "ext/date/lib/parse_date.re"
+#line 1594 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("datenoday");
@@ -15980,7 +15970,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_NO_DAY;
 	}
-#line 15984 "ext/date/lib/parse_date.c"
+#line 15974 "ext/date/lib/parse_date.c"
 yy698:
 	YYDEBUG(698, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -16541,7 +16531,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy722:
 	YYDEBUG(722, *YYCURSOR);
-#line 1199 "ext/date/lib/parse_date.re"
+#line 1189 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("midnight | today");
 		TIMELIB_INIT;
@@ -16550,7 +16540,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 16554 "ext/date/lib/parse_date.c"
+#line 16544 "ext/date/lib/parse_date.c"
 yy723:
 	YYDEBUG(723, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -16860,7 +16850,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy897;
 yy739:
 	YYDEBUG(739, *YYCURSOR);
-#line 1590 "ext/date/lib/parse_date.re"
+#line 1580 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("pointed date YY");
@@ -16873,7 +16863,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_FULL_POINTED;
 	}
-#line 16877 "ext/date/lib/parse_date.c"
+#line 16867 "ext/date/lib/parse_date.c"
 yy740:
 	YYDEBUG(740, *YYCURSOR);
 	yyaccept = 15;
@@ -16985,7 +16975,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy752:
 	YYDEBUG(752, *YYCURSOR);
-#line 1535 "ext/date/lib/parse_date.re"
+#line 1525 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("gnudateshorter");
@@ -16998,7 +16988,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 17002 "ext/date/lib/parse_date.c"
+#line 16992 "ext/date/lib/parse_date.c"
 yy753:
 	YYDEBUG(753, *YYCURSOR);
 	yyaccept = 18;
@@ -17247,7 +17237,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy777:
 	YYDEBUG(777, *YYCURSOR);
-#line 1461 "ext/date/lib/parse_date.re"
+#line 1451 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("iso8601nocolon");
@@ -17266,7 +17256,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_NOCOLON;
 	}
-#line 17270 "ext/date/lib/parse_date.c"
+#line 17260 "ext/date/lib/parse_date.c"
 yy778:
 	YYDEBUG(778, *YYCURSOR);
 	yyaccept = 19;
@@ -18494,7 +18484,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy849:
 	YYDEBUG(849, *YYCURSOR);
-#line 1938 "ext/date/lib/parse_date.re"
+#line 1928 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
@@ -18523,7 +18513,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_SHORTDATE_WITH_TIME;
 	}
-#line 18527 "ext/date/lib/parse_date.c"
+#line 18517 "ext/date/lib/parse_date.c"
 yy850:
 	YYDEBUG(850, *YYCURSOR);
 	yyaccept = 20;
@@ -19567,7 +19557,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy926:
 	YYDEBUG(926, *YYCURSOR);
-#line 1696 "ext/date/lib/parse_date.re"
+#line 1686 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("pgydotd");
@@ -19580,7 +19570,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_PG_YEARDAY;
 	}
-#line 19584 "ext/date/lib/parse_date.c"
+#line 19574 "ext/date/lib/parse_date.c"
 yy927:
 	YYDEBUG(927, *YYCURSOR);
 	yyaccept = 21;
@@ -19834,7 +19824,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '7') goto yy1059;
 yy942:
 	YYDEBUG(942, *YYCURSOR);
-#line 1729 "ext/date/lib/parse_date.re"
+#line 1719 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll w, d;
 		DEBUG_OUTPUT("isoweek");
@@ -19852,7 +19842,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_WEEK;
 	}
-#line 19856 "ext/date/lib/parse_date.c"
+#line 19846 "ext/date/lib/parse_date.c"
 yy943:
 	YYDEBUG(943, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -20328,7 +20318,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych == 'e') goto yy1094;
 yy982:
 	YYDEBUG(982, *YYCURSOR);
-#line 1872 "ext/date/lib/parse_date.re"
+#line 1862 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll i;
 		int         behavior = 0;
@@ -20344,7 +20334,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 20348 "ext/date/lib/parse_date.c"
+#line 20338 "ext/date/lib/parse_date.c"
 yy983:
 	YYDEBUG(983, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -20691,7 +20681,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1020, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1021, *YYCURSOR);
-#line 1578 "ext/date/lib/parse_date.re"
+#line 1568 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("pointed date YYYY");
 		TIMELIB_INIT;
@@ -20702,7 +20692,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_FULL_POINTED;
 	}
-#line 20706 "ext/date/lib/parse_date.c"
+#line 20696 "ext/date/lib/parse_date.c"
 yy1022:
 	YYDEBUG(1022, *YYCURSOR);
 	++YYCURSOR;
@@ -20731,7 +20721,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy1025:
 	YYDEBUG(1025, *YYCURSOR);
-#line 1509 "ext/date/lib/parse_date.re"
+#line 1499 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("iso8601date2");
@@ -20744,7 +20734,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 20748 "ext/date/lib/parse_date.c"
+#line 20738 "ext/date/lib/parse_date.c"
 yy1026:
 	YYDEBUG(1026, *YYCURSOR);
 	yyaccept = 15;
@@ -20964,7 +20954,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy1043:
 	YYDEBUG(1043, *YYCURSOR);
-#line 1497 "ext/date/lib/parse_date.re"
+#line 1487 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
 		TIMELIB_INIT;
@@ -20975,7 +20965,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 20979 "ext/date/lib/parse_date.c"
+#line 20969 "ext/date/lib/parse_date.c"
 yy1044:
 	YYDEBUG(1044, *YYCURSOR);
 	yyaccept = 26;
@@ -21090,7 +21080,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy1048:
 	YYDEBUG(1048, *YYCURSOR);
-#line 1658 "ext/date/lib/parse_date.re"
+#line 1648 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("datenocolon");
 		TIMELIB_INIT;
@@ -21101,7 +21091,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_DATE_NOCOLON;
 	}
-#line 21105 "ext/date/lib/parse_date.c"
+#line 21095 "ext/date/lib/parse_date.c"
 yy1049:
 	YYDEBUG(1049, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -21171,7 +21161,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1059, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1060, *YYCURSOR);
-#line 1710 "ext/date/lib/parse_date.re"
+#line 1700 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll w, d;
 		DEBUG_OUTPUT("isoweekday");
@@ -21189,7 +21179,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_WEEK;
 	}
-#line 21193 "ext/date/lib/parse_date.c"
+#line 21183 "ext/date/lib/parse_date.c"
 yy1061:
 	YYDEBUG(1061, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -21252,7 +21242,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy1143;
 yy1070:
 	YYDEBUG(1070, *YYCURSOR);
-#line 1748 "ext/date/lib/parse_date.re"
+#line 1738 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("pgtextshort");
@@ -21265,7 +21255,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_PG_TEXT;
 	}
-#line 21269 "ext/date/lib/parse_date.c"
+#line 21259 "ext/date/lib/parse_date.c"
 yy1071:
 	YYDEBUG(1071, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -21738,7 +21728,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	++YYCURSOR;
 yy1107:
 	YYDEBUG(1107, *YYCURSOR);
-#line 1209 "ext/date/lib/parse_date.re"
+#line 1199 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("tomorrow");
 		TIMELIB_INIT;
@@ -21749,7 +21739,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 21753 "ext/date/lib/parse_date.c"
+#line 21743 "ext/date/lib/parse_date.c"
 yy1108:
 	YYDEBUG(1108, *YYCURSOR);
 	yyaccept = 28;
@@ -22086,7 +22076,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1140, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1141, *YYCURSOR);
-#line 1762 "ext/date/lib/parse_date.re"
+#line 1752 "ext/date/lib/parse_date.re"
 	{
 		int length = 0;
 		DEBUG_OUTPUT("pgtextreverse");
@@ -22099,7 +22089,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_PG_TEXT;
 	}
-#line 22103 "ext/date/lib/parse_date.c"
+#line 22093 "ext/date/lib/parse_date.c"
 yy1142:
 	YYDEBUG(1142, *YYCURSOR);
 	++YYCURSOR;
@@ -22143,7 +22133,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy1145:
 	YYDEBUG(1145, *YYCURSOR);
-#line 1304 "ext/date/lib/parse_date.re"
+#line 1294 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("backof | frontof");
 		TIMELIB_INIT;
@@ -22165,7 +22155,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_LF_DAY_OF_MONTH;
 	}
-#line 22169 "ext/date/lib/parse_date.c"
+#line 22159 "ext/date/lib/parse_date.c"
 yy1146:
 	YYDEBUG(1146, *YYCURSOR);
 	yyaccept = 29;
@@ -22489,7 +22479,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	}
 yy1172:
 	YYDEBUG(1172, *YYCURSOR);
-#line 1848 "ext/date/lib/parse_date.re"
+#line 1838 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll i;
 		int         behavior = 0;
@@ -22512,7 +22502,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 22516 "ext/date/lib/parse_date.c"
+#line 22506 "ext/date/lib/parse_date.c"
 yy1173:
 	YYDEBUG(1173, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -22524,7 +22514,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	++YYCURSOR;
 yy1175:
 	YYDEBUG(1175, *YYCURSOR);
-#line 1166 "ext/date/lib/parse_date.re"
+#line 1156 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("yesterday");
 		TIMELIB_INIT;
@@ -22535,7 +22525,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_RELATIVE;
 	}
-#line 22539 "ext/date/lib/parse_date.c"
+#line 22529 "ext/date/lib/parse_date.c"
 yy1176:
 	YYDEBUG(1176, *YYCURSOR);
 	yyaccept = 31;
@@ -23028,7 +23018,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1222, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1223, *YYCURSOR);
-#line 1914 "ext/date/lib/parse_date.re"
+#line 1904 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
 		TIMELIB_INIT;
@@ -23051,7 +23041,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_SHORTDATE_WITH_TIME;
 	}
-#line 23055 "ext/date/lib/parse_date.c"
+#line 23045 "ext/date/lib/parse_date.c"
 yy1224:
 	YYDEBUG(1224, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -23553,7 +23543,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1268, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1269, *YYCURSOR);
-#line 1327 "ext/date/lib/parse_date.re"
+#line 1317 "ext/date/lib/parse_date.re"
 	{
 		timelib_sll i;
 		int         behavior = 0;
@@ -23574,7 +23564,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_WEEK_DAY_OF_MONTH;
 	}
-#line 23578 "ext/date/lib/parse_date.c"
+#line 23568 "ext/date/lib/parse_date.c"
 yy1270:
 	YYDEBUG(1270, *YYCURSOR);
 	yyaccept = 24;
@@ -23621,7 +23611,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1273, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1274, *YYCURSOR);
-#line 1287 "ext/date/lib/parse_date.re"
+#line 1277 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("firstdayof | lastdayof");
 		TIMELIB_INIT;
@@ -23637,12 +23627,12 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_LF_DAY_OF_MONTH;
 	}
-#line 23641 "ext/date/lib/parse_date.c"
+#line 23631 "ext/date/lib/parse_date.c"
 yy1275:
 	YYDEBUG(1275, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1276, *YYCURSOR);
-#line 1523 "ext/date/lib/parse_date.re"
+#line 1513 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("iso8601datex");
 		TIMELIB_INIT;
@@ -23653,7 +23643,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 23657 "ext/date/lib/parse_date.c"
+#line 23647 "ext/date/lib/parse_date.c"
 yy1277:
 	YYDEBUG(1277, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -23756,7 +23746,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	YYDEBUG(1290, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(1291, *YYCURSOR);
-#line 1367 "ext/date/lib/parse_date.re"
+#line 1357 "ext/date/lib/parse_date.re"
 	{
 		DEBUG_OUTPUT("mssqltime");
 		TIMELIB_INIT;
@@ -23775,7 +23765,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_TIME24_WITH_ZONE;
 	}
-#line 23779 "ext/date/lib/parse_date.c"
+#line 23769 "ext/date/lib/parse_date.c"
 yy1292:
 	YYDEBUG(1292, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -24199,7 +24189,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= '9') goto yy1331;
 yy1329:
 	YYDEBUG(1329, *YYCURSOR);
-#line 1670 "ext/date/lib/parse_date.re"
+#line 1660 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
@@ -24224,7 +24214,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_XMLRPC_SOAP;
 	}
-#line 24228 "ext/date/lib/parse_date.c"
+#line 24218 "ext/date/lib/parse_date.c"
 yy1330:
 	YYDEBUG(1330, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -24594,7 +24584,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych <= ':') goto yy1383;
 yy1375:
 	YYDEBUG(1375, *YYCURSOR);
-#line 1776 "ext/date/lib/parse_date.re"
+#line 1766 "ext/date/lib/parse_date.re"
 	{
 		int tz_not_found;
 		DEBUG_OUTPUT("clf");
@@ -24617,7 +24607,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 		TIMELIB_DEINIT;
 		return TIMELIB_CLF;
 	}
-#line 24621 "ext/date/lib/parse_date.c"
+#line 24611 "ext/date/lib/parse_date.c"
 yy1376:
 	YYDEBUG(1376, *YYCURSOR);
 	yyaccept = 33;
@@ -24849,7 +24839,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
 	if (yych == ':') goto yy1286;
 	goto yy1329;
 }
-#line 2004 "ext/date/lib/parse_date.re"
+#line 1994 "ext/date/lib/parse_date.re"

 }

@@ -24870,7 +24860,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/parse_date.re b/ext/date/lib/parse_date.re
index 07d9c0e7348..15a96f2af95 100644
--- a/ext/date/lib/parse_date.re
+++ b/ext/date/lib/parse_date.re
@@ -32,16 +32,6 @@
 #include <assert.h>
 #include <limits.h>

-#if defined(_MSC_VER)
-# define strtoll(s, f, b) _atoi64(s)
-#elif !defined(HAVE_STRTOLL)
-# if defined(HAVE_ATOLL)
-#  define strtoll(s, f, b) atoll(s)
-# else
-#  define strtoll(s, f, b) strtol(s, f, b)
-# endif
-#endif
-
 #define EOI      257
 #define TIME     258
 #define DATE     259
diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c
index 5dcbfe35063..8f9221e6aa0 100644
--- a/ext/date/lib/parse_iso_intervals.c
+++ b/ext/date/lib/parse_iso_intervals.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 1.0.3 on Mon Sep 15 10:40:09 2025 */
+/* Generated by re2c 1.0.3 on Wed Aug  5 11:34:39 2026 */
 #line 1 "ext/date/lib/parse_iso_intervals.re"
 /*
  * The MIT License (MIT)
@@ -29,16 +29,6 @@

 #include <ctype.h>

-#if defined(_MSC_VER)
-# define strtoll(s, f, b) _atoi64(s)
-#elif !defined(HAVE_STRTOLL)
-# if defined(HAVE_ATOLL)
-#  define strtoll(s, f, b) atoll(s)
-# else
-#  define strtoll(s, f, b) strtol(s, f, b)
-# endif
-#endif
-
 #define EOI      257

 #define TIMELIB_PERIOD  260
@@ -176,11 +166,11 @@ static int scan(Scanner *s)
 std:
 	s->tok = cursor;
 	s->len = 0;
-#line 204 "ext/date/lib/parse_iso_intervals.re"
+#line 194 "ext/date/lib/parse_iso_intervals.re"



-#line 184 "ext/date/lib/parse_iso_intervals.c"
+#line 174 "ext/date/lib/parse_iso_intervals.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -247,32 +237,32 @@ static int scan(Scanner *s)
 	YYDEBUG(2, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(3, *YYCURSOR);
-#line 311 "ext/date/lib/parse_iso_intervals.re"
+#line 301 "ext/date/lib/parse_iso_intervals.re"
 	{
 		s->pos = cursor; s->line++;
 		goto std;
 	}
-#line 256 "ext/date/lib/parse_iso_intervals.c"
+#line 246 "ext/date/lib/parse_iso_intervals.c"
 yy4:
 	YYDEBUG(4, *YYCURSOR);
 	++YYCURSOR;
 yy5:
 	YYDEBUG(5, *YYCURSOR);
-#line 317 "ext/date/lib/parse_iso_intervals.re"
+#line 307 "ext/date/lib/parse_iso_intervals.re"
 	{
 		add_error(s, "Unexpected character");
 		goto std;
 	}
-#line 267 "ext/date/lib/parse_iso_intervals.c"
+#line 257 "ext/date/lib/parse_iso_intervals.c"
 yy6:
 	YYDEBUG(6, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(7, *YYCURSOR);
-#line 306 "ext/date/lib/parse_iso_intervals.re"
+#line 296 "ext/date/lib/parse_iso_intervals.re"
 	{
 		goto std;
 	}
-#line 276 "ext/date/lib/parse_iso_intervals.c"
+#line 266 "ext/date/lib/parse_iso_intervals.c"
 yy8:
 	YYDEBUG(8, *YYCURSOR);
 	yyaccept = 0;
@@ -289,7 +279,7 @@ static int scan(Scanner *s)
 	if (yych == 'T') goto yy15;
 yy10:
 	YYDEBUG(10, *YYCURSOR);
-#line 244 "ext/date/lib/parse_iso_intervals.re"
+#line 234 "ext/date/lib/parse_iso_intervals.re"
 	{
 		timelib_sll nr;
 		int         in_time = 0;
@@ -330,7 +320,7 @@ static int scan(Scanner *s)
 		TIMELIB_DEINIT;
 		return TIMELIB_PERIOD;
 	}
-#line 334 "ext/date/lib/parse_iso_intervals.c"
+#line 324 "ext/date/lib/parse_iso_intervals.c"
 yy11:
 	YYDEBUG(11, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -389,7 +379,7 @@ static int scan(Scanner *s)
 		goto yy16;
 	}
 	YYDEBUG(18, *YYCURSOR);
-#line 209 "ext/date/lib/parse_iso_intervals.re"
+#line 199 "ext/date/lib/parse_iso_intervals.re"
 	{
 		DEBUG_OUTPUT("recurrences");
 		TIMELIB_INIT;
@@ -399,7 +389,7 @@ static int scan(Scanner *s)
 		s->have_recurrences = 1;
 		return TIMELIB_PERIOD;
 	}
-#line 403 "ext/date/lib/parse_iso_intervals.c"
+#line 393 "ext/date/lib/parse_iso_intervals.c"
 yy19:
 	YYDEBUG(19, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -894,7 +884,7 @@ static int scan(Scanner *s)
 	YYDEBUG(89, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(90, *YYCURSOR);
-#line 220 "ext/date/lib/parse_iso_intervals.re"
+#line 210 "ext/date/lib/parse_iso_intervals.re"
 	{
 		timelib_time *current;

@@ -917,7 +907,7 @@ static int scan(Scanner *s)
 		TIMELIB_DEINIT;
 		return TIMELIB_ISO_DATE;
 	}
-#line 921 "ext/date/lib/parse_iso_intervals.c"
+#line 911 "ext/date/lib/parse_iso_intervals.c"
 yy91:
 	YYDEBUG(91, *YYCURSOR);
 	yych = *++YYCURSOR;
@@ -937,7 +927,7 @@ static int scan(Scanner *s)
 	YYDEBUG(95, *YYCURSOR);
 	++YYCURSOR;
 	YYDEBUG(96, *YYCURSOR);
-#line 286 "ext/date/lib/parse_iso_intervals.re"
+#line 276 "ext/date/lib/parse_iso_intervals.re"
 	{
 		DEBUG_OUTPUT("combinedrep");
 		TIMELIB_INIT;
@@ -956,9 +946,9 @@ static int scan(Scanner *s)
 		TIMELIB_DEINIT;
 		return TIMELIB_PERIOD;
 	}
-#line 960 "ext/date/lib/parse_iso_intervals.c"
+#line 950 "ext/date/lib/parse_iso_intervals.c"
 }
-#line 321 "ext/date/lib/parse_iso_intervals.re"
+#line 311 "ext/date/lib/parse_iso_intervals.re"

 }
 #ifdef PHP_WIN32
diff --git a/ext/date/lib/parse_iso_intervals.re b/ext/date/lib/parse_iso_intervals.re
index 00942007751..7757cd5edc8 100644
--- a/ext/date/lib/parse_iso_intervals.re
+++ b/ext/date/lib/parse_iso_intervals.re
@@ -27,16 +27,6 @@

 #include <ctype.h>

-#if defined(_MSC_VER)
-# define strtoll(s, f, b) _atoi64(s)
-#elif !defined(HAVE_STRTOLL)
-# if defined(HAVE_ATOLL)
-#  define strtoll(s, f, b) atoll(s)
-# else
-#  define strtoll(s, f, b) strtol(s, f, b)
-# endif
-#endif
-
 #define EOI      257

 #define TIMELIB_PERIOD  260
diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c
index c7f93580d72..c3cd79843b6 100644
--- a/ext/date/lib/parse_tz.c
+++ b/ext/date/lib/parse_tz.c
@@ -211,6 +211,7 @@ static int read_64bit_transitions(const unsigned char **tzf, timelib_tzinfo *tz)
 			buffer[i] = timelib_conv_int64_signed(buffer[i]);
 			/* Sanity check to see whether TS is just increasing */
 			if (i > 0 && !(buffer[i] > buffer[i - 1])) {
+				timelib_free(buffer);
 				return TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE;
 			}
 		}
@@ -543,7 +544,7 @@ void timelib_dump_tzinfo(timelib_tzinfo *tz)
 		timelib_free(trans_str);
 	}
 	for (i = 0; i < tz->bit64.leapcnt; i++) {
-		date_str = format_ut_time(tz->trans[i], tz);
+		date_str = format_ut_time(tz->leap_times[i].trans, tz);
 		printf (
 			"%s (%20ld) = %d\n",
 			date_str,
diff --git a/ext/date/lib/timelib.c b/ext/date/lib/timelib.c
index faf383a5fa1..787e8533416 100644
--- a/ext/date/lib/timelib.c
+++ b/ext/date/lib/timelib.c
@@ -35,7 +35,7 @@

 #define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y)

-const char *timelib_error_messages[10] = {
+const char *timelib_error_messages[18] = {
 	"No error",
 	"Cannot allocate buffer for parsing",
 	"Corrupt tzfile: The transitions in the file don't always increase",
@@ -45,7 +45,15 @@ const char *timelib_error_messages[10] = {
 	"No timezone with this name could be found",
 	"A 'slim' timezone file has been detected",
 	"The embedded POSIX string is not valid",
-	"The embedded POSIX string is empty"
+	"The embedded POSIX string is empty",
+	"Seconds value out of range",
+	"Nanoseconds value out of range",
+	"Division by zero",
+	"Integer Overflow",
+	"The ISO8601 duration string may only contain the period (P) aspect",
+	"The ISO8601 duration string is missing the perido (P) aspect",
+	"The ISO8601 duration string may only contain the time (T) aspect",
+	"The ISO8601 duration string could not be parsed",
 };

 const char *timelib_get_error_message(int error_code)
diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h
index d08aa77ea5e..97f76f048d7 100644
--- a/ext/date/lib/timelib.h
+++ b/ext/date/lib/timelib.h
@@ -30,83 +30,17 @@
 # include "timelib_config.h"
 #endif

-#define TIMELIB_VERSION 202217
-#define TIMELIB_EXTENDED_VERSION 20221701
-#define TIMELIB_ASCII_VERSION "2022.17"
+#define TIMELIB_VERSION 202601
+#define TIMELIB_EXTENDED_VERSION 20260101
+#define TIMELIB_ASCII_VERSION "2026.01"

 #include <stdlib.h>
 #include <stdbool.h>
 #include <limits.h>
 #include <inttypes.h>

-# ifndef HAVE_INT32_T
-#  if SIZEOF_INT == 4
-typedef int int32_t;
-#  elif SIZEOF_LONG == 4
-typedef long int int32_t;
-#  endif
-# endif
-
-# ifndef HAVE_UINT32_T
-#  if SIZEOF_INT == 4
-typedef unsigned int uint32_t;
-#  elif SIZEOF_LONG == 4
-typedef unsigned long int uint32_t;
-#  endif
-# endif
-
 #ifdef _WIN32
-# if _MSC_VER >= 1600
 # include <stdint.h>
-# endif
-# ifndef SIZEOF_INT
-#  define SIZEOF_INT 4
-# endif
-# ifndef SIZEOF_LONG
-#  define SIZEOF_LONG 4
-# endif
-# ifndef int32_t
-typedef __int32           int32_t;
-# endif
-# ifndef uint32_t
-typedef unsigned __int32  uint32_t;
-# endif
-# ifndef int64_t
-typedef __int64           int64_t;
-# endif
-# ifndef uint64_t
-typedef unsigned __int64  uint64_t;
-# endif
-# ifndef PRId32
-#  define PRId32       "I32d"
-# endif
-# ifndef PRIu32
-#  define PRIu32       "I32u"
-# endif
-# ifndef PRId64
-#  define PRId64       "I64d"
-# endif
-# ifndef PRIu64
-#  define PRIu64       "I64u"
-# endif
-# ifndef INT32_MAX
-#define INT32_MAX    _I32_MAX
-# endif
-# ifndef INT32_MIN
-#define INT32_MIN    ((int32_t)_I32_MIN)
-# endif
-# ifndef UINT32_MAX
-#define UINT32_MAX   _UI32_MAX
-# endif
-# ifndef INT64_MIN
-#define INT64_MIN    ((int64_t)_I64_MIN)
-# endif
-# ifndef INT64_MAX
-#define INT64_MAX    _I64_MAX
-# endif
-# ifndef UINT64_MAX
-#define UINT64_MAX   _UI64_MAX
-# endif
 #endif

 #if (defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)) && !defined(TIMELIB_FORCE_LONG32)
@@ -276,6 +210,12 @@ typedef struct _timelib_abbr_info {
 	int          dst;
 } timelib_abbr_info;

+typedef struct _timelib_duration {
+	timelib_ull  seconds;
+	uint32_t     nanoseconds;
+	bool         negative;
+} timelib_duration;
+
 #define TIMELIB_WARN_MASK                      0x1ff
 #define TIMELIB_ERR_MASK                       0x2ff

@@ -373,6 +313,8 @@ typedef struct _timelib_tzdb {
 #  define TIMELIB_USE_BUILTIN_STRNDUP 0
 #  define timelib_strndup strndup
 # endif
+#else
+# define TIMELIB_USE_BUILTIN_STRNDUP 0
 #endif

 #define TIMELIB_NONE             0x00
@@ -394,6 +336,14 @@ typedef struct _timelib_tzdb {
 #define TIMELIB_ERROR_SLIM_FILE                           0x07 /* Warns if the file is SLIM, but we can't read it */
 #define TIMELIB_ERROR_CORRUPT_POSIX_STRING                0x08
 #define TIMELIB_ERROR_EMPTY_POSIX_STRING                  0x09 /* Warns if the POSIX string is empty, but still produces results */
+#define TIMELIB_ERROR_SECONDS_OUT_OF_RANGE                0x0A /* Durations only allow 0..9223372035 */
+#define TIMELIB_ERROR_NANOSECONDS_OUT_OF_RANGE            0x0B /* Durations only allow 0..999999999 */
+#define TIMELIB_ERROR_DIVISION_BY_ZERO                    0x0C
+#define TIMELIB_ERROR_OVERFLOW                            0x0D
+#define TIMELIB_ERROR_DURATION_ONLY_PERIOD_ALLOWED        0x0E
+#define TIMELIB_ERROR_DURATION_MISSING_PERIOD             0x0F
+#define TIMELIB_ERROR_DURATION_DAYS_FOUND                 0x10
+#define TIMELIB_ERROR_ISO8601_DURATION_PARSE_FAILURE      0x11

 #ifdef __cplusplus
 extern "C" {
@@ -1097,6 +1047,149 @@ timelib_posix_str* timelib_parse_posix_str(const char *posix);
 void timelib_get_transitions_for_year(timelib_tzinfo *tz, timelib_sll year, timelib_posix_transitions *transitions);


+/* from duration.c */
+
+/* Returns a newly allocated timelib_duration struct upon success, or NULL
+ * upon failure (such as out of range for seconds/nanoseconds) with
+ * *error_code set to a value from the TIMELIB_ERROR_* set.
+ */
+timelib_duration *timelib_duration_ctor(
+	timelib_ull  seconds,
+	uint32_t     nanoseconds,
+	bool         negative,
+	int         *error_code
+);
+
+/* Updates 'duration' with the values of 'seconds', 'nanoseconds', and
+ * 'negative', after checking 'seconds' and 'nanoseconds' for their range. If
+ * either of them is out of range, it returns an error code from the
+ * TIMELIB_ERROR_* set. It also only sets 'negative' to true if 'seconds' and
+ * 'nanoseconds' aren't both NULL.
+ */
+int timelib_duration_ctor_static(
+	timelib_duration *duration,
+	timelib_ull       seconds,
+	uint32_t          nanoseconds,
+	bool              negative
+);
+
+/* Returns a newly allocated timelib_duration struct upon success, or NULL
+ * upon failure with *error set to a newly allocated error_message container
+ * (which you'll have to free). */
+timelib_duration *timelib_duration_create_from_iso8601string(
+	const char *string,
+	int        *error_code
+);
+
+/* Frees up memory allocated for the duration struct. You must not use it or
+ * its elements after calling this function. */
+void timelib_duration_dtor(timelib_duration *duration);
+
+/* Adds two durations together, and returns a newly allocated one, or NULL
+ * upon failure (such as out of range) with *error_code set to a value from
+ * the TIMELIB_ERROR_* set. */
+timelib_duration *timelib_duration_add(
+	const timelib_duration *original,
+	const timelib_duration *additional,
+	int                    *error_code
+);
+
+/* Adds two durations together, and updates the 'new_duration' argument with
+ * the calculated values. It returns an error code (from the TIMELIB_ERROR_*
+ * set), or TIMELIB_ERROR_NO_ERROR, if there was no error. */
+int timelib_duration_add_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *additional
+);
+
+/* Subtracts the second duration from the first one, and returns a newly
+ * allocated one, or NULL upon failure (such as out of range) with *error_code
+ * set to a value from the TIMELIB_ERROR_* set. */
+timelib_duration *timelib_duration_sub (
+	const timelib_duration *original,
+	const timelib_duration *minus,
+	int                    *error_code
+);
+
+/* Subtracts the second duration from the first one, and updates the
+ * 'new_duration' argument with the calculated values. It returns an error
+ * code (from the TIMELIB_ERROR_* set), or TIMELIB_ERROR_NO_ERROR, if there
+ * was no error. */
+int timelib_duration_sub_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	const timelib_duration *minus
+);
+
+/* Multiplies the duration with a certain integer factor. It returns a newly
+ * allocated duration, or NULL, upon failure (such as out of range) with
+ * *error_code set to a value from the TIMELIB_ERROR_* set. */
+timelib_duration *timelib_duration_mul (
+	const timelib_duration *original,
+	uint64_t                factor,
+	int                    *error_code
+);
+
+/* Multiplies the duration with a certain integer factor, and updates the
+ * 'new_duration' argument with the calculated values. It returns an error
+ * code (from the TIMELIB_ERROR_* set), or TIMELIB_ERROR_NO_ERROR, if there
+ * was no error. */
+int timelib_duration_mul_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	uint64_t                factor
+);
+
+/* Divides the duration with a certain integer divisor. It returns a newly
+ * allocated duration, or NULL upon failure (such as div-by-0) with
+ * *error_code set to a value from the TIMELIB_ERROR_* set. */
+timelib_duration *timelib_duration_div (
+	const timelib_duration *original,
+	uint64_t                divisor,
+	int                    *error_code
+);
+
+/* Divides the duration with a certain integer divisor, and updates the
+ * 'new_duration' argument with the calculated values. It returns an error
+ * code (from the TIMELIB_ERROR_* set), or TIMELIB_ERROR_NO_ERROR, if there
+ * was no error. */
+int timelib_duration_div_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original,
+	uint64_t                divisor
+);
+
+/* Switches the 'negate' flag of 'original' in a newly allocated duration, or
+ * NULL upon failure with *error_code set to a value from the TIMELIB_ERROR_*
+ * set */
+timelib_duration *timelib_duration_negate(
+	const timelib_duration *original,
+	int                    *error_code
+);
+
+/* Switches the 'negate' flag of 'original', and updates the 'new_duration'
+ * argument with the calculated value. It returns an error code (from the
+ * TIMELIB_ERROR_* set), or TIMELIB_ERROR_NO_ERROR, if there was no error. */
+int timelib_duration_negate_static(
+	timelib_duration       *new_duration,
+	const timelib_duration *original
+);
+
+/* Returns -1 if one is smaller than two, 0 if they're equal, and 1 if one is
+ * larger than two — IGNORING the negate flags */
+int timelib_duration_abs_compare(
+	const timelib_duration *one,
+	const timelib_duration *two
+);
+
+/* Returns -1 if one is smaller than two, 0 if they're equal, and 1 if one is
+ * larger than two. */
+int timelib_duration_compare(
+	const timelib_duration *one,
+	const timelib_duration *two
+);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/ext/date/lib/timelib_private.h b/ext/date/lib/timelib_private.h
index 42cbaa68dbb..eece57421fd 100644
--- a/ext/date/lib/timelib_private.h
+++ b/ext/date/lib/timelib_private.h
@@ -45,19 +45,17 @@
 #include <sys/types.h>
 #endif

-#if defined(HAVE_STDINT_H)
-# include <stdint.h>
-#endif
+#include <stdint.h>

-#if HAVE_UNISTD_H
+#if defined(HAVE_UNISTD_H)
 # include <unistd.h>
 #endif

-#if HAVE_IO_H
+#if defined(HAVE_IO_H)
 # include <io.h>
 #endif

-#if HAVE_DIRENT_H
+#if defined(HAVE_DIRENT_H)
 # include <dirent.h>
 #endif

@@ -89,6 +87,8 @@
 #define SECS_PER_DAY   86400
 #define SECS_PER_HOUR   3600
 #define USECS_PER_HOUR TIMELIB_LL_CONST(3600000000)
+#define NSECS_PER_SEC  TIMELIB_LL_CONST(1000000000)
+#define MAX_DURATION_SECONDS TIMELIB_LL_CONST(9223372035)

 #define DAYS_PER_WEEK      7
 #define DAYS_PER_YEAR    365