2025-01-04 Paul Eggert <eggert@cs.ucla.edu>
+ mktime: improve tm_isdst heuristic
+ * lib/mktime.c (__mktime_internal): When tm_isdst disagrees with
+ what was requested, search at most a year (+ stride) from the
+ requested time for a matching tm_isdst, and ignore the request if
+ the search fails. This is more likely to match user expectations
+ in timezones like Asia/Kolkata.
+ Problem reported by Florian Weimer in:
+ https://sourceware.org/pipermail/libc-alpha/2025-January/163342.html
+
mktime: support glibc locking
This is part of my attempt to merge glibc and gnulib mktime.c.
It should not affect Gnulib-using code.
int mday = tp->tm_mday;
int mon = tp->tm_mon;
int year_requested = tp->tm_year;
-
- /* Ignore any tm_isdst request for timegm. */
- int isdst = local ? tp->tm_isdst : 0;
+ int isdst = tp->tm_isdst;
/* True if the previous probe was DST. */
bool dst2 = false;
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
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)
{
}
}
- /* No unusual DST offset was found nearby. Assume one-hour DST. */
- t += 60 * 60 * dst_difference;
- if (mktime_min <= t && t <= mktime_max && __tz_convert (t, local, &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: