From 48ddce5e9a063d89689ffe4be1680767186e13ee Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 18 Sep 2021 09:33:21 +0100 Subject: win32: limited version of timegm(3) timegm(3) from musl checks that the calculated time_t value can be broken out into a struct tm without overflow. The limiting factor is that tm_year is an int. Our only use of timegm(3) is followed by a call to localtime(3). The Microsoft version of this only works for time_t values between 0 and INT_MAX (32-bit) or 0 and 32535215999 (64-bit). Enforce these limits in timegm(3), thus avoiding the use of musl's __secs_to_tm() and saving 624 bytes. --- win32/timegm.c | 97 ++++++++-------------------------------------------------- 1 file changed, 13 insertions(+), 84 deletions(-) diff --git a/win32/timegm.c b/win32/timegm.c index abba2579f..8bfa041c4 100644 --- a/win32/timegm.c +++ b/win32/timegm.c @@ -111,95 +111,24 @@ static long long __tm_to_secs(const struct tm *tm) return t; } -/* 2000-03-01 (mod 400 year, immediately after feb29 */ -#define LEAPOCH (946684800LL + 86400*(31+29)) - -#define DAYS_PER_400Y (365*400 + 97) -#define DAYS_PER_100Y (365*100 + 24) -#define DAYS_PER_4Y (365*4 + 1) - -static int __secs_to_tm(long long t, struct tm *tm) -{ - long long days, secs, years; - int remdays, remsecs, remyears; - int qc_cycles, c_cycles, q_cycles; - int months; - int wday, yday, leap; - static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; - - /* Reject time_t values whose year would overflow int */ - if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) - return -1; - - secs = t - LEAPOCH; - days = secs / 86400; - remsecs = secs % 86400; - if (remsecs < 0) { - remsecs += 86400; - days--; - } - - wday = (3+days)%7; - if (wday < 0) wday += 7; - - qc_cycles = days / DAYS_PER_400Y; - remdays = days % DAYS_PER_400Y; - if (remdays < 0) { - remdays += DAYS_PER_400Y; - qc_cycles--; - } - - c_cycles = remdays / DAYS_PER_100Y; - if (c_cycles == 4) c_cycles--; - remdays -= c_cycles * DAYS_PER_100Y; - - q_cycles = remdays / DAYS_PER_4Y; - if (q_cycles == 25) q_cycles--; - remdays -= q_cycles * DAYS_PER_4Y; - - remyears = remdays / 365; - if (remyears == 4) remyears--; - remdays -= remyears * 365; - - leap = !remyears && (q_cycles || !c_cycles); - yday = remdays + 31 + 28 + leap; - if (yday >= 365+leap) yday -= 365+leap; - - years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; - - for (months=0; days_in_month[months] <= remdays; months++) - remdays -= days_in_month[months]; - - if (months >= 10) { - months -= 12; - years++; - } - - if (years+100 > INT_MAX || years+100 < INT_MIN) - return -1; - - tm->tm_year = years + 100; - tm->tm_mon = months + 2; - tm->tm_mday = remdays + 1; - tm->tm_wday = wday; - tm->tm_yday = yday; - - tm->tm_hour = remsecs / 3600; - tm->tm_min = remsecs / 60 % 60; - tm->tm_sec = remsecs % 60; - - return 0; -} - +/* + * Restricted version of timegm: + * + * it doesn't normalise its argument + * its return value is limited to the range Microsoft supports + */ time_t timegm(struct tm *tm) { - struct tm new; long long t = __tm_to_secs(tm); - if (__secs_to_tm(t, &new) < 0) { + if (t < 0 || +#ifdef _USE_32BIT_TIME_T + t > INT_MAX /* 2038-01-19 03:14:07Z */ +#else + t > 32535215999 /* 3000-12-31 23:59:59Z */ +#endif + ) { errno = EOVERFLOW; return -1; } - *tm = new; - tm->tm_isdst = 0; return t; } -- cgit v1.2.3-55-g6feb