diff options
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 385 |
1 files changed, 39 insertions, 346 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 5ce8664f274..61c58d05e7a 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -3,7 +3,7 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.6 2004/05/21 20:59:10 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.7 2004/06/03 02:08:07 tgl Exp $ */ /* @@ -69,7 +69,7 @@ struct ttinfo struct lsinfo { /* leap second information */ - time_t ls_trans; /* transition time */ + pg_time_t ls_trans; /* transition time */ long ls_corr; /* correction to apply */ }; @@ -81,7 +81,7 @@ struct state int timecnt; int typecnt; int charcnt; - time_t ats[TZ_MAX_TIMES]; + pg_time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), @@ -114,23 +114,11 @@ 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 time_t *timep, long offset, struct pg_tm * tmp); -static void localsub(const time_t *timep, long offset, struct pg_tm * tmp); -static int increment_overflow(int *number, int delta); -static int normalize_overflow(int *tensptr, int *unitsptr, int base); -static time_t time1(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset); -static time_t time2(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset, int *okayp); -static time_t time2sub(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset, int *okayp, int do_norm_secs); -static void timesub(const time_t *timep, long offset, +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); +static void timesub(const pg_time_t *timep, long offset, const struct state * sp, struct pg_tm * tmp); -static int tmcomp(const struct pg_tm * atmp, const struct pg_tm * btmp); -static time_t transtime(time_t janfirst, int year, +static pg_time_t transtime(pg_time_t janfirst, int year, const struct rule * rulep, long offset); static int tzload(const char *name, struct state * sp); static int tzparse(const char *name, struct state * sp, int lastditch); @@ -503,12 +491,12 @@ getrule(const char *strp, register struct rule * rulep) * year, a rule, and the offset from UTC at the time that rule takes effect, * calculate the Epoch-relative time that rule takes effect. */ -static time_t -transtime(const time_t janfirst, const int year, +static pg_time_t +transtime(const pg_time_t janfirst, const int year, register const struct rule * rulep, const long offset) { register int leapyear; - register time_t value = 0; + register pg_time_t value = 0; register int i; int d, m1, @@ -612,7 +600,7 @@ tzparse(const char *name, register struct state * sp, const int lastditch) size_t dstlen; long stdoffset; long dstoffset; - register time_t *atp; + register pg_time_t *atp; register unsigned char *typep; register char *cp; register int load_result; @@ -663,9 +651,9 @@ tzparse(const char *name, register struct state * sp, const int lastditch) struct rule start; struct rule end; register int year; - register time_t janfirst; - time_t starttime; - time_t endtime; + register pg_time_t janfirst; + pg_time_t starttime; + pg_time_t endtime; ++name; if ((name = getrule(name, &start)) == NULL) @@ -886,12 +874,12 @@ pg_tzset(const char *name) * The unused offset argument is for the benefit of mktime variants. */ static void -localsub(const time_t *timep, const long offset, struct pg_tm * tmp) +localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) { register struct state *sp; register const struct ttinfo *ttisp; register int i; - const time_t t = *timep; + const pg_time_t t = *timep; sp = lclptr; if (sp->timecnt == 0 || t < sp->ats[0]) @@ -920,7 +908,7 @@ localsub(const time_t *timep, const long offset, struct pg_tm * tmp) struct pg_tm * -pg_localtime(const time_t *timep) +pg_localtime(const pg_time_t *timep) { localsub(timep, 0L, &tm); return &tm; @@ -931,7 +919,7 @@ pg_localtime(const time_t *timep) * gmtsub is to gmtime as localsub is to localtime. */ static void -gmtsub(const time_t *timep, const long offset, struct pg_tm * tmp) +gmtsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) { if (!gmt_is_set) { @@ -952,7 +940,7 @@ gmtsub(const time_t *timep, const long offset, struct pg_tm * tmp) } struct pg_tm * -pg_gmtime(const time_t *timep) +pg_gmtime(const pg_time_t *timep) { gmtsub(timep, 0L, &tm); return &tm; @@ -960,11 +948,13 @@ pg_gmtime(const time_t *timep) static void -timesub(const time_t *timep, const long offset, +timesub(const pg_time_t *timep, const long offset, register const struct state * sp, register struct pg_tm * tmp) { register const struct lsinfo *lp; - register long days; + /* expand days to 64 bits to support full Julian-day range */ + register int64 days; + register int idays; register long rem; register int y; register int yleap; @@ -1036,335 +1026,38 @@ timesub(const time_t *timep, const long offset, if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; y = EPOCH_YEAR; -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) + /* + * 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... + */ +#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)]) { register int newy; newy = y + days / DAYSPERNYEAR; if (days < 0) --newy; - days -= (newy - y) * DAYSPERNYEAR + + 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; - tmp->tm_yday = (int) days; + idays = (int) days; /* no longer have a range problem */ + tmp->tm_yday = idays; ip = mon_lengths[yleap]; - for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) - days = days - (long) ip[tmp->tm_mon]; - tmp->tm_mday = (int) (days + 1); + for (i = 0; idays >= ip[i]; ++i) + idays -= ip[i]; + tmp->tm_mon = i; + tmp->tm_mday = idays + 1; tmp->tm_isdst = 0; tmp->tm_gmtoff = offset; } -/* - * Adapted from code provided by Robert Elz, who writes: - * The "best" way to do mktime I think is based on an idea of Bob - * Kridle's (so its said...) from a long time ago. - * [kridle@xinet.com as of 1996-01-16.] - * It does a binary search of the time_t space. Since time_t's are - * just 32 bits, its a max of 32 iterations (even at 64 bits it - * would still be very reasonable). - */ - -#define WRONG (-1) - -/* - * Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). - */ - -static int -increment_overflow(int *number, int delta) -{ - int number0; - - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -} - -static int -normalize_overflow(int *tensptr, int *unitsptr, const int base) -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int -tmcomp(register const struct pg_tm * atmp, register const struct pg_tm * btmp) -{ - register int result; - - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && - (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && - (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && - (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; - return result; -} - -static time_t -time2sub(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset, int *okayp, const int do_norm_secs) -{ - register const struct state *sp; - register int dir; - register int bits; - register int i, - j; - register int saved_seconds; - time_t newt; - time_t t; - struct pg_tm yourtm, - mytm; - - *okayp = FALSE; - yourtm = *tmp; - if (do_norm_secs) - { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - - /* - * Turn yourtm.tm_year into an actual year number for now. It is - * converted back to an offset from TM_YEAR_BASE later. - */ - if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) - { - if (increment_overflow(&yourtm.tm_year, -1)) - return WRONG; - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(i)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) - { - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(i)]; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; - } - for (;;) - { - i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; - if (yourtm.tm_mday <= i) - break; - yourtm.tm_mday -= i; - if (++yourtm.tm_mon >= MONSPERYEAR) - { - yourtm.tm_mon = 0; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; - } - } - if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) - { - /* - * We can't set tm_sec to 0, because that might push the time - * below the minimum representable time. Set tm_sec to 59 - * instead. This assumes that the minimum representable time is - * not in the same minute that a leap second was deleted from, - * which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } - else - { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - - /* - * Divide the search space in half (this works whether time_t is - * signed or unsigned). - */ - bits = TYPE_BIT(time_t) -1; - - /* - * If time_t is signed, then 0 is just above the median, assuming - * two's complement arithmetic. If time_t is unsigned, then (1 << - * bits) is just above the median. - */ - t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); - for (;;) - { - (*funcp) (&t, offset, &mytm); - dir = tmcomp(&mytm, &yourtm); - if (dir != 0) - { - if (bits-- < 0) - return WRONG; - if (bits < 0) - --t; /* may be needed if new t is minimal */ - else if (dir > 0) - t -= ((time_t) 1) << bits; - else - t += ((time_t) 1) << bits; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - - /* - * Right time, wrong type. Hunt for right time, right type. - * It's okay to guess wrong since the guess gets checked. - */ - - /* - * The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); - for (i = sp->typecnt - 1; i >= 0; --i) - { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) - { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - (*funcp) (&newt, offset, &mytm); - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - - /* - * We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - (*funcp) (&t, offset, tmp); - *okayp = TRUE; - return t; -} - -static time_t -time2(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset, int *okayp) -{ - time_t t; - - /* - * First try without normalization of seconds (in case tm_sec - * contains a value associated with a leap second). If that fails, - * try with normalization of seconds. - */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); -} - -static time_t -time1(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset) -{ - register time_t t; - register const struct state *sp; - register int samei, - otheri; - register int sameind, - otherind; - register int i; - register int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; - - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); - if (okay || tmp->tm_isdst < 0) - return t; - - /* - * We're supposed to assume that somebody took a time of one type - * and did some math on it that yielded a "struct pg_tm" that's bad. - * We try to divine the type they started from and adjust to the - * type they need. - */ - - /* - * The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); - for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) - { - seen[sp->types[i]] = TRUE; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) - { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) - { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -time_t -pg_mktime(struct pg_tm * tmp) -{ - return time1(tmp, localsub, 0L); -} /* * Return the name of the current timezone |