[1/1] Y2038: add function __difftime64

Message ID 20180620121427.25397-2-albert.aribaud@3adev.fr
State New, archived
Headers

Commit Message

Albert ARIBAUD June 20, 2018, 12:14 p.m. UTC
  Note:

1. The implementation expects __time64_t arguments, and could,
   in theory, require 64 bits to express the difference accurately;
   but it returns a double, which only provides about 55 significant
   bits.

   We could make it return a long double, which would be more than
   enough for 64 bits accuracy. But then, existing source code which
   uses difftime, and therefore stores difftime results in doubles,
   would need to change to long doubles. However, we want 64-bit time
   support to work without any application source code change.

   Besides, 55 bits allow for counting seconds accurately over 417
   billion years, which is arguably enough for most actual uses of
   difftime.

2. The 64-bit time implementation was obtained by duplicating the
   original 32-bit code then simplifying the source code based on
   the knowledge that __time64_t is a 64-bit signed integer for
   all architectures. This led to the following removals:
   - in the subtract64 function, removal of code which handled
     unsigned time handling (as __time64_t is signed);
   - in the difftime64 function, removal of code which handled
     time bitsize smaller than or equal to that of a double matissa
     (as __time64_t has more bits than a double mantissa can hold)

3. The 32-bit implementation is now a wrapper around the 64-bit
   one.
---
 include/time.h  |   6 +++
 time/difftime.c | 116 ++++++++++++++++++++++++------------------------
 2 files changed, 64 insertions(+), 58 deletions(-)
  

Patch

diff --git a/include/time.h b/include/time.h
index 5afb12305e..63cc855749 100644
--- a/include/time.h
+++ b/include/time.h
@@ -125,6 +125,12 @@  extern char * __strptime_internal (const char *rp, const char *fmt,
 				   struct tm *tm, void *statep,
 				   locale_t locparam) attribute_hidden;
 
+#if __TIMESIZE == 64
+# define __difftime64 __difftime
+#else
+extern double __difftime64 (__time64_t time1, __time64_t time0);
+#endif
+
 extern double __difftime (time_t time1, time_t time0);
 
 /* Use in the clock_* functions.  Size of the field representing the
diff --git a/time/difftime.c b/time/difftime.c
index 7c5dd9898b..fbc7ad9d57 100644
--- a/time/difftime.c
+++ b/time/difftime.c
@@ -30,87 +30,75 @@ 
 /* Return the difference between TIME1 and TIME0, where TIME0 <= TIME1.
    time_t is known to be an integer type.  */
 
