From b0afb8fe0ff1d87165af9df61501197a06240dda Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:13:40 +0100 Subject: Sync with Gnulib stable-202507 code (a8ac9f9ce5) --- gl/mktime.c | 163 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 84 insertions(+), 79 deletions(-) (limited to 'gl/mktime.c') diff --git a/gl/mktime.c b/gl/mktime.c index c704f415..4218fca6 100644 --- a/gl/mktime.c +++ b/gl/mktime.c @@ -1,5 +1,5 @@ /* Convert a 'struct tm' to a time_t value. - Copyright (C) 1993-2024 Free Software Foundation, Inc. + Copyright (C) 1993-2025 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Paul Eggert . @@ -51,7 +51,6 @@ #include #include -#include #ifndef NEED_MKTIME_INTERNAL # define NEED_MKTIME_INTERNAL 0 @@ -63,6 +62,9 @@ # define NEED_MKTIME_WORKING 0 #endif +#ifdef _LIBC +# include +#endif #include "mktime-internal.h" #if !defined _LIBC && (NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS) @@ -99,8 +101,8 @@ my_tzset (void) tzset (); # endif } -# undef __tzset -# define __tzset() my_tzset () +# undef tzset +# define tzset() my_tzset () #endif #if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL @@ -119,12 +121,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 @@ -155,9 +157,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 @@ -172,9 +174,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. */ @@ -206,7 +208,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. */ @@ -251,29 +253,34 @@ 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. */ +#ifndef _LIBC +/* 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); } +# define __tz_convert convert_time +#endif -/* 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. */ +/* 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; @@ -294,7 +301,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; @@ -310,29 +317,38 @@ 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. */ + This function is external because it is used also by timegm.c. + + If _LIBC, the caller must lock __tzset_lock. */ __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; @@ -341,8 +357,8 @@ __mktime_internal (struct tm *tp, int year_requested = tp->tm_year; int isdst = tp->tm_isdst; - /* 1 if the previous probe was DST. */ - int dst2 = 0; + /* True if the previous probe was DST. */ + bool dst2 = false; /* Ensure that mon is in range, and set year accordingly. */ int mon_remainder = mon % 12; @@ -390,7 +406,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) @@ -431,13 +447,10 @@ __mktime_internal (struct tm *tp, Heuristic: probe the adjacent timestamps in both directions, looking for the desired isdst. If none is found within a - reasonable duration bound, assume a one-hour DST difference. + reasonable duration bound, ignore the disagreement. This should work for all real time zone histories in the tz database. */ - /* +1 if we wanted standard time but got DST, -1 if the reverse. */ - int dst_difference = (isdst == 0) - (tm.tm_isdst == 0); - /* Distance between probes when looking for a DST boundary. In tzdata2003a, the shortest period of DST is 601200 seconds (e.g., America/Recife starting 2000-10-08 01:00), and the @@ -447,21 +460,17 @@ __mktime_internal (struct tm *tp, periods when probing. */ int stride = 601200; - /* In TZDB 2021e, the longest period of DST (or of non-DST), in - which the DST (or adjacent DST) difference is not one hour, - is 457243209 seconds: e.g., America/Cambridge_Bay with leap - seconds, starting 1965-10-31 00:00 in a switch from - double-daylight time (-05) to standard time (-07), and - continuing to 1980-04-27 02:00 in a switch from standard time - (-07) to daylight time (-06). */ - int duration_max = 457243209; - - /* Search in both directions, so the maximum distance is half - the duration; add the stride to avoid off-by-1 problems. */ - int delta_bound = duration_max / 2 + stride; + /* Do not probe too far away from the requested time, + by striding until at least a year has passed, but then giving up. + This helps avoid unexpected results in (for example) Asia/Kolkata, + for which today's users expect to see no DST even though it + did observe DST long ago. */ + int year_seconds_bound = 366 * 24 * 60 * 60 + 1; + int delta_bound = year_seconds_bound + stride; int delta, direction; + /* Search in both directions, closest first. */ for (delta = stride; delta < delta_bound; delta += stride) for (direction = -1; direction <= 1; direction += 2) { @@ -469,7 +478,7 @@ __mktime_internal (struct tm *tp, 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)) { @@ -479,7 +488,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; @@ -491,13 +500,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)) - goto offset_found; - - __set_errno (EOVERFLOW); - return -1; + /* No probe with the requested tm_isdst was found nearby. + Ignore the requested tm_isdst. */ } offset_found: @@ -520,7 +524,7 @@ __mktime_internal (struct tm *tp, __set_errno (EOVERFLOW); return -1; } - if (! convert_time (convert, t, &tm)) + if (! __tz_convert (t, local, &tm)) return -1; } @@ -536,18 +540,19 @@ __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 names contained in the external variable 'tzname' shall - be set as if the tzset() function had been called. */ - __tzset (); + __libc_lock_lock (__tzset_lock); + __tzset_unlocked (); # if defined _LIBC || NEED_MKTIME_WORKING static mktime_offset_t localtime_offset; - return __mktime_internal (tp, __localtime64_r, &localtime_offset); + __time64_t result = __mktime_internal (tp, true, &localtime_offset); # else # undef mktime - return mktime (tp); + __time64_t result = mktime (tp); # endif + + __libc_lock_unlock (__tzset_lock); + return result; } #endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */ -- cgit v1.2.3-74-g34f1