diff options
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 599 |
1 files changed, 473 insertions, 126 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index e499ba62f67..2efa3ecc0e3 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -1,15 +1,14 @@ /* * This file is in the public domain, so clarified as of - * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). + * 1996-06-05 by Arthur David Olson. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.19 2007/11/15 21:14:46 momjian Exp $ + * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.20 2008/02/16 21:16:04 tgl Exp $ */ /* - * Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). - * POSIX-style TZ environment variable handling from Guy Harris - * (guy@auspex.com). + * Leap second handling from Bradley White. + * POSIX-style TZ environment variable handling from Guy Harris. */ /* this file needs to build in both frontend and backend contexts */ @@ -36,9 +35,9 @@ * 5. They might reference tm.TM_ZONE after calling offtime. * What's best to do in the above cases is open to debate; * for now, we just set things up so that in any of the five cases - * WILDABBR is used. Another possibility: initialize tzname[0] to the + * WILDABBR is used. Another possibility: initialize tzname[0] to the * string "tzname[0] used before set", and similarly for the other cases. - * And another: initialize tzname[0] to "ERA", with an explanation in the + * And another: initialize tzname[0] to "ERA", with an explanation in the * manual page of what this "time zone abbreviation" means (doing this so * that tzname[0] has the "normal" length of three characters). *---------- @@ -46,7 +45,7 @@ #define WILDABBR " " #endif /* !defined WILDABBR */ -static char wildabbr[] = "WILDABBR"; +static char wildabbr[] = WILDABBR; static const char gmt[] = "GMT"; @@ -77,18 +76,25 @@ struct rule */ static long detzcode(const char *codep); +static pg_time_t detzcode64(const char *codep); +static int differ_by_repeat(pg_time_t t1, pg_time_t t0); static const char *getzname(const char *strp); +static const char *getqzname(const char *strp, int delim); static const char *getnum(const char *strp, int *nump, int min, int max); static const char *getsecs(const char *strp, long *secsp); static const char *getoffset(const char *strp, long *offsetp); static const char *getrule(const char *strp, struct rule * rulep); static void gmtload(struct state * sp); -static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp); -static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz); -static void timesub(const pg_time_t *timep, long offset, - const struct state * sp, struct pg_tm * tmp); +static struct pg_tm *gmtsub(const pg_time_t *timep, long offset, + struct pg_tm *tmp); +static struct pg_tm *localsub(const pg_time_t *timep, long offset, + struct pg_tm *tmp, const pg_tz *tz); +static int increment_overflow(int *number, int delta); static pg_time_t transtime(pg_time_t janfirst, int year, - const struct rule * rulep, long offset); + const struct rule *rulep, long offset); +static int typesequiv(const struct state *sp, int a, int b); +static struct pg_tm *timesub(const pg_time_t *timep, long offset, + const struct state *sp, struct pg_tm *tmp); /* GMT timezone */ static struct state gmtmem; @@ -103,7 +109,7 @@ static int gmt_is_set = 0; * Except for the strftime function, these functions [asctime, * ctime, gmtime, localtime] return values in one of two static * objects: a broken-down time structure and an array of char. - * Thanks to Paul Eggert (eggert@twinsun.com) for noting this. + * Thanks to Paul Eggert for noting this. */ static struct pg_tm tm; @@ -115,18 +121,48 @@ detzcode(const char *codep) long result; int i; - result = (codep[0] & 0x80) ? ~0L : 0L; + result = (codep[0] & 0x80) ? ~0L : 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } +static pg_time_t +detzcode64(const char *codep) +{ + pg_time_t result; + int i; + + result = (codep[0] & 0x80) ? (~(int64) 0) : 0; + for (i = 0; i < 8; ++i) + result = result * 256 + (codep[i] & 0xff); + return result; +} + +static int +differ_by_repeat(pg_time_t t1, pg_time_t t0) +{ + if (TYPE_INTEGRAL(pg_time_t) && + TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_time_t) < SECSPERREPEAT_BITS) + return 0; + return t1 - t0 == SECSPERREPEAT; +} + int -tzload(const char *name, char *canonname, struct state * sp) +tzload(const char *name, char *canonname, struct state * sp, int doextend) { const char *p; int i; int fid; + int stored; + int nread; + union + { + struct tzhead tzhead; + char buf[2 * sizeof(struct tzhead) + + 2 * sizeof *sp + + 4 * TZ_MAX_TIMES]; + } u; if (name == NULL && (name = TZDEFAULT) == NULL) return -1; @@ -135,19 +171,14 @@ tzload(const char *name, char *canonname, struct state * sp) fid = pg_open_tzfile(name, canonname); if (fid < 0) return -1; + nread = read(fid, u.buf, sizeof u.buf); + if (close(fid) != 0 || nread <= 0) + return -1; + for (stored = 4; stored <= 8; stored *= 2) { - struct tzhead *tzhp; - union - { - struct tzhead tzhead; - char buf[sizeof *sp + sizeof *tzhp]; - } u; int ttisstdcnt; int ttisgmtcnt; - i = read(fid, u.buf, sizeof u.buf); - if (close(fid) != 0) - return -1; ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); @@ -162,18 +193,19 @@ tzload(const char *name, char *canonname, struct state * sp) (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; - if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ + if (nread - (p - u.buf) < + sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ - sp->typecnt * (4 + 2) + /* ttinfos */ + sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ - sp->leapcnt * (4 + 4) + /* lsinfos */ + sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ return -1; for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = detzcode(p); - p += 4; + sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p); + p += stored; } for (i = 0; i < sp->timecnt; ++i) { @@ -204,8 +236,8 @@ tzload(const char *name, char *canonname, struct state * sp) struct lsinfo *lsisp; lsisp = &sp->lsis[i]; - lsisp->ls_trans = detzcode(p); - p += 4; + lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p); + p += stored; lsisp->ls_corr = detzcode(p); p += 4; } @@ -239,10 +271,127 @@ tzload(const char *name, char *canonname, struct state * sp) return -1; } } + /* + * Out-of-sort ats should mean we're running on a + * signed time_t system but using a data file with + * unsigned values (or vice versa). + */ + for (i = 0; i < sp->timecnt - 2; ++i) + if (sp->ats[i] > sp->ats[i + 1]) + { + ++i; + if (TYPE_SIGNED(pg_time_t)) + { + /* + * Ignore the end (easy). + */ + sp->timecnt = i; + } + else + { + /* + * Ignore the beginning (harder). + */ + int j; + + for (j = 0; j + i < sp->timecnt; ++j) + { + sp->ats[j] = sp->ats[j + i]; + sp->types[j] = sp->types[j + i]; + } + sp->timecnt = j; + } + break; + } + /* + * If this is an old file, we're done. + */ + if (u.tzhead.tzh_version[0] == '\0') + break; + nread -= p - u.buf; + for (i = 0; i < nread; ++i) + u.buf[i] = p[i]; + /* + * If this is a narrow integer time_t system, we're done. + */ + if (stored >= (int) sizeof(pg_time_t) && TYPE_INTEGRAL(pg_time_t)) + break; } + if (doextend && nread > 2 && + u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) + { + struct state ts; + int result; + + u.buf[nread - 1] = '\0'; + result = tzparse(&u.buf[1], &ts, FALSE); + if (result == 0 && ts.typecnt == 2 && + sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) + { + for (i = 0; i < 2; ++i) + ts.ttis[i].tt_abbrind += + sp->charcnt; + for (i = 0; i < ts.charcnt; ++i) + sp->chars[sp->charcnt++] = + ts.chars[i]; + i = 0; + while (i < ts.timecnt && + ts.ats[i] <= + sp->ats[sp->timecnt - 1]) + ++i; + while (i < ts.timecnt && + sp->timecnt < TZ_MAX_TIMES) + { + sp->ats[sp->timecnt] = + ts.ats[i]; + sp->types[sp->timecnt] = + sp->typecnt + + ts.types[i]; + ++sp->timecnt; + ++i; + } + sp->ttis[sp->typecnt++] = ts.ttis[0]; + sp->ttis[sp->typecnt++] = ts.ttis[1]; + } + } + i = 2 * YEARSPERREPEAT; + sp->goback = sp->goahead = sp->timecnt > i; + sp->goback = sp->goback && + typesequiv(sp, sp->types[i], sp->types[0]) && + differ_by_repeat(sp->ats[i], sp->ats[0]); + sp->goahead = sp->goahead && + typesequiv(sp, sp->types[sp->timecnt - 1], + sp->types[sp->timecnt - 1 - i]) && + differ_by_repeat(sp->ats[sp->timecnt - 1], + sp->ats[sp->timecnt - 1 - i]); return 0; } +static int +typesequiv(const struct state *sp, int a, int b) +{ + int result; + + if (sp == NULL || + a < 0 || a >= sp->typecnt || + b < 0 || b >= sp->typecnt) + result = FALSE; + else + { + const struct ttinfo *ap = &sp->ttis[a]; + const struct ttinfo *bp = &sp->ttis[b]; + + result = ap->tt_gmtoff == bp->tt_gmtoff && + ap->tt_isdst == bp->tt_isdst && + ap->tt_ttisstd == bp->tt_ttisstd && + ap->tt_ttisgmt == bp->tt_ttisgmt && + strcmp(&sp->chars[ap->tt_abbrind], + &sp->chars[bp->tt_abbrind]) == 0; + } + return result; +} + static const int mon_lengths[2][MONSPERYEAR] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} @@ -254,7 +403,7 @@ static const int year_lengths[2] = { /* * Given a pointer into a time zone string, scan until a character that is not - * a valid character in a zone name is found. Return a pointer to that + * a valid character in a zone name is found. Return a pointer to that * character. */ static const char * @@ -269,6 +418,24 @@ getzname(const char *strp) } /* + * Given a pointer into an extended time zone string, scan until the ending + * delimiter of the zone name is located. Return a pointer to the delimiter. + * + * As with getzname above, the legal character set is actually quite + * restricted, with other characters producing undefined results. + * We don't do any checking here; checking is done later in common-case code. + */ +static const char * +getqzname(const char *strp, int delim) +{ + int c; + + while ((c = *strp) != '\0' && c != delim) + ++strp; + return strp; +} + +/* * Given a pointer into a time zone string, extract a number from that string. * Check that the number is within a specified range; if it is not, return * NULL. @@ -327,7 +494,7 @@ getsecs(const char *strp, long *secsp) if (*strp == ':') { ++strp; - /* `SECSPERMIN' allows for leap seconds. */ + /* `SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; @@ -365,7 +532,7 @@ getoffset(const char *strp, long *offsetp) /* * Given a pointer into a time zone string, extract a rule in the form - * date[/time]. See POSIX section 8 for the format of "date" and "time". + * date[/time]. See POSIX section 8 for the format of "date" and "time". * If a valid rule is not found, return NULL. * Otherwise, return a pointer to the first character not part of the rule. */ @@ -559,26 +726,47 @@ tzparse(const char *name, struct state * sp, int lastditch) } else { - name = getzname(name); - stdlen = name - stdname; - if (stdlen < 3) - return -1; + if (*name == '<') + { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return (-1); + stdlen = name - stdname; + name++; + } + else + { + name = getzname(name); + stdlen = name - stdname; + } if (*name == '\0') return -1; name = getoffset(name, &stdoffset); if (name == NULL) return -1; - load_result = tzload(TZDEFRULES, NULL, sp); + load_result = tzload(TZDEFRULES, NULL, sp, FALSE); } if (load_result != 0) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - if (dstlen < 3) - return -1; + if (*name == '<') + { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return -1; + dstlen = name - dstname; + name++; + } + else + { + dstname = name; + name = getzname(name); + dstlen = name - dstname; /* length of DST zone name */ + } if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); @@ -610,11 +798,8 @@ tzparse(const char *name, struct state * sp, int lastditch) sp->typecnt = 2; /* standard time and DST */ /* - * Two transitions per year, from EPOCH_YEAR to 2037. + * Two transitions per year, from EPOCH_YEAR forward. */ - sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); - if (sp->timecnt > TZ_MAX_TIMES) - return -1; sp->ttis[0].tt_gmtoff = -dstoffset; sp->ttis[0].tt_isdst = 1; sp->ttis[0].tt_abbrind = stdlen + 1; @@ -624,8 +809,13 @@ tzparse(const char *name, struct state * sp, int lastditch) atp = sp->ats; typep = sp->types; janfirst = 0; - for (year = EPOCH_YEAR; year <= 2037; ++year) + sp->timecnt = 0; + for (year = EPOCH_YEAR; + sp->timecnt + 2 <= TZ_MAX_TIMES; + ++year) { + pg_time_t newfirst; + starttime = transtime(janfirst, year, &start, stdoffset); endtime = transtime(janfirst, year, &end, @@ -644,8 +834,13 @@ tzparse(const char *name, struct state * sp, int lastditch) *atp++ = endtime; *typep++ = 1; /* DST ends */ } - janfirst += year_lengths[isleap(year)] * + sp->timecnt += 2; + newfirst = janfirst; + newfirst += year_lengths[isleap(year)] * SECSPERDAY; + if (newfirst <= janfirst) + break; + janfirst = newfirst; } } else @@ -776,7 +971,7 @@ tzparse(const char *name, struct state * sp, int lastditch) static void gmtload(struct state * sp) { - if (tzload(gmt, NULL, sp) != 0) + if (tzload(gmt, NULL, sp, TRUE) != 0) (void) tzparse(gmt, sp, TRUE); } @@ -784,20 +979,63 @@ gmtload(struct state * sp) /* * The easy way to behave "as if no library function calls" localtime * is to not call it--so we drop its guts into "localsub", which can be - * freely called. (And no, the PANS doesn't require the above behavior-- + * freely called. (And no, the PANS doesn't require the above behavior-- * but it *is* desirable.) * * The unused offset argument is for the benefit of mktime variants. */ -static void -localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz) +static struct pg_tm * +localsub(const pg_time_t *timep, long offset, + struct pg_tm *tmp, const pg_tz *tz) { const struct state *sp; const struct ttinfo *ttisp; int i; + struct pg_tm *result; const pg_time_t t = *timep; sp = &tz->state; + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) + { + pg_time_t newt = t; + pg_time_t seconds; + pg_time_t tcycles; + int64 icycles; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) + return NULL; + seconds = icycles; + seconds *= YEARSPERREPEAT; + seconds *= AVGSECSPERYEAR; + if (t < sp->ats[0]) + newt += seconds; + else newt -= seconds; + if (newt < sp->ats[0] || + newt > sp->ats[sp->timecnt - 1]) + return NULL; /* "cannot happen" */ + result = localsub(&newt, offset, tmp, tz); + if (result == tmp) + { + pg_time_t newy; + + newy = tmp->tm_year; + if (t < sp->ats[0]) + newy -= icycles * YEARSPERREPEAT; + else newy += icycles * YEARSPERREPEAT; + tmp->tm_year = newy; + if (tmp->tm_year != newy) + return NULL; + } + return result; + } if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; @@ -810,39 +1048,49 @@ localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *t } else { - for (i = 1; i < sp->timecnt; ++i) - if (t < sp->ats[i]) - break; - i = sp->types[i - 1]; + int lo = 1; + int hi = sp->timecnt; + + while (lo < hi) + { + int mid = (lo + hi) >> 1; + + if (t < sp->ats[mid]) + hi = mid; + else lo = mid + 1; + } + i = (int) sp->types[lo - 1]; } ttisp = &sp->ttis[i]; - timesub(&t, ttisp->tt_gmtoff, sp, tmp); + result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); tmp->tm_isdst = ttisp->tt_isdst; tmp->tm_zone = &sp->chars[ttisp->tt_abbrind]; + return result; } struct pg_tm * pg_localtime(const pg_time_t *timep, const pg_tz *tz) { - localsub(timep, 0L, &tm, tz); - return &tm; + return localsub(timep, 0L, &tm, tz); } /* * gmtsub is to gmtime as localsub is to localtime. */ -static void -gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp) +static struct pg_tm * +gmtsub(const pg_time_t *timep, long offset, struct pg_tm *tmp) { + struct pg_tm *result; + if (!gmt_is_set) { gmt_is_set = TRUE; gmtload(gmtptr); } - timesub(timep, offset, gmtptr, tmp); + result = timesub(timep, offset, gmtptr, tmp); /* * Could get fancy here and deliver something such as "UTC+xxxx" or @@ -853,28 +1101,37 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp) tmp->tm_zone = wildabbr; else tmp->tm_zone = gmtptr->chars; + + return result; } struct pg_tm * pg_gmtime(const pg_time_t *timep) { - gmtsub(timep, 0L, &tm); - return &tm; + return gmtsub(timep, 0L, &tm); } +/* + * Return the number of leap years through the end of the given year + * where, to make the math easy, the answer for year zero is defined as zero. + */ +static int +leaps_thru_end_of(const int y) +{ + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); +} -static void + +static struct pg_tm * timesub(const pg_time_t *timep, long offset, - const struct state * sp, struct pg_tm * tmp) + const struct state *sp, struct pg_tm *tmp) { const struct lsinfo *lp; - - /* expand days to 64 bits to support full Julian-day range */ - int64 days; - int idays; + pg_time_t tdays; + int idays; /* unsigned would be so 2003 */ long rem; int y; - int yleap; const int *ip; long corr; int hit; @@ -907,74 +1164,111 @@ timesub(const pg_time_t *timep, long offset, break; } } - days = *timep / SECSPERDAY; - rem = *timep % SECSPERDAY; -#ifdef mc68k - if (*timep == 0x80000000) + y = EPOCH_YEAR; + tdays = *timep / SECSPERDAY; + rem = *timep - tdays * SECSPERDAY; + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { - /* - * A 3B1 muffs the division on the most negative number. - */ - days = -24855; - rem = -11648; + int newy; + pg_time_t tdelta; + int idelta; + int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + idelta = tdelta; + if (tdelta - idelta >= 1 || idelta - tdelta >= 1) + return NULL; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + return NULL; + leapdays = leaps_thru_end_of(newy - 1) - + leaps_thru_end_of(y - 1); + tdays -= ((pg_time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } + { + long seconds; + + seconds = tdays * SECSPERDAY + 0.5; + tdays = seconds / SECSPERDAY; + rem += seconds - tdays * SECSPERDAY; } -#endif /* defined mc68k */ - rem += (offset - corr); + /* + * Given the range, we can now fearlessly cast... + */ + idays = tdays; + rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; - --days; + --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; - ++days; + ++idays; } - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem = rem % SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - + while (idays < 0) + { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) + { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; /* - * A positive leap second requires a special representation. This uses - * "... ??:59:60" et seq. + * The "extra" mods below avoid overflow problems. */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; - y = EPOCH_YEAR; + tmp->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + tmp->tm_min = (int) (rem / SECSPERMIN); /* - * Note: the point of adding 4800 is to ensure we make the same - * assumptions as Postgres' Julian-date routines about the placement of - * leap years in centuries BC, at least back to 4713BC which is as far as - * we'll go. This is effectively extending Gregorian timekeeping into - * pre-Gregorian centuries, which is a tad bogus but it conforms to the - * SQL spec... + * A positive leap second requires a special representation. This uses + * "... ??:59:60" et seq. */ -#define LEAPS_THRU_END_OF(y) (((y) + 4800) / 4 - ((y) + 4800) / 100 + ((y) + 4800) / 400) - while (days < 0 || days >= (int64) year_lengths[yleap = isleap(y)]) - { - int newy; - - newy = y + days / DAYSPERNYEAR; - if (days < 0) - --newy; - days -= ((int64) (newy - y)) * DAYSPERNYEAR + - LEAPS_THRU_END_OF(newy - 1) - - LEAPS_THRU_END_OF(y - 1); - y = newy; - } - tmp->tm_year = y - TM_YEAR_BASE; - idays = (int) days; /* no longer have a range problem */ - tmp->tm_yday = idays; - ip = mon_lengths[yleap]; - for (i = 0; idays >= ip[i]; ++i) - idays -= ip[i]; - tmp->tm_mon = i; - tmp->tm_mday = idays + 1; + tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; tmp->tm_gmtoff = offset; + return tmp; +} + +/* + * Simplified normalize logic courtesy Paul Eggert. + */ + +static int +increment_overflow(int *number, int delta) +{ + int number0; + + number0 = *number; + *number += delta; + return (*number < number0) != (delta < 0); } /* @@ -1027,6 +1321,48 @@ pg_next_dst_boundary(const pg_time_t *timep, *before_isdst = ttisp->tt_isdst; return 0; } + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) + { + /* For values outside the transition table, extrapolate */ + pg_time_t newt = t; + pg_time_t seconds; + pg_time_t tcycles; + int64 icycles; + int result; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) + return -1; + seconds = icycles; + seconds *= YEARSPERREPEAT; + seconds *= AVGSECSPERYEAR; + if (t < sp->ats[0]) + newt += seconds; + else newt -= seconds; + if (newt < sp->ats[0] || + newt > sp->ats[sp->timecnt - 1]) + return -1; /* "cannot happen" */ + + result = pg_next_dst_boundary(&newt, before_gmtoff, + before_isdst, + boundary, + after_gmtoff, + after_isdst, + tz); + if (t < sp->ats[0]) + *boundary -= seconds; + else + *boundary += seconds; + return result; + } + if (t > sp->ats[sp->timecnt - 1]) { /* No known transition >= t, so use last known segment's type */ @@ -1058,9 +1394,20 @@ pg_next_dst_boundary(const pg_time_t *timep, return 1; } /* Else search to find the containing segment */ - for (i = 1; i < sp->timecnt; ++i) - if (t <= sp->ats[i]) - break; + { + int lo = 1; + int hi = sp->timecnt; + + while (lo < hi) + { + int mid = (lo + hi) >> 1; + + if (t < sp->ats[mid]) + hi = mid; + else lo = mid + 1; + } + i = lo; + } j = sp->types[i - 1]; ttisp = &sp->ttis[j]; *before_gmtoff = ttisp->tt_gmtoff; |