@@ -71,9 +71,8 @@ typedef int mktime_offset_t;
#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, given that a struct tm * maps to a time_t. If
+ LOCAL, the mapping is performed by localtime_r, otherwise by gmtime_r.
+ Record next guess for localtime-gmtime offset in *OFFSET. */
+extern __time64_t __mktime_internal (struct tm *tp, bool local,
mktime_offset_t *offset) attribute_hidden;
@@ -46,11 +46,11 @@
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdckdint.h>
#include <stdlib.h>
#include <string.h>
#include <intprops.h>
-#include <verify.h>
#ifndef NEED_MKTIME_INTERNAL
# define NEED_MKTIME_INTERNAL 0
@@ -118,12 +118,12 @@ my_tzset (void)
__time64_t values that mktime can generate even on platforms where
__time64_t is wider than the int components of struct tm. */
-#if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60
+# if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60
typedef long int long_int;
-#else
+# else
typedef long long int long_int;
-#endif
-verify (INT_MAX <= TYPE_MAXIMUM (long_int) / 4 / 366 / 24 / 60 / 60);
+# endif
+static_assert (INT_MAX <= TYPE_MAXIMUM (long_int) / 4 / 366 / 24 / 60 / 60);
/* Shift A right by B bits portably, by dividing A by 2**B and
truncating towards minus infinity. B should be in the range 0 <= B
@@ -154,9 +154,9 @@ static long_int const mktime_max
= (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (__time64_t)
? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (__time64_t));
-#define EPOCH_YEAR 1970
-#define TM_YEAR_BASE 1900
-verify (TM_YEAR_BASE % 100 == 0);
+# define EPOCH_YEAR 1970
+# define TM_YEAR_BASE 1900
+static_assert (TM_YEAR_BASE % 100 == 0);
/* Is YEAR + TM_YEAR_BASE a leap year? */
static bool
@@ -171,9 +171,9 @@ leapyear (long_int year)
}
/* How many days come before each month (0-12). */
-#ifndef _LIBC
+# ifndef _LIBC
static
-#endif
+# endif
const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
@@ -205,7 +205,7 @@ static long_int
ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
int year0, int yday0, int hour0, int min0, int sec0)
{
- verify (-1 / 2 == 0);
+ static_assert (-1 / 2 == 0);
/* Compute intervening leap days correctly even if year is negative.
Take care to avoid integer overflow here. */
@@ -250,29 +250,33 @@ 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. */
+/* Convert T to a struct tm value in *TM. Use localtime64_r if LOCAL,
+ otherwise gmtime64_r. 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)
+convert_time (long_int t, bool local, struct tm *tm)
{
__time64_t x = t;
- return convert (&x, tm);
+ if (local)
+ return __localtime64_r (&x, tm);
+ else
+ return __gmtime64_r (&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 __time64_t and long_int.
- Return TP on success, NULL (setting errno) on failure. */
+/* Call it __tzconvert to sync with other parts of glibc. */
+#define __tz_convert convert_time
+
+/* Convert *T to a broken down time in *TP (as if by localtime if
+ LOCAL, otherwise as if by gmtime). 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 (bool local, long_int *t, 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, local, tp);
if (r)
{
*t = t1;
@@ -293,7 +297,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, local, tp))
ok = mid, oktm = *tp;
else if (errno != EOVERFLOW)
return NULL;
@@ -309,36 +313,45 @@ 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.
- Use *OFFSET to keep track of a guess at the offset of the result,
+/* Convert *TP to a __time64_t value. If LOCAL, the reverse mapping
+ is performed as if localtime, otherwise as if by gmtime. 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 successful, set *TP to the canonicalized struct tm;
+ If *OFFSET's guess is correct, only one reverse mapping 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 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 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. */
+#ifndef _LIBC
+ /* Gnulib mktime doesn't lock the tz state, so it may need to probe
+ more often if some other thread changes local time while
+ __mktime_internal is probing. Double the number of probes; this
+ should suffice for practical cases that are at all likely. */
+ remaining_probes *= 2;
+#endif
+
+ /* Time requested. Copy it in case gmtime/localtime modify *TP;
+ this can occur if TP is localtime's returned value and CONVERT is
+ localtime. */
int sec = tp->tm_sec;
int min = tp->tm_min;
int hour = tp->tm_hour;
int mday = tp->tm_mday;
int mon = tp->tm_mon;
int year_requested = tp->tm_year;
- int isdst = tp->tm_isdst;
+
+ /* If the timezone never observes DST, ignore any tm_isdst request. */
+ int isdst = local && __daylight ? tp->tm_isdst : 0;
/* 1 if the previous probe was DST. */
int dst2 = 0;
@@ -379,7 +392,7 @@ __mktime_internal (struct tm *tp,
/* Invert CONVERT by probing. First assume the same offset as last
time. */
- INT_SUBTRACT_WRAPV (0, off, &negative_offset_guess);
+ ckd_sub (&negative_offset_guess, 0, off);
long_int t0 = ydhms_diff (year, yday, hour, min, sec,
EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0,
negative_offset_guess);
@@ -389,7 +402,7 @@ __mktime_internal (struct tm *tp,
while (true)
{
- if (! ranged_convert (convert, &t, &tm))
+ if (! ranged_convert (local, &t, &tm))
return -1;
long_int dt = tm_diff (year, yday, hour, min, sec, &tm);
if (dt == 0)
@@ -465,10 +478,10 @@ __mktime_internal (struct tm *tp,
for (direction = -1; direction <= 1; direction += 2)
{
long_int ot;
- if (! INT_ADD_WRAPV (t, delta * direction, &ot))
+ if (! ckd_add (&ot, t, delta * direction))
{
struct tm otm;
- if (! ranged_convert (convert, &ot, &otm))
+ if (! ranged_convert (local, &ot, &otm))
return -1;
if (! isdst_differ (isdst, otm.tm_isdst))
{
@@ -478,7 +491,7 @@ __mktime_internal (struct tm *tp,
&otm);
if (mktime_min <= gt && gt <= mktime_max)
{
- if (convert_time (convert, gt, &tm))
+ if (__tz_convert (gt, local, &tm))
{
t = gt;
goto offset_found;
@@ -492,7 +505,7 @@ __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, local, &tm))
goto offset_found;
__set_errno (EOVERFLOW);
@@ -503,8 +516,8 @@ __mktime_internal (struct tm *tp,
/* Set *OFFSET to the low-order bits of T - T0 - NEGATIVE_OFFSET_GUESS.
This is just a heuristic to speed up the next mktime call, and
correctness is unaffected if integer overflow occurs here. */
- INT_SUBTRACT_WRAPV (t, t0, offset);
- INT_SUBTRACT_WRAPV (*offset, negative_offset_guess, offset);
+ ckd_sub (offset, t, t0);
+ ckd_sub (offset, *offset, negative_offset_guess);
if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
{
@@ -513,13 +526,13 @@ __mktime_internal (struct tm *tp,
long_int sec_adjustment = sec == 0 && tm.tm_sec == 60;
sec_adjustment -= sec;
sec_adjustment += sec_requested;
- if (INT_ADD_WRAPV (t, sec_adjustment, &t)
+ if (ckd_add (&t, t, sec_adjustment)
|| ! (mktime_min <= t && t <= mktime_max))
{
__set_errno (EOVERFLOW);
return -1;
}
- if (! convert_time (convert, t, &tm))
+ if (! __tz_convert (t, local, &tm))
return -1;
}
@@ -535,14 +548,13 @@ __mktime_internal (struct tm *tp,
__time64_t
__mktime64 (struct tm *tp)
{
- /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
- time zone abbreviations contained in the external variable 'tzname' shall
- be set as if the tzset() function had been called. */
+ /* POSIX.1 requires mktime to set external variables like 'tzname'
+ as though tzset had been called. */
__tzset ();
# 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);
@@ -30,8 +30,7 @@ __time64_t
__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