From 6f2e9b6938c421ff05d57a73f0bbe0dabed0456d Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 6 Jun 2022 11:20:08 -0700
Subject: [PROPOSED] Stricter mktime -1 heuristic in strftime

This responds to comments by Almaz Mingaleev in:
https://mm.icann.org/pipermail/tz/2022-June/031469.html
* strftime.c (signum): New static function.
(_fmt): Use a stricter heuristic when checking mktime when
it returns -1.
---
 strftime.c | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/strftime.c b/strftime.c
index 5273155..504639e 100644
--- a/strftime.c
+++ b/strftime.c
@@ -126,6 +126,13 @@ strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
 }
 #endif
 
+/* Return -1, 0, 1 if n is negative, zero, positive.  */
+static int
+signum(int n)
+{
+  return (n > 0) - (n < 0);
+}
+
 size_t
 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
 {
@@ -323,17 +330,31 @@ label:
 					tm.tm_yday = -1;
 					mkt = mktime(&tm);
 					if (mkt == (time_t) -1) {
-					  /* Fail unless this -1 represents
-					     a valid time.  */
+					  /* Fail unless the -1 almost surely
+					     represents a valid time.  Do not
+					     rely on errno, since it can change
+					     even if mktime succeeds.  A
+					     non-TZDB failing mktime might
+					     set TM to junk values, so reject
+					     mktime's result unless it matches
+					     localtime_r so closely that it's
+					     unlikely to be junk.  */
 					  struct tm tm_1;
 					  if (!localtime_r(&mkt, &tm_1))
 					    return NULL;
 					  if (!(tm.tm_year == tm_1.tm_year
 						&& tm.tm_yday == tm_1.tm_yday
+						&& tm.tm_mon == tm_1.tm_mon
+						&& tm.tm_mday == tm_1.tm_mday
+						&& tm.tm_wday == tm_1.tm_wday
 						&& tm.tm_hour == tm_1.tm_hour
 						&& tm.tm_min == tm_1.tm_min
-						&& tm.tm_sec == tm_1.tm_sec))
+						&& tm.tm_sec == tm_1.tm_sec
+						&& (signum(tm.tm_isdst)
+						    == signum(tm_1.tm_isdst))))
 					    return NULL;
+					  /* Guarantee that tm is not junk.  */
+					  tm = tm_1;
 					}
 					if (TYPE_SIGNED(time_t)) {
 					  intmax_t n = mkt;
-- 
2.36.1

