diff -Naur tz-latest/localtime.c time_successive/localtime.c --- tz-latest/localtime.c 2017-05-29 13:46:37.818104937 +0200 +++ time_successive/localtime.c 2017-05-29 14:00:40.001286702 +0200 @@ -11,6 +11,9 @@ /*LINTLIBRARY*/ #define LOCALTIME_IMPLEMENTATION +// uncomment to use the time_successive() approach, for better peformance of mktime(). +//#define USE_TIME_SUCCESSIVE + #include "private.h" #include "tzfile.h" @@ -159,6 +162,24 @@ static bool typesequiv(struct state const *, int, int); static bool tzparse(char const *, struct state *, bool); +#ifdef USE_TIME_SUCCESSIVE + +static int time_successive( + struct tm * (* const funcp)( + struct state const *, + time_t const *, + int_fast32_t, + struct tm * + ), + struct state const * sp, + time_t * tp, + int_fast32_t offset, + struct tm * tmp, + int * compensate +); + +#endif // USE_TIME_SUCCESSIVE + #ifdef ALL_STATE static struct state * lclptr; static struct state * gmtptr; @@ -1846,17 +1867,30 @@ bool *okayp, bool do_norm_secs) { +#ifndef USE_TIME_SUCCESSIVE register int dir; +#endif // USE_TIME_SUCCESSIVE + register int i, j; register int saved_seconds; register int_fast32_t li; + +#ifndef USE_TIME_SUCCESSIVE register time_t lo; register time_t hi; +#endif // USE_TIME_SUCCESSIVE + int_fast32_t y; time_t newt; time_t t; struct tm yourtm, mytm; +#ifdef USE_TIME_SUCCESSIVE + int compensate; + int ignore_isdst = 0; + int different; +#endif // USE_TIME_SUCCESSIVE + *okayp = false; yourtm = *tmp; if (do_norm_secs) { @@ -1924,6 +1958,38 @@ saved_seconds = yourtm.tm_sec; yourtm.tm_sec = 0; } + +#ifdef USE_TIME_SUCCESSIVE + + if (time_successive(funcp, sp, &t, offset, &yourtm, &compensate)) + return WRONG; + funcp(sp, &t, offset, &mytm); + + /* verification, should never fail (if compensate: this is done below) */ + different = tmcomp(&mytm, &yourtm); + if (yourtm.tm_isdst == -1 && different) + { + yourtm.tm_isdst = 1 - mytm.tm_isdst; + compensate = 1; + ignore_isdst = 1; + } + if (!compensate && different) + { + /* + * it is allowed to the user to override DST with tm_isdst flag + * lateron the correct structure will be returned in time1() + * no logmessage over here + log_mesg(__SRC__, __LINE__, "utc", TO_SET, + "successive algorithm failed, offset=%d, t=%d, yourtm=%s", + offset, t, asctime(&yourtm)); + */ + return WRONG; + } + if (yourtm.tm_isdst >= 0 && compensate) + { + +#else // USE_TIME_SUCCESSIVE + /* ** Do a binary search (this works whatever time_t's type is). */ @@ -1993,6 +2059,9 @@ #endif if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; + +#endif // USE_TIME_SUCCESSIVE + /* ** Right time, wrong type. ** Hunt for right time, right type. @@ -2002,10 +2071,18 @@ if (sp == NULL) return WRONG; for (i = sp->typecnt - 1; i >= 0; --i) { + +#ifndef USE_TIME_SUCCESSIVE if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; +#endif // USE_TIME_SUCCESSIVE + for (j = sp->typecnt - 1; j >= 0; --j) { +#ifdef USE_TIME_SUCCESSIVE + if (sp->ttis[j].tt_isdst == sp->ttis[i].tt_isdst) +#else // USE_TIME_SUCCESSIVE if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) +#endif // USE_TIME_SUCCESSIVE continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; @@ -2014,6 +2091,16 @@ if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) + +#ifdef USE_TIME_SUCCESSIVE + /* if we have done a compensation for a + * tm_isdst = -1, we can ignore a different + * yourtm.is_dst, since that was a wild + * guess above (where the ignore flag was set). + */ + if (!ignore_isdst) +#endif // USE_TIME_SUCCESSIVE + continue; /* ** We have a match. @@ -2325,3 +2412,149 @@ } #endif + +#ifdef USE_TIME_SUCCESSIVE + +/* +* This is another implementation of the "successive approximation" +* algorithm that can be found in Perl's timelocal.pl (see www.perl.org). +* +* A binary search algorithm takes 32 calls to gmtime, which is rather +* expensive. In contrast this algoritm takes just only one such call. +* +* However, the implementation of time2() makes 2 additional calls to +* the <>, giving a call rate of 3. +* +* The gmtime calls mentioned above are in fact calls of the funcp argument, +* which points to localsub or gmtsub. +* +* January 2011 +* Jan Koen Annot +*/ + +static const int sCumulativeMonthLength[] = { + 0, + 31, + 59, + 90, + 120, + 151, + 181, + 212, + 243, + 273, + 304, + 334 +}; + +static int time_successive( + struct tm * (* const funcp)( + struct state const *, + time_t const *, + int_fast32_t, + struct tm * + ), + struct state const * sp, + time_t * tp, + int_fast32_t offset, + struct tm * tmp, + int * compensate +) +{ + time_t guess; + time_t guess_offset; + struct tm g; + + /* As an initial guess for the correct time_t value, + take the utc value of 00:00:00 at the 3rd day of the specified month and year. + When calculating that value back to a struct tm, + at least the year and the month will be correct. + */ + time_t lYear; + time_t lMonth; + time_t lDays; + + lYear = tmp->tm_year; + lYear += TM_YEAR_BASE; /* change years from 1900 to years from 0 */ + + lMonth = tmp->tm_mon; + + lDays = lYear * DAYSPERNYEAR; + + if (0 < lYear) { + /* adapt for leap years until the previous year */ + time_t lPreviousYear = lYear - 1; + + lDays += 1; /* year 0 is a leap year */ + lDays += lPreviousYear / 4; /* every 4 year add a leap year */ + lDays -= lPreviousYear / 100; /* every 100 year subtract a leap year */ + lDays += lPreviousYear / 400; /* every 400 year add a leap year */ + } + + lDays += sCumulativeMonthLength[lMonth]; + + if ( 2 <= lMonth + && isleap(lYear) + ) { + /* in leap years, add a leap day from March onwards */ + lDays += 1; + } + + /* change days from January 1, 0 to days from January 1, 1970 */ + lDays -= EPOCH_YEAR * DAYSPERNYEAR + + 1 + + (EPOCH_YEAR - 1) / 4 + - (EPOCH_YEAR - 1) / 100 + + (EPOCH_YEAR - 1) / 400 + ; + + lDays += 3; /* take the 3rd day of the month */ + + guess = lDays; + + if (guess <= time_t_max / SECSPERDAY) { + guess *= SECSPERDAY; + } else { + *tp = -1; + return WRONG; + } + + funcp(sp, &guess, offset, &g); + + if ( g.tm_year != tmp->tm_year + || g.tm_mon != tmp->tm_mon + ) { + *tp = -1; + return WRONG; + } + + guess_offset = ((time_t)tmp->tm_sec - (time_t)g.tm_sec) + + ((time_t)tmp->tm_min - (time_t)g.tm_min) * SECSPERMIN + + ((time_t)tmp->tm_hour - (time_t)g.tm_hour) * SECSPERHOUR + + ((time_t)tmp->tm_mday - (time_t)g.tm_mday) * SECSPERDAY; + + if ( guess_offset < 0 + ? time_t_min - guess_offset <= guess /* avoid time_t underflow */ + : guess <= time_t_max - guess_offset /* avoid time_t overflow */ + ) { + guess += guess_offset; + } else { + *tp = -1; + return WRONG; + } + + /* it could happen to be wrong because dailight savings state changed + * in between the fixed calculation above. This is compensated after + * returning from this func. + */ + *compensate = (tmp->tm_isdst != g.tm_isdst); + if (tmp->tm_isdst < 0) { + *compensate = 0; + } + + *tp = guess; + return 0; +} + +#endif // USE_TIME_SUCCESSIVE +