+ /* 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
+ that is bounded by a small value. */
+
+ /* Approximate log base 2 of the number of time units per
+ biennium. A biennium is 2 years; use this unit instead of
+ years to avoid integer overflow. For example, 2 average
+ Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
+ which is 63113904 seconds, and rint (log2 (63113904)) is
+ 26. */
+ int ALOG2_SECONDS_PER_BIENNIUM = 26;
+ int ALOG2_MINUTES_PER_BIENNIUM = 20;
+ int ALOG2_HOURS_PER_BIENNIUM = 14;
+ int ALOG2_DAYS_PER_BIENNIUM = 10;
+ int LOG2_YEARS_PER_BIENNIUM = 1;
+
+ int approx_requested_biennia =
+ ((year_requested >> LOG2_YEARS_PER_BIENNIUM)
+ - ((EPOCH_YEAR - TM_YEAR_BASE) >> LOG2_YEARS_PER_BIENNIUM)
+ + (mday >> ALOG2_DAYS_PER_BIENNIUM)
+ + (hour >> ALOG2_HOURS_PER_BIENNIUM)
+ + (min >> ALOG2_MINUTES_PER_BIENNIUM)
+ + (LEAP_SECONDS_POSSIBLE ? 0 : sec >> ALOG2_SECONDS_PER_BIENNIUM));
+
+ int approx_biennia = t0 >> ALOG2_SECONDS_PER_BIENNIUM;
+ int diff = approx_biennia - approx_requested_biennia;
+ int abs_diff = diff < 0 ? - diff : diff;
+
+ /* IRIX 4.0.5 cc miscaculates 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 =
+ (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
+
+ if (overflow_threshold < 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;
+ approx_biennia = repaired_t0 >> ALOG2_SECONDS_PER_BIENNIUM;
+ diff = approx_biennia - approx_requested_biennia;
+ abs_diff = diff < 0 ? - diff : diff;
+ if (overflow_threshold < abs_diff)
+ return -1;
+ guessed_offset += repaired_t0 - t0;
+ t0 = repaired_t0;
+ }
+ }
+
+ /* Repeatedly use the error to improve the guess. */
+
+ for (t = t1 = t2 = t0, dst2 = 0;
+ (gt = guess_time_tm (year, yday, hour, min, sec, &t,
+ ranged_convert (convert, &t, &tm)),
+ t != gt);
+ t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
+ if (t == t1 && t != t2
+ && (tm.tm_isdst < 0
+ || (isdst < 0
+ ? dst2 <= (tm.tm_isdst != 0)
+ : (isdst != 0) != (tm.tm_isdst != 0))))
+ /* We can't possibly find a match, as we are oscillating
+ between two values. The requested time probably falls
+ within a spring-forward gap of size GT - T. Follow the common
+ practice in this case, which is to return a time that is GT - T
+ away from the requested time, preferring a time whose
+ tm_isdst differs from the requested value. (If no tm_isdst
+ was requested and only one of the two values has a nonzero
+ tm_isdst, prefer that value.) In practice, this is more
+ useful than returning -1. */
+ goto offset_found;
+ else if (--remaining_probes == 0)
+ return -1;
+
+ /* We have a match. Check whether tm.tm_isdst has the requested
+ value, if any. */
+ if (isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
+ {
+ /* tm.tm_isdst has the wrong value. Look for a neighboring
+ time with the right value, and use its UTC offset.
+
+ Heuristic: probe the adjacent timestamps in both directions,
+ looking for the desired isdst. This should work for all real
+ time zone histories in the tz database. */
+
+ /* 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
+ shortest period of non-DST surrounded by DST is 694800
+ seconds (Africa/Tunis starting 1943-04-17 01:00). Use the
+ minimum of these two values, so we don't miss these short
+ periods when probing. */
+ int stride = 601200;
+
+ /* The longest period of DST in tzdata2003a is 536454000 seconds
+ (e.g., America/Jujuy starting 1946-10-01 01:00). The longest
+ period of non-DST is much longer, but it makes no real sense
+ to search for more than a year of non-DST, so use the DST
+ max. */
+ int duration_max = 536454000;
+
+ /* 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;
+
+ int delta, direction;
+
+ for (delta = stride; delta < delta_bound; delta += stride)
+ for (direction = -1; direction <= 1; direction += 2)
+ {
+ time_t ot = t + delta * direction;
+ if ((ot < t) == (direction < 0))
+ {
+ struct tm otm;
+ ranged_convert (convert, &ot, &otm);
+ if (otm.tm_isdst == isdst)
+ {
+ /* We found the desired tm_isdst.
+ Extrapolate back to the desired time. */
+ t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);
+ ranged_convert (convert, &t, &tm);
+ goto offset_found;
+ }
+ }
+ }