This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH v2 1/1] Y2038: make __mktime_internal compatible with 64-bit-time
- From: "Albert ARIBAUD (3ADEV)" <albert dot aribaud at 3adev dot fr>
- To: libc-alpha at sourceware dot org
- Cc: "Albert ARIBAUD (3ADEV)" <albert dot aribaud at 3adev dot fr>
- Date: Tue, 19 Jun 2018 23:01:12 +0200
- Subject: [PATCH v2 1/1] Y2038: make __mktime_internal compatible with 64-bit-time
- References: <20180619210112.1600-1-albert.aribaud@3adev.fr>
This implies that its callers be 64-bit-time compatible too.
It is done by creating 64-bit-time versions of these and
turning their original 32-bit-time versions into wrappers
(at a slight execution time cost).
The callers affected are:
* mktime
* timelocal (as an alias of mktime)
* timegm
---
include/time.h | 12 ++--
time/mktime.c | 180 ++++++++++++++++++++++++-------------------------
time/timegm.c | 24 +++++--
3 files changed, 112 insertions(+), 104 deletions(-)
diff --git a/include/time.h b/include/time.h
index eaec898fac..8c261abcf4 100644
--- a/include/time.h
+++ b/include/time.h
@@ -50,13 +50,13 @@ extern void __tzset_parse_tz (const char *tz) attribute_hidden;
extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
__THROW attribute_hidden;
-/* 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
+/* Subroutine of `mktime'. Return the `__time64_t' representation of TP and
+ normalize TP, given that a `struct tm *' maps to a `__time64_t' as performed
by FUNC. Keep track of next guess for time_t offset in *OFFSET. */
-extern time_t __mktime_internal (struct tm *__tp,
- struct tm *(*__func) (const time_t *,
- struct tm *),
- time_t *__offset) attribute_hidden;
+extern __time64_t __mktime_internal (struct tm *__tp,
+ struct tm *(*__func) (const __time64_t *,
+ struct tm *),
+ __time64_t *__offset) attribute_hidden;
/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
we define __ctime64 as ctime so that nis/nis_print.c can get linked
diff --git a/time/mktime.c b/time/mktime.c
index 5f038a212f..18837c87cd 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -1,4 +1,4 @@
-/* Convert a 'struct tm' to a time_t value.
+/* Convert a 'struct tm' to a __time64_t value.
Copyright (C) 1993-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Paul Eggert <eggert@twinsun.com>.
@@ -35,6 +35,7 @@
#include <time.h>
#include <limits.h>
+#include <errno.h>
#include <string.h> /* For the real memcpy prototype. */
@@ -92,50 +93,36 @@ verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2);
#define SHR(a, b) \
((-1 >> 1 == -1 \
&& (long_int) -1 >> 1 == -1 \
- && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t))) \
+ && (__time64_t) -1 >> 1 == -1) \
? (a) >> (b) \
: (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
/* The extra casts in the following macros work around compiler bugs,
e.g., in Cray C 5.0.3.0. */
-/* True if the arithmetic type T is an integer type. bool counts as
- an integer. */
-#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
-
/* True if negative values of the signed integer type T use two's
complement, or if T is an unsigned integer type. */
#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
-/* True if the arithmetic type T is signed. */
-#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-
/* The maximum and minimum values for the integer type T. These
macros have undefined behavior if T is signed and has padding bits.
If this is a problem for you, please let us know how to fix it for
your host. */
-#define TYPE_MINIMUM(t) \
- ((t) (! TYPE_SIGNED (t) \
- ? (t) 0 \
- : ~ TYPE_MAXIMUM (t)))
-#define TYPE_MAXIMUM(t) \
- ((t) (! TYPE_SIGNED (t) \
- ? (t) -1 \
- : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
-
-#ifndef TIME_T_MIN
-# define TIME_T_MIN TYPE_MINIMUM (time_t)
+#define TYPE_MINIMUM(t) (~ TYPE_MAXIMUM (t))
+#define TYPE_MAXIMUM(t) ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)
+
+#ifndef TIME64_T_MIN
+# define TIME64_T_MIN TYPE_MINIMUM (__time64_t)
#endif
-#ifndef TIME_T_MAX
-# define TIME_T_MAX TYPE_MAXIMUM (time_t)
+#ifndef TIME64_T_MAX
+# define TIME64_T_MAX TYPE_MAXIMUM (__time64_t)
#endif
-#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
+#define TIME_T_MIDPOINT (SHR (TIME64_T_MIN + TIME64_T_MAX, 1) + 1)
-verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
verify (twos_complement_arithmetic,
(TYPE_TWOS_COMPLEMENT (int)
&& TYPE_TWOS_COMPLEMENT (long_int)
- && TYPE_TWOS_COMPLEMENT (time_t)));
+ && TYPE_TWOS_COMPLEMENT (__time64_t)));
#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
@@ -196,7 +183,7 @@ isdst_differ (int a, int b)
The result may overflow. It is the caller's responsibility to
detect overflow. */
-static time_t
+static __time64_t
ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
int year0, int yday0, int hour0, int min0, int sec0)
{
@@ -212,80 +199,73 @@ ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
int b400 = SHR (b100, 2);
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
- /* Compute the desired time in time_t precision. Overflow might
+ /* Compute the desired time in __time64_t precision. Overflow might
occur here. */
- time_t tyear1 = year1;
- time_t years = tyear1 - year0;
- time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
- time_t hours = 24 * days + hour1 - hour0;
- time_t minutes = 60 * hours + min1 - min0;
- time_t seconds = 60 * minutes + sec1 - sec0;
+ __time64_t tyear1 = year1;
+ __time64_t years = tyear1 - year0;
+ __time64_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
+ __time64_t hours = 24 * days + hour1 - hour0;
+ __time64_t minutes = 60 * hours + min1 - min0;
+ __time64_t seconds = 60 * minutes + sec1 - sec0;
return seconds;
}
/* Return the average of A and B, even if A + B would overflow. */
-static time_t
-time_t_avg (time_t a, time_t b)
+static __time64_t
+time_t_avg (__time64_t a, __time64_t b)
{
return SHR (a, 1) + SHR (b, 1) + (a & b & 1);
}
-/* Return 1 if A + B does not overflow. If time_t is unsigned and if
- B's top bit is set, assume that the sum represents A - -B, and
- return 1 if the subtraction does not wrap around. */
+/* Return 1 if A + B does not overflow. */
static int
-time_t_add_ok (time_t a, time_t b)
+time_t_add_ok (__time64_t a, __time64_t b)
{
- if (! TYPE_SIGNED (time_t))
- {
- time_t sum = a + b;
- return (sum < a) == (TIME_T_MIDPOINT <= b);
- }
- else if (WRAPV)
+ if (WRAPV)
{
- time_t sum = a + b;
+ __time64_t sum = a + b;
return (sum < a) == (b < 0);
}
else
{
- time_t avg = time_t_avg (a, b);
- return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
+ __time64_t avg = time_t_avg (a, b);
+ return TIME64_T_MIN / 2 <= avg && avg <= TIME64_T_MAX / 2;
}
}
/* Return 1 if A + B does not overflow. */
static int
-time_t_int_add_ok (time_t a, int b)
+time_t_int_add_ok (__time64_t a, int b)
{
- verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX);
+ verify (int_no_wider_than_time_t, INT_MAX <= TIME64_T_MAX);
if (WRAPV)
{
- time_t sum = a + b;
+ __time64_t sum = a + b;
return (sum < a) == (b < 0);
}
else
{
int a_odd = a & 1;
- time_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b));
- return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
+ __time64_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b));
+ return TIME64_T_MIN / 2 <= avg && avg <= TIME64_T_MAX / 2;
}
}
-/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
+/* Return a __time64_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
assuming that *T corresponds to *TP and that no clock adjustments
occurred between *TP and the desired time.
If TP is null, return a value not equal to *T; this avoids false matches.
If overflow occurs, yield the minimal or maximal value, except do not
yield a value equal to *T. */
-static time_t
+static __time64_t
guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
- const time_t *t, const struct tm *tp)
+ const __time64_t *t, const struct tm *tp)
{
if (tp)
{
- time_t d = ydhms_diff (year, yday, hour, min, sec,
- tp->tm_year, tp->tm_yday,
- tp->tm_hour, tp->tm_min, tp->tm_sec);
+ __time64_t d = ydhms_diff (year, yday, hour, min, sec,
+ tp->tm_year, tp->tm_yday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
if (time_t_add_ok (*t, d))
return *t + d;
}
@@ -296,30 +276,30 @@ guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
match; and don't oscillate between two values, as that would
confuse the spring-forward gap detector. */
return (*t < TIME_T_MIDPOINT
- ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
- : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
+ ? (*t <= TIME64_T_MIN + 1 ? *t + 1 : TIME64_T_MIN)
+ : (TIME64_T_MAX - 1 <= *t ? *t - 1 : TIME64_T_MAX));
}
/* Use CONVERT to convert *T to a broken down time in *TP.
If *T is out of range for conversion, adjust it so that
it is the nearest in-range value and then convert that. */
static struct tm *
-ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
- time_t *t, struct tm *tp)
+ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
+ __time64_t *t, struct tm *tp)
{
struct tm *r = convert (t, tp);
if (!r && *t)
{
- time_t bad = *t;
- time_t ok = 0;
+ __time64_t bad = *t;
+ __time64_t ok = 0;
- /* BAD is a known unconvertible time_t, and OK is a known good one.
+ /* BAD is a known unconvertible __time64_t, and OK is a known good one.
Use binary search to narrow the range between BAD and OK until
they differ by 1. */
while (bad != ok + (bad < 0 ? -1 : 1))
{
- time_t mid = *t = time_t_avg (ok, bad);
+ __time64_t mid = *t = time_t_avg (ok, bad);
r = convert (t, tp);
if (r)
ok = mid;
@@ -340,18 +320,18 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
}
-/* Convert *TP to a time_t value, inverting
+/* Convert *TP to a __time64_t value, inverting
the monotonic and mostly-unit-linear conversion function CONVERT.
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.
This function is external because it is used also by timegm.c. */
-time_t
+__time64_t
__mktime_internal (struct tm *tp,
- struct tm *(*convert) (const time_t *, struct tm *),
- time_t *offset)
+ struct tm *(*convert) (const __time64_t *, struct tm *),
+ __time64_t *offset)
{
- time_t t, gt, t0, t1, t2;
+ __time64_t t, gt, t0, t1, t2;
struct tm tm;
/* The maximum number of probes (calls to CONVERT) should be enough
@@ -382,7 +362,7 @@ __mktime_internal (struct tm *tp,
/* The other values need not be in range:
the remaining code handles minor overflows correctly,
- assuming int and time_t arithmetic wraps around.
+ assuming int and __time64_t arithmetic wraps around.
Major overflows are caught at the end. */
/* Calculate day of year from year, month, and day of month.
@@ -393,7 +373,7 @@ __mktime_internal (struct tm *tp,
long_int lmday = mday;
long_int yday = mon_yday + lmday;
- time_t guessed_offset = *offset;
+ __time64_t guessed_offset = *offset;
int sec_requested = sec;
@@ -413,12 +393,12 @@ __mktime_internal (struct tm *tp,
t0 = ydhms_diff (year, yday, hour, min, sec,
EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
- if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
+ if (TIME64_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
{
- /* time_t isn't large enough to rule out overflows, so check
- for major overflows. A gross check suffices, since if t0
- has overflowed, it is off by a multiple of TIME_T_MAX -
- TIME_T_MIN + 1. So ignore any component of the difference
+ /* __time64_t is large enough to rule out overflows, but check
+ for major overflows anyway. A gross check suffices, since if t0
+ has overflowed, it is off by a multiple of TIME64_T_MAX -
+ TIME64_T_MIN + 1. So ignore any component of the difference
that is bounded by a small value. */
/* Approximate log base 2 of the number of time units per
@@ -447,20 +427,21 @@ __mktime_internal (struct tm *tp,
int diff = approx_biennia - approx_requested_biennia;
int approx_abs_diff = diff < 0 ? -1 - diff : diff;
- /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously
+ /* IRIX 4.0.5 cc miscalculates 32-bit TIME_T_MIN / 3: it erroneously
gives a positive value of 715827882. Setting a variable
- first then doing math on it seems to work.
- (ghazi@caip.rutgers.edu) */
- time_t time_t_max = TIME_T_MAX;
- time_t time_t_min = TIME_T_MIN;
- time_t overflow_threshold =
+ first then doing math on it seems to work (ghazi@caip.rutgers.edu).
+ Do this with 64-bit TIME64_T_MIN too just in case.
+ */
+ __time64_t time_t_max = TIME64_T_MAX;
+ __time64_t time_t_min = TIME64_T_MIN;
+ __time64_t overflow_threshold =
(time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
if (overflow_threshold < approx_abs_diff)
{
/* Overflow occurred. Try repairing it; this might work if
the time zone offset is enough to undo the overflow. */
- time_t repaired_t0 = -1 - t0;
+ __time64_t repaired_t0 = -1 - t0;
approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
diff = approx_biennia - approx_requested_biennia;
approx_abs_diff = diff < 0 ? -1 - diff : diff;
@@ -533,7 +514,7 @@ __mktime_internal (struct tm *tp,
for (direction = -1; direction <= 1; direction += 2)
if (time_t_int_add_ok (t, delta * direction))
{
- time_t ot = t + delta * direction;
+ __time64_t ot = t + delta * direction;
struct tm otm;
ranged_convert (convert, &ot, &otm);
if (! isdst_differ (isdst, otm.tm_isdst))
@@ -575,11 +556,11 @@ __mktime_internal (struct tm *tp,
offset in seconds. 'int' should be good enough for GNU code. We
can't fix this unilaterally though, as other modules invoke
__mktime_internal. */
-static time_t localtime_offset;
+static __time64_t localtime_offset;
-/* Convert *TP to a time_t value. */
-time_t
-mktime (struct tm *tp)
+/* Convert *TP to a __time64_t value. */
+__time64_t
+__mktime64 (struct tm *tp)
{
#ifdef _LIBC
/* POSIX.1 8.1.1 requires that whenever mktime() is called, the
@@ -588,7 +569,22 @@ mktime (struct tm *tp)
__tzset ();
#endif
- return __mktime_internal (tp, __localtime_r, &localtime_offset);
+ return __mktime_internal (tp, __localtime64_r, &localtime_offset);
+}
+
+#ifdef weak_alias
+weak_alias (__mktime64, __timelocal64)
+#endif
+
+/* The 32-bit-time wrapper. */
+time_t
+mktime (struct tm *tp)
+{
+ __time64_t t64 = __mktime64 (tp);
+ if (fits_in_time_t (t64))
+ return (time_t) t64;
+ __set_errno (EOVERFLOW);
+ return -1;
}
#ifdef weak_alias
diff --git a/time/timegm.c b/time/timegm.c
index fb720e2d7d..b36fef2320 100644
--- a/time/timegm.c
+++ b/time/timegm.c
@@ -33,15 +33,27 @@
# include <time_r.h>
# undef __gmtime_r
# define __gmtime_r gmtime_r
-time_t __mktime_internal (struct tm *,
- struct tm * (*) (time_t const *, struct tm *),
- time_t *);
+time64_t __mktime_internal (struct tm *,
+ struct tm * (*) (__time64_t const *, struct tm *),
+ __time64_t *);
#endif
+# include <limits.h>
+# include <errno.h>
+
+__time64_t
+__timegm64 (struct tm *tmp)
+{
+ static __time64_t gmtime_offset;
+ tmp->tm_isdst = 0;
+ return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset);
+}
time_t
timegm (struct tm *tmp)
{
- static time_t gmtime_offset;
- tmp->tm_isdst = 0;
- return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
+ __time64_t t64 = __timegm64(tmp);
+ if (fits_in_time_t (t64))
+ return (time_t) t64;
+ __set_errno (EOVERFLOW);
+ return -1;
}
--
2.17.1