commit 59a8cce85e45329b3f0eece6b734686313bfb81a
parent 30e021c6f5621382cffb390b75e6dce9a042a906
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date: Thu, 16 Apr 2026 10:55:06 +0200
libc/time: Allow negative years in mktime()
We support years before 1900, and it means that tm_year can be a negative
number. This also means that we have to separate what is the base year
used in mktime() and the minimum year that we can represent.
Diffstat:
12 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/include/scc/bits/darwin/amd64/arch/time.h b/include/scc/bits/darwin/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/dragonfly/amd64/arch/time.h b/include/scc/bits/dragonfly/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/freebsd/amd64/arch/time.h b/include/scc/bits/freebsd/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/linux/amd64/arch/time.h b/include/scc/bits/linux/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/linux/arm/arch/time.h b/include/scc/bits/linux/arm/arch/time.h
@@ -1,4 +1,5 @@
#define _MAXYEAR 2038
+#define _MINYEAR 1902
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/linux/arm64/arch/time.h b/include/scc/bits/linux/arm64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/linux/i386/arch/time.h b/include/scc/bits/linux/i386/arch/time.h
@@ -1,4 +1,5 @@
#define _MAXYEAR 2038
+#define _MINYEAR 1902
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/linux/ppc/arch/time.h b/include/scc/bits/linux/ppc/arch/time.h
@@ -1,4 +1,5 @@
#define _MAXYEAR 2038
+#define _MINYEAR 1902
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/netbsd/amd64/arch/time.h b/include/scc/bits/netbsd/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/include/scc/bits/openbsd/amd64/arch/time.h b/include/scc/bits/openbsd/amd64/arch/time.h
@@ -1,4 +1,5 @@
-#define _MAXYEAR 9999
+#define _MAXYEAR 99999
+#define _MINYEAR -99999
typedef long time_t;
typedef long clock_t;
diff --git a/src/libc/libc.h b/src/libc/libc.h
@@ -28,7 +28,7 @@ enum {
#define FEBDAYS(y) ((_daysyear(y) == 366) ? 29 : 28)
#define EPOCH 1970
-#define MINYEAR 1900
+#define BASEYEAR 1900
#define SECMIN 60
#define SECHOUR (60 * SECMIN) /* 3600 */
#define SECDAY (24 * SECHOUR) /* 86400 */
diff --git a/src/libc/time/mktime.c b/src/libc/time/mktime.c
@@ -47,15 +47,9 @@ normalize(struct tm *tm)
|| !norm(&tm->tm_mon, &tm->tm_year, 12))
return 0;
- if (tm->tm_year < 0)
- return 0;
-
day = tm->tm_mday;
yday = 0;
- year = MINYEAR + tm->tm_year;
-
- if (year > _MAXYEAR)
- return 0;
+ year = BASEYEAR + tm->tm_year;
_daysmon[FEB] = FEBDAYS(year);
@@ -65,7 +59,7 @@ normalize(struct tm *tm)
*/
for (mon = tm->tm_mon; day < 1; --mon) {
if (mon == JAN) {
- if (year == MINYEAR)
+ if (year == INT_MIN)
return 0;
year--;
_daysmon[FEB] = FEBDAYS(year);
@@ -77,7 +71,7 @@ normalize(struct tm *tm)
for (; day > _daysmon[mon]; ++mon) {
day -= _daysmon[mon];
if (mon == DEC) {
- if (year == _MAXYEAR)
+ if (year == INT_MAX)
return 0;
year++;
_daysmon[FEB] = FEBDAYS(year);
@@ -85,11 +79,18 @@ normalize(struct tm *tm)
}
}
+ if (year < INT_MIN + BASEYEAR)
+ return 0;
+ year -= BASEYEAR;
+
+ if (year > _MAXYEAR || year < _MINYEAR)
+ return 0;
+
for (int i = 0; i < mon; ++i)
yday += _daysmon[i];
tm->tm_mon = mon;
- tm->tm_year = year - MINYEAR;
+ tm->tm_year = year;
tm->tm_mday = day;
tm->tm_yday = yday + day - 1;
tm->tm_wday = (_newyear(tm->tm_year) + tm->tm_yday) % 7;
@@ -107,7 +108,7 @@ mktime(struct tm *tm)
if (!normalize(tm))
return -1;
- year = tm->tm_year + MINYEAR;
+ year = tm->tm_year + BASEYEAR;
for (i = EPOCH; i < year; ++i)
t += _daysyear(i) * SECDAY;