-static double
-subtract (time_t time1, time_t time0)
+static double subtract (__time64_t time1, __time64_t time0)
 {
-  if (! TYPE_SIGNED (time_t))
-    return time1 - time0;
-  else
-    {
-      /* Optimize the common special cases where time_t
-	 can be converted to uintmax_t without losing information.  */
-      uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0;
-      double delta = dt;
-
-      if (UINTMAX_MAX / 2 < INTMAX_MAX)
-	{
-	  /* This is a rare host where uintmax_t has padding bits, and possibly
-	     information was lost when converting time_t to uintmax_t.
-	     Check for overflow by comparing dt/2 to (time1/2 - time0/2).
-	     Overflow occurred if they differ by more than a small slop.
-	     Thanks to Clive D.W. Feather for detailed technical advice about
-	     hosts with padding bits.
+  /* Optimize the common special cases where __time64_t
+ can be converted to uintmax_t without losing information.  */
+  uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0;
+  double delta = dt;
 
-	     In the following code the "h" prefix means half.  By range
-	     analysis, we have:
+  if (UINTMAX_MAX / 2 < INTMAX_MAX)
+{
+  /* This is a rare host where uintmax_t has padding bits, and possibly
+	 information was lost when converting time_t to uintmax_t.
+	 Check for overflow by comparing dt/2 to (time1/2 - time0/2).
+	 Overflow occurred if they differ by more than a small slop.
+	 Thanks to Clive D.W. Feather for detailed technical advice about
+	 hosts with padding bits.
 
-                  -0.5 <= ht1 - 0.5*time1 <= 0.5
-                  -0.5 <= ht0 - 0.5*time0 <= 0.5
-                  -1.0 <= dht - 0.5*(time1 - time0) <= 1.0
+	 In the following code the "h" prefix means half.  By range
+	 analysis, we have:
 
-             If overflow has not occurred, we also have:
+			  -0.5 <= ht1 - 0.5*time1 <= 0.5
+			  -0.5 <= ht0 - 0.5*time0 <= 0.5
+			  -1.0 <= dht - 0.5*(time1 - time0) <= 1.0
 
-                  -0.5 <= hdt - 0.5*(time1 - time0) <= 0
-                  -1.0 <= dht - hdt <= 1.5
+		 If overflow has not occurred, we also have:
 
-             and since dht - hdt is an integer, we also have:
+			  -0.5 <= hdt - 0.5*(time1 - time0) <= 0
+			  -1.0 <= dht - hdt <= 1.5
 
-                  -1 <= dht - hdt <= 1
+		 and since dht - hdt is an integer, we also have:
 
-             or equivalently:
+			  -1 <= dht - hdt <= 1
 
-                  0 <= dht - hdt + 1 <= 2
+		 or equivalently:
 
-             In the above analysis, all the operators have their exact
-             mathematical semantics, not C semantics.  However, dht - hdt +
-             1 is unsigned in C, so it need not be compared to zero.  */
+			  0 <= dht - hdt + 1 <= 2
 
-	  uintmax_t hdt = dt / 2;
-	  time_t ht1 = time1 / 2;
-	  time_t ht0 = time0 / 2;
-	  time_t dht = ht1 - ht0;
+		 In the above analysis, all the operators have their exact
+		 mathematical semantics, not C semantics.  However, dht - hdt +
+		 1 is unsigned in C, so it need not be compared to zero.  */
 
-	  if (2 < dht - hdt + 1)
-	    {
-	      /* Repair delta overflow.
+  uintmax_t hdt = dt / 2;
+  __time64_t ht1 = time1 / 2;
+  __time64_t ht0 = time0 / 2;
+  __time64_t dht = ht1 - ht0;
 
-		 The following expression contains a second rounding,
-		 so the result may not be the closest to the true answer.
-		 This problem occurs only with very large differences.
-		 It's too painful to fix this portably.  */
+  if (2 < dht - hdt + 1)
+	{
+	  /* Repair delta overflow.
 
-	      delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2);
-	    }
+	 The following expression contains a second rounding,
+	 so the result may not be the closest to the true answer.
+	 This problem occurs only with very large differences.
+	 It's too painful to fix this portably.  */
+
+	  delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2);
 	}
+}
 
-      return delta;
-    }
+  return delta;
 }
 
 /* Return the difference between TIME1 and TIME0.  */
 double
-__difftime (time_t time1, time_t time0)
+__difftime64 (__time64_t time1, __time64_t time0)
 {
-  /* Convert to double and then subtract if no double-rounding error could
+  /* Convert to long double and then subtract if no double-rounding error could
      result.  */
 
-  if (TYPE_BITS (time_t) <= DBL_MANT_DIG
-      || (TYPE_FLOATING (time_t) && sizeof (time_t) < sizeof (long double)))
-    return (double) time1 - (double) time0;
-
-  /* Likewise for long double.  */
-
-  if (TYPE_BITS (time_t) <= LDBL_MANT_DIG || TYPE_FLOATING (time_t))
+  if (TYPE_BITS (__time64_t) <= LDBL_MANT_DIG)
     return (long double) time1 - (long double) time0;
 
   /* Subtract the smaller integer from the larger, convert the difference to
@@ -118,4 +106,16 @@  __difftime (time_t time1, time_t time0)
 
   return time1 < time0 ? - subtract (time0, time1) : subtract (time1, time0);
 }
+
+#if __TIMESIZE != 64
+
+/* Return the difference between TIME1 and TIME0.  */
+double
+__difftime (time_t time1, time_t time0)
+{
+  return __difftime64 (time1, time0);
+}
+
+#endif
+
 strong_alias (__difftime, difftime)