diff options
author | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2001-05-24 06:10:36 +0000 |
---|---|---|
committer | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2001-05-24 06:10:36 +0000 |
commit | 0fb0d42369a02071e2dec4fb3cfd137262b192f1 (patch) | |
tree | 1a4852fe4c65ce74774f1e9587fc8010437b1e2d /time.c | |
parent | 2287c526be0074e3d6e8e2b2927247866c237bd0 (diff) |
* eval.c (rb_yield_0): need argument adjustment for C defined
blocks too.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1444 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'time.c')
-rw-r--r-- | time.c | 244 |
1 files changed, 155 insertions, 89 deletions
@@ -286,108 +286,174 @@ static VALUE time_gmtime _((VALUE)); static VALUE time_localtime _((VALUE)); static VALUE time_get_tm _((VALUE, int)); +static int +tmcmp(a, b) + struct tm *a; + struct tm *b; +{ + if (a->tm_year != b->tm_year) + return a->tm_year < b->tm_year ? -1 : 1; + else if (a->tm_mon != b->tm_mon) + return a->tm_mon < b->tm_mon ? -1 : 1; + else if (a->tm_mday != b->tm_mday) + return a->tm_mday < b->tm_mday ? -1 : 1; + else if (a->tm_hour != b->tm_hour) + return a->tm_hour < b->tm_hour ? -1 : 1; + else if (a->tm_min != b->tm_min) + return a->tm_min < b->tm_min ? -1 : 1; + else if (a->tm_sec != b->tm_sec) + return a->tm_sec < b->tm_sec ? -1 : 1; + else + return 0; +} + static time_t make_time_t(tptr, utc_p) struct tm *tptr; int utc_p; { - struct timeval tv; - time_t oguess, guess; - struct tm *tm; - long t, diff, i; + time_t guess, guess_lo, guess_hi; + struct tm *tm, tm_lo, tm_hi; + int d; - if (gettimeofday(&tv, 0) < 0) { - rb_sys_fail("gettimeofday"); - } - guess = tv.tv_sec; +#ifdef NEGATIVE_TIME_T + guess_lo = 1 << (8 * sizeof(time_t) - 1); +#else + guess_lo = 0; +#endif + guess_hi = ((time_t)-1) < ((time_t)0) ? + (1U << (8 * sizeof(time_t) - 1)) - 1 : + ~(time_t)0; - tm = gmtime(&guess); + tm = (utc_p ? gmtime : localtime)(&guess_lo); if (!tm) goto error; - t = tptr->tm_year; -#ifndef NEGATIVE_TIME_T - if (t < 69) goto out_of_range; -#endif - i = 0; - while (diff = t - tm->tm_year) { - guess += diff * 363 * 24 * 3600; - if (i++ > 255) goto out_of_range; - tm = gmtime(&guess); - if (!tm) goto error; - } - t = tptr->tm_mon; - while (diff = t - tm->tm_mon) { - guess += diff * 27 * 24 * 3600; - tm = gmtime(&guess); - if (!tm) goto error; - if (tptr->tm_year != tm->tm_year) goto out_of_range; - } - oguess = guess; - guess += (tptr->tm_mday - tm->tm_mday) * 24 * 3600; - guess += (tptr->tm_hour - tm->tm_hour) * 3600; - guess += (tptr->tm_min - tm->tm_min) * 60; - guess += (tptr->tm_sec - tm->tm_sec); -#ifndef NEGATIVE_TIME_T - if (guess < 0) goto out_of_range; -#endif + d = tmcmp(tptr, tm); + if (d < 0) goto out_of_range; + if (d == 0) return guess_lo; + tm_lo = *tm; - if (!utc_p) { /* localtime zone adjust */ - struct tm gt, lt; - long tzsec; - - t = 0; - tm = gmtime(&guess); - if (!tm) goto error; - gt = *tm; - tm = localtime(&guess); - if (!tm) goto error; - lt = *tm; - tzsec = (gt.tm_min-lt.tm_min)*60 + (gt.tm_hour-lt.tm_hour)*3600; - - if (lt.tm_year > gt.tm_year) { - tzsec -= 24*3600; - } - else if(gt.tm_year > lt.tm_year) { - tzsec += 24*3600; - } - else { - tzsec += (gt.tm_yday - lt.tm_yday)*24*3600; + tm = (utc_p ? gmtime : localtime)(&guess_hi); + if (!tm) goto error; + d = tmcmp(tptr, tm); + if (d > 0) goto out_of_range; + if (d == 0) return guess_hi; + tm_hi = *tm; + + while (guess_lo + 1 < guess_hi) { /* there is a gap between lo and hi. */ + unsigned long range; + int a, b; + /* + Try precious guess by a linear interpolation at first. + `a' and `b' is a coefficient of guess_lo and guess_hi. + `range' is approximation of maximum error by the interpolation. + (a + b)**2 should be less than 2**31 to avoid overflow. + When these parameter is wrong, binary search is used. + */ + a = (tm_hi.tm_year - tptr->tm_year); + b = (tptr->tm_year - tm_lo.tm_year); + range = 366 * 24 * 3600; + if (a + b < 46000 / 366) { + /* 46000 is selected as `some big number less than sqrt(2**31)'. */ + /* The distinction between leap/non-leap year is not important here. */ + static int days[] = { + 0, + 0 + 31, + 0 + 31 + 29, + 0 + 31 + 29 + 31, + 0 + 31 + 29 + 31 + 30, + 0 + 31 + 29 + 31 + 30 + 31, + 0 + 31 + 29 + 31 + 30 + 31 + 30, + 0 + 31 + 29 + 31 + 30 + 31 + 30 + 31, + 0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31, + 0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov */ + }; + a *= 366; + b *= 366; + d = days[tptr->tm_mon] + tptr->tm_mday; + a += days[tm_hi.tm_mon] + tm_hi.tm_mday - d; + b += d - (days[tm_lo.tm_mon] + tm_lo.tm_mday); + range = 2 * 24 * 3600; + } + if (a + b <= 1) { + range = 2; + a *= 24 * 3600; + b *= 24 * 3600; + d = tptr->tm_hour * 3600 + tptr->tm_min * 60 + tptr->tm_sec; + a += tm_hi.tm_hour * 3600 + tm_hi.tm_min * 60 + tm_hi.tm_sec - d; + b += d - (tm_lo.tm_hour * 3600 + tm_lo.tm_min * 60 + tm_lo.tm_sec); + } + if (a <= 0) a = 1; + if (b <= 0) b = 1; + d = a + b; + guess = guess_lo / d * a + guess_hi / d * b; + /* Although `%' may not work with negative value, + it doesn't cause serious problem because there is a fail safe. */ + guess += ((guess_lo % d) * a + (guess_hi % d) * b) / d; + + fixguess: + if (guess <= guess_lo || guess >= guess_hi) { + /* Precious guess is invalid. try binary search. */ + guess = guess_lo / 2 + guess_hi / 2; + if (guess <= guess_lo) + guess = guess_lo + 1; + else if (guess >= guess_hi) + guess = guess_hi - 1; + range = 0; + } + + tm = (utc_p ? gmtime : localtime)(&guess); + if (!tm) goto error; + + d = tmcmp(tptr, tm); + if (d == 0) { + if (!utc_p && !tm->tm_isdst) { + /* When leaving DST, there may be two time corresponding to given + argument. make_time_t returns DST in such cases. */ + /* xxx this assumes a difference in time as 3600 seconds. */ + time_t guess2 = guess - 3600; + tm = localtime(&guess2); + if (!tm) return guess; + if (tmcmp(tptr, tm) == 0) + return guess2; } - if (lt.tm_isdst) guess += 3600; - guess += tzsec; -#ifndef NEGATIVE_TIME_T - if (guess < 0) goto out_of_range; -#endif - tm = localtime(&guess); - if (!tm) goto error; - if (lt.tm_isdst != tm->tm_isdst || tptr->tm_hour != tm->tm_hour) { - time_t tmp = guess - 3600; - tm = localtime(&tmp); - if (!tm) goto error; - if (tptr->tm_hour == tm->tm_hour) { - guess = tmp; - } - else if (lt.tm_isdst == tm->tm_isdst) { - tmp = guess + 3600; - tm = localtime(&tmp); - if (!tm) goto error; - if (tptr->tm_hour == tm->tm_hour) { - guess = tmp; - } - } + return guess; + } + else if (d < 0) { + guess_hi = guess; + tm_hi = *tm; + if (range && range < (unsigned long)(guess_hi - guess_lo)) { + guess = guess - range; + range = 0; + goto fixguess; } - if (tptr->tm_min != tm->tm_min) { - guess += (tptr->tm_min - tm->tm_min) * 60; + } + else { + guess_lo = guess; + tm_lo = *tm; + if (range && range < (unsigned long)(guess_hi - guess_lo)) { + guess = guess + range; + range = 0; + goto fixguess; } -#ifndef NEGATIVE_TIME_T - if (guess < 0) goto out_of_range; -#endif + } + } + /* given time is not found. */ + if (guess_lo + 1 == guess_hi) { + /* given argument is invalid: 04/29 at non-leap year for example. */ + return guess_hi; + } + else { + /* given argument is in a gap. When it enters DST, for example. */ + d = tptr->tm_sec - tm_lo.tm_sec; + d += (tptr->tm_min - tm_lo.tm_min) * 60; + d += (tptr->tm_hour - tm_lo.tm_hour) * 3600; + if (d < 0) + d += 24 * 3600; + return guess_hi + d - 1; } -#ifdef NEGATIVE_TIME_T - if (oguess > 365 * 24 * 3600 && guess < 0) goto out_of_range; - if (guess > 365 * 24 * 3600 && oguess < 0) goto out_of_range; -#endif - - return guess; out_of_range: rb_raise(rb_eArgError, "time out of range"); |