[tz] [PROPOSED] Support timegm by default, as per C23

Paul Eggert eggert at cs.ucla.edu
Thu Nov 10 19:32:18 UTC 2022


Also, fix a bug in timegm; it sometimes modified its input even on
failure.
* Makefile, NEWS: Mention this.
* localtime.c (mktmcpy): New static function.
(time2sub): Use it.
(timegm, timeoff): Define even if !STD_INSPIRED.
(timeoff): Make it static if !STD_INSPIRED.
(timegm): Do not modify *TMP on failure, even if only to change
TMP->is_isdst.
* private.h (HAVE_DECL_TIMEGM): Specify reasonable default value.
(timegm): Always declare one way or another.
---
 Makefile    |  8 +++-----
 NEWS        |  5 +++++
 localtime.c | 53 +++++++++++++++++++++++++++++++++--------------------
 private.h   | 18 +++++++++++++++---
 4 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/Makefile b/Makefile
index 9ee15607..5ec0d4ab 100644
--- a/Makefile
+++ b/Makefile
@@ -209,6 +209,7 @@ LDLIBS=
 #	For example, N is 252460800 on AmigaOS.
 #  -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r
 #  -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
+#  -DHAVE_DECL_TIMEGM=0 if <time.h> does not declare timegm
 #  -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
 #  -DHAVE_GENERIC=0 if _Generic does not work*
 #  -DHAVE_GETRANDOM if getrandom works (e.g., GNU/Linux),
@@ -344,14 +345,11 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
 # If you want functions that were inspired by early versions of X3J11's work,
 # add
 #	-DSTD_INSPIRED
-# to the end of the "CFLAGS=" line.  This arranges for the functions
-# "offtime", "timelocal", "timegm", "timeoff",
-# "posix2time", and "time2posix" to be added to the time conversion library.
+# to the end of the "CFLAGS=" line.  This arranges for the following
+# functions to be added to the time conversion library.
 # "offtime" is like "gmtime" except that it accepts a second (long) argument
 # that gives an offset to add to the time_t when converting it.
 # "timelocal" is equivalent to "mktime".
-# "timegm" is like "timelocal" except that it turns a struct tm into
-# a time_t using UT (rather than local time as "timelocal" does).
 # "timeoff" is like "timegm" except that it accepts a second (long) argument
 # that gives an offset to use when converting to a time_t.
 # "posix2time" and "time2posix" are described in an included manual page.
diff --git a/NEWS b/NEWS
index 4a053077..23f1f7f1 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ News for the tz database
 
   Briefly:
     Fix some pre-1996 timestamps in northern Canada
+    C23 timegm now supported by default
 
 Unreleased, experimental changes
 
@@ -22,6 +23,10 @@ Unreleased, experimental changes
 
   Changes to code
 
+    timegm, which tzcode implemented in 1989, will finally be
+    standardized 34 years later as part of C23, so timegm is now
+    supported even if STD_INSPIRED is not defined.
+
     Fix bug in zdump's tzalloc emulation on hosts that lack tm_zone.
     (Problem reported by Đoàn Trần Công Danh.)
 
diff --git a/localtime.c b/localtime.c
index 712be135..9935b5d3 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1951,6 +1951,23 @@ tmcomp(register const struct tm *const atmp,
 	return result;
 }
 
+/* Copy to *DEST from *SRC.  Copy only the members needed for mktime,
+   as other members might not be initialized.  */
+static void
+mktmcpy(struct tm *dest, struct tm *const src)
+{
+  dest->tm_sec = src->tm_sec;
+  dest->tm_min = src->tm_min;
+  dest->tm_hour = src->tm_hour;
+  dest->tm_mday = src->tm_mday;
+  dest->tm_mon = src->tm_mon;
+  dest->tm_year = src->tm_year;
+  dest->tm_isdst = src->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+  dest->TM_GMTOFF = src->TM_GMTOFF;
+#endif
+}
+
 static time_t
 time2sub(struct tm *const tmp,
 	 struct tm *(*funcp)(struct state const *, time_t const *,
@@ -1972,17 +1989,7 @@ time2sub(struct tm *const tmp,
 	struct tm			yourtm, mytm;
 
 	*okayp = false;
-
-	yourtm.tm_sec = tmp->tm_sec;
-	yourtm.tm_min = tmp->tm_min;
-	yourtm.tm_hour = tmp->tm_hour;
-	yourtm.tm_mday = tmp->tm_mday;
-	yourtm.tm_mon = tmp->tm_mon;
-	yourtm.tm_year = tmp->tm_year;
-	yourtm.tm_isdst = tmp->tm_isdst;
-#if defined TM_GMTOFF && ! UNINIT_TRAP
-	yourtm.TM_GMTOFF = tmp->TM_GMTOFF;
-#endif
+	mktmcpy(&yourtm, tmp);
 
 	if (do_norm_secs) {
 		if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
@@ -2289,7 +2296,6 @@ mktime(struct tm *tmp)
 }
 
 #ifdef STD_INSPIRED
-
 time_t
 timelocal(struct tm *tmp)
 {
@@ -2297,13 +2303,9 @@ timelocal(struct tm *tmp)
 		tmp->tm_isdst = -1;	/* in case it wasn't initialized */
 	return mktime(tmp);
 }
-
-time_t
-timegm(struct tm *tmp)
-{
-  return timeoff(tmp, 0);
-}
-
+#else
+static
+#endif
 time_t
 timeoff(struct tm *tmp, long offset)
 {
@@ -2313,7 +2315,18 @@ timeoff(struct tm *tmp, long offset)
   return time1(tmp, gmtsub, gmtptr, offset);
 }
 
-#endif /* defined STD_INSPIRED */
+time_t
+timegm(struct tm *tmp)
+{
+  time_t t;
+  struct tm tmcpy;
+  mktmcpy(&tmcpy, tmp);
+  tmcpy.tm_wday = -1;
+  t = timeoff(&tmcpy, 0);
+  if (0 <= tmcpy.tm_wday)
+    *tmp = tmcpy;
+  return t;
+}
 
 static int_fast32_t
 leapcorr(struct state const *sp, time_t t)
diff --git a/private.h b/private.h
index 7cbd56d2..7320173f 100644
--- a/private.h
+++ b/private.h
@@ -543,9 +543,24 @@ struct tm *localtime(time_t const *);
 struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
 time_t mktime(struct tm *);
 time_t time(time_t *);
+time_t timegm(struct tm *);
 void tzset(void);
 #endif
 
+#ifndef HAVE_DECL_TIMEGM
+# if (202311 <= __STDC_VERSION__ \
+      || defined __GLIBC__ || defined __tm_zone /* musl */ \
+      || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+      || (defined __APPLE__ && defined __MACH__))
+#  define HAVE_DECL_TIMEGM true
+# else
+#  define HAVE_DECL_TIMEGM false
+# endif
+#endif
+#if !HAVE_DECL_TIMEGM && !defined timegm
+time_t timegm(struct tm *);
+#endif
+
 #if !HAVE_DECL_ASCTIME_R && !defined asctime_r
 extern char *asctime_r(struct tm const *restrict, char *restrict);
 #endif
@@ -582,9 +597,6 @@ extern long altzone;
 # if TZ_TIME_T || !defined offtime
 struct tm *offtime(time_t const *, long);
 # endif
-# if TZ_TIME_T || !defined timegm
-time_t timegm(struct tm *);
-# endif
 # if TZ_TIME_T || !defined timelocal
 time_t timelocal(struct tm *);
 # endif
-- 
2.37.2



More information about the tz mailing list