>From edeaac0060e9fbe4e1aa094e4ad6b58398a75f9d Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 31 Aug 2018 23:45:31 -0700 Subject: [PATCH 3/3] Add support for __time64_t to mktime, timegm * include/time.h, time/mktime.c, time/timegm.c: Change externally-visible names to their __xxx64yyy version. * include/time.h (fits_in_time_t): New static function. * time/mktime.c (mktime) [_LIBC]: New wrapper function. * time/timegm.c (timegm) [_LIBC]: New wrapper function. --- ChangeLog | 7 ++++++ include/time.h | 20 ++++++++++----- time/mktime.c | 66 +++++++++++++++++++++++++++++++------------------- time/timegm.c | 22 ++++++++++++++--- 4 files changed, 81 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9f31582e19..3b20dafde1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2018-08-31 Paul Eggert + Add support for __time64_t to mktime, timegm + * include/time.h, time/mktime.c, time/timegm.c: + Change externally-visible names to their __xxx64yyy version. + * include/time.h (fits_in_time_t): New static function. + * time/mktime.c (mktime) [_LIBC]: New wrapper function. + * time/timegm.c (timegm) [_LIBC]: New wrapper function. + Fix mktime localtime offset confusion * include/time.h (__mktime_internal): The localtime offset is now of type long int instead of time_t. This is the longstanding type diff --git a/include/time.h b/include/time.h index 114d0f7da6..05a7372285 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. Record next guess for localtime-gmtime offset in *OFFSET. */ -extern time_t __mktime_internal (struct tm *__tp, - struct tm *(*__func) (const time_t *, - struct tm *), - long int *__offset) attribute_hidden; +extern __time64_t __mktime_internal (struct tm *__tp, + struct tm *(*__func) (const __time64_t *, + struct tm *), + long int *__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 @@ -131,5 +131,13 @@ extern double __difftime (time_t time1, time_t time0); actual clock ID. */ #define CLOCK_IDFIELD_SIZE 3 +/* Check whether a time64_t value fits in a time_t. */ +static inline bool +fits_in_time_t (__time64_t t64) +{ + time_t t = t64; + return t == t64; +} + #endif #endif diff --git a/time/mktime.c b/time/mktime.c index a307671feb..fcef7ee3ca 100644 --- a/time/mktime.c +++ b/time/mktime.c @@ -51,6 +51,7 @@ #include +#include #include #include #include @@ -129,11 +130,11 @@ my_tzset (void) to be subtracted from each other, and sometimes with an offset added to them, without worrying about overflow. - Much of the code uses long_int to represent time_t values, to - lessen the hassle of dealing with platforms where time_t is - unsigned, and because long_int should suffice to represent all - time_t values that mktime can generate even on platforms where - time_t is excessively wide. */ + Much of the code uses long_int to represent __time64_t values, to + lessen the hassle of dealing with Gnulib-using platforms where + __time64_t is time_t and time_t is unsigned, and because long_int + should suffice to represent all __time64_t values that mktime can + generate even on platforms where __time64_t is excessively wide. */ #if INT_MAX <= LONG_MAX / 3 / 366 / 24 / 60 / 60 typedef long int long_int; @@ -161,16 +162,17 @@ shr (long_int a, int b) : a / (one << b) - (a % (one << b) < 0)); } -/* Bounds for the intersection of time_t and long_int. */ +/* Bounds for the intersection of __time64_t and long_int. */ static long_int const mktime_min - = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int)) - ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t)); + = ((TYPE_SIGNED (__time64_t) + && TYPE_MINIMUM (__time64_t) < TYPE_MINIMUM (long_int)) + ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (__time64_t)); static long_int const mktime_max - = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t) - ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t)); + = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (__time64_t) + ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (__time64_t)); -verify (TYPE_IS_INTEGER (time_t)); +verify (TYPE_IS_INTEGER (__time64_t)); #define EPOCH_YEAR 1970 #define TM_YEAR_BASE 1900 @@ -251,11 +253,11 @@ long_int_avg (long_int a, long_int b) return shr (a, 1) + shr (b, 1) + ((a | b) & 1); } -/* 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. Although T and the returned value are of type long_int, - they represent time_t values and must be in time_t range. + they represent __time64_t values and must be in __time64_t range. If TP is null, return a value not equal to T; this avoids false matches. YEAR and YDAY must not be so large that multiplying them by three times the number of seconds in a year (or day, respectively) would overflow long_int. @@ -286,22 +288,22 @@ guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, } /* Use CONVERT to convert T to a struct tm value in *TM. T must be in - range for time_t. Return TM if successful, NULL if T is out of + range for __time64_t. Return TM if successful, NULL if T is out of range for CONVERT. */ static struct tm * -convert_time (struct tm *(*convert) (const time_t *, struct tm *), +convert_time (struct tm *(*convert) (const __time64_t *, struct tm *), long_int t, struct tm *tm) { - time_t x = t; + __time64_t x = t; return convert (&x, tm); } /* 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. - A value is in range if it fits in both time_t and long_int. */ + A value is in range if it fits in both __time64_t and long_int. */ static struct tm * -ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), +ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *), long_int *t, struct tm *tp) { struct tm *r; @@ -343,15 +345,15 @@ 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 *), + struct tm *(*convert) (const __time64_t *, struct tm *), mktime_offset_t *offset) { long_int t, gt, t0, t1, t2, dt; @@ -521,9 +523,9 @@ __mktime_internal (struct tm *tp, #if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS -/* 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) { /* POSIX.1 8.1.1 requires that whenever mktime() is called, the time zone names contained in the external variable 'tzname' shall @@ -532,7 +534,7 @@ mktime (struct tm *tp) # if defined __LIBC || NEED_MKTIME_WORKING static mktime_offset_t localtime_offset; - return __mktime_internal (tp, __localtime_r, &localtime_offset); + return __mktime_internal (tp, __localtime64_r, &localtime_offset); # else # undef mktime return mktime (tp); @@ -540,8 +542,22 @@ mktime (struct tm *tp) } #endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */ +#ifdef _LIBC +/* The 32-bit-time wrapper. */ +time_t +mktime (struct tm *tp) +{ + __time64_t t64 = __mktime64 (tp); + if (fits_in_time_t (t64)) + return t64; + __set_errno (EOVERFLOW); + return -1; +} +#endif + #ifdef weak_alias weak_alias (mktime, timelocal) +weak_alias (__mktime64, __timelocal64) #endif #ifdef _LIBC diff --git a/time/timegm.c b/time/timegm.c index 71276bbe0b..104f206086 100644 --- a/time/timegm.c +++ b/time/timegm.c @@ -23,16 +23,32 @@ #include +#include + #ifdef _LIBC typedef long int mktime_offset_t; #else # include "mktime-internal.h" #endif +__time64_t +__timegm64 (struct tm *tmp) +{ + static long int gmtime_offset; + tmp->tm_isdst = 0; + return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset); +} + +#ifdef _LIBC + time_t timegm (struct tm *tmp) { - static mktime_offset_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 t64; + __set_errno (EOVERFLOW); + return -1; } + +#endif -- 2.17.1