[PATCH v2 1/4] time: Use __tz_convert in mktime implementation

Florian Weimer fweimer@redhat.com
Thu Jan 2 17:42:31 GMT 2025


This replaces calls to convert_time and changes the signature
of __mktime_internal to accept a flag instead of a conversion
function.
---
 time/mktime-internal.h | 23 +++++++++-------
 time/mktime.c          | 59 +++++++++++++++++-------------------------
 time/timegm.c          |  2 +-
 3 files changed, 38 insertions(+), 46 deletions(-)

diff --git a/time/mktime-internal.h b/time/mktime-internal.h
index 5fbcc0ec60..64dc35b3cf 100644
--- a/time/mktime-internal.h
+++ b/time/mktime-internal.h
@@ -51,6 +51,15 @@ typedef int mktime_offset_t;
 #endif
 
 #ifndef _LIBC
+/* Convert T to a struct tm value in *TM.  Use local time if USE_LOCAL.
+   T must be in range for __time64_t.  Return TM if successful, NULL
+   (setting errno) on failure.  */
+static inline struct tm *
+tz_convert (long_int t, bool use_local, struct tm *tm)
+{
+  return (use_local ? localtime_r : gmtime_r) (&t, tm);
+}
+
 
 /* Although glibc source code uses leading underscores, Gnulib wants
    ordinary names.
@@ -61,19 +70,13 @@ typedef int mktime_offset_t;
    Similarly for gmtime_r.  See the gnulib time_r module for one way
    to implement this.  */
 
-# undef __gmtime_r
-# undef __localtime_r
-# define __gmtime_r gmtime_r
-# define __localtime_r localtime_r
-
 # define __mktime_internal mktime_internal
+# define __tz_convert tz_convert
 
 #endif
 
 /* Subroutine of mktime.  Return the time_t representation of TP and
-   normalize TP, given that a struct tm * maps to a time_t as performed
-   by FUNC.  Record next guess for localtime-gmtime offset in *OFFSET.  */
-extern __time64_t __mktime_internal (struct tm *tp,
-                                     struct tm *(*func) (__time64_t const *,
-                                                         struct tm *),
+   normalize TP, in local time if USE_LOCAL.  Record next guess for
+   localtime-gmtime offset in *OFFSET.  */
+extern __time64_t __mktime_internal (struct tm *tp, bool use_local,
                                      mktime_offset_t *offset) attribute_hidden;
diff --git a/time/mktime.c b/time/mktime.c
index 86d496ca65..2a1a925b75 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -250,29 +250,17 @@ tm_diff (long_int year, long_int yday, int hour, int min, int sec,
 		     tp->tm_hour, tp->tm_min, tp->tm_sec);
 }
 
-/* Use CONVERT to convert T to a struct tm value in *TM.  T must be in
-   range for __time64_t.  Return TM if successful, NULL (setting errno) on
-   failure.  */
-static struct tm *
-convert_time (struct tm *(*convert) (const __time64_t *, struct tm *),
-	      long_int t, struct tm *tm)
-{
-  __time64_t x = t;
-  return convert (&x, tm);
-}
-
-/* Use CONVERT to convert *T to a broken down time in *TP.
+/* Convert *T to a broken down time in *TP.  Use local time if USE_LOCAL.
    If *T is out of range for conversion, adjust it so that
    it is the nearest in-range value and then convert that.
    A value is in range if it fits in both __time64_t and long_int.
    Return TP on success, NULL (setting errno) on failure.  */
 static struct tm *
-ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
-		long_int *t, struct tm *tp)
+ranged_convert (long_int *t, bool use_local, struct tm *tp)
 {
   long_int t1 = (*t < mktime_min ? mktime_min
 		 : *t <= mktime_max ? *t : mktime_max);
-  struct tm *r = convert_time (convert, t1, tp);
+  struct tm *r = __tz_convert (t1, use_local, tp);
   if (r)
     {
       *t = t1;
@@ -293,7 +281,7 @@ ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
       long_int mid = long_int_avg (ok, bad);
       if (mid == ok || mid == bad)
 	break;
-      if (convert_time (convert, mid, tp))
+      if (__tz_convert (mid, use_local, tp))
 	ok = mid, oktm = *tp;
       else if (errno != EOVERFLOW)
 	return NULL;
@@ -310,28 +298,28 @@ ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
 
 
 /* Convert *TP to a __time64_t value, inverting
-   the monotonic and mostly-unit-linear conversion function CONVERT.
+   the monotonic and mostly-unit-linear conversion function
+   gmtime_r or localtime_r (if USE_LOCAL).
    Use *OFFSET to keep track of a guess at the offset of the result,
    compared to what the result would be for UTC without leap seconds.
-   If *OFFSET's guess is correct, only one CONVERT call is needed.
+   If *OFFSET's guess is correct, only one conversion call is needed.
    If successful, set *TP to the canonicalized struct tm;
    otherwise leave *TP alone, return ((time_t) -1) and set errno.
    This function is external because it is used also by timegm.c.  */
 __time64_t
-__mktime_internal (struct tm *tp,
-		   struct tm *(*convert) (const __time64_t *, struct tm *),
-		   mktime_offset_t *offset)
+__mktime_internal (struct tm *tp, bool use_local, mktime_offset_t *offset)
 {
   struct tm tm;
 
-  /* The maximum number of probes (calls to CONVERT) should be enough
-     to handle any combinations of time zone rule changes, solar time,
-     leap seconds, and oscillations around a spring-forward gap.
-     POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
+  /* The maximum number of probes (calls to gmtime_r/localime_r)
+     should be enough to handle any combinations of time zone rule
+     changes, solar time, leap seconds, and oscillations around a
+     spring-forward gap.  POSIX.1 prohibits leap seconds, but some
+     hosts have them anyway.  */
   int remaining_probes = 6;
 
-  /* Time requested.  Copy it in case CONVERT modifies *TP; this can
-     occur if TP is localtime's returned value and CONVERT is localtime.  */
+  /* Time requested.  Copy it in case the probe modifies *TP; this can
+     occur if TP is localtime's returned value and USE_LOCAL.  */
   int sec = tp->tm_sec;
   int min = tp->tm_min;
   int hour = tp->tm_hour;
@@ -376,8 +364,8 @@ __mktime_internal (struct tm *tp,
 	sec = 59;
     }
 
-  /* Invert CONVERT by probing.  First assume the same offset as last
-     time.  */
+  /* Invert gmtime_r/localtime_r by probing.  First assume the same
+     offset as last time.  */
 
   INT_SUBTRACT_WRAPV (0, off, &negative_offset_guess);
   long_int t0 = ydhms_diff (year, yday, hour, min, sec,
@@ -389,7 +377,7 @@ __mktime_internal (struct tm *tp,
 
   while (true)
     {
-      if (! ranged_convert (convert, &t, &tm))
+      if (! ranged_convert (&t, use_local, &tm))
 	return -1;
       long_int dt = tm_diff (year, yday, hour, min, sec, &tm);
       if (dt == 0)
@@ -468,7 +456,7 @@ __mktime_internal (struct tm *tp,
 	    if (! INT_ADD_WRAPV (t, delta * direction, &ot))
 	      {
 		struct tm otm;
-		if (! ranged_convert (convert, &ot, &otm))
+		if (! ranged_convert (&ot, use_local, &otm))
 		  return -1;
 		if (! isdst_differ (isdst, otm.tm_isdst))
 		  {
@@ -478,7 +466,7 @@ __mktime_internal (struct tm *tp,
 						&otm);
 		    if (mktime_min <= gt && gt <= mktime_max)
 		      {
-			if (convert_time (convert, gt, &tm))
+			if (__tz_convert (gt, use_local, &tm))
 			  {
 			    t = gt;
 			    goto offset_found;
@@ -492,7 +480,8 @@ __mktime_internal (struct tm *tp,
 
       /* No unusual DST offset was found nearby.  Assume one-hour DST.  */
       t += 60 * 60 * dst_difference;
-      if (mktime_min <= t && t <= mktime_max && convert_time (convert, t, &tm))
+      if (mktime_min <= t && t <= mktime_max
+	  && __tz_convert (t, use_local, &tm))
 	goto offset_found;
 
       __set_errno (EOVERFLOW);
@@ -519,7 +508,7 @@ __mktime_internal (struct tm *tp,
 	  __set_errno (EOVERFLOW);
 	  return -1;
 	}
-      if (! convert_time (convert, t, &tm))
+      if (! __tz_convert (t, use_local, &tm))
 	return -1;
     }
 
@@ -542,7 +531,7 @@ __mktime64 (struct tm *tp)
 
 # if defined _LIBC || NEED_MKTIME_WORKING
   static mktime_offset_t localtime_offset;
-  return __mktime_internal (tp, __localtime64_r, &localtime_offset);
+  return __mktime_internal (tp, true, &localtime_offset);
 # else
 #  undef mktime
   return mktime (tp);
diff --git a/time/timegm.c b/time/timegm.c
index 5e5fa0127f..ac3f98036d 100644
--- a/time/timegm.c
+++ b/time/timegm.c
@@ -31,7 +31,7 @@ __timegm64 (struct tm *tmp)
 {
   static mktime_offset_t gmtime_offset;
   tmp->tm_isdst = 0;
-  return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset);
+  return __mktime_internal (tmp, false, &gmtime_offset);
 }
 
 #if defined _LIBC && __TIMESIZE != 64
-- 
2.47.1




More information about the Libc-alpha mailing list