aboutsummaryrefslogtreecommitdiff
path: root/src/timezone/localtime.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-07-19 15:59:36 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-07-19 15:59:36 -0400
commit19d477aa681b4927f95824d724a4197c696f8c75 (patch)
tree4795da77200eb86060bef5e637190e03de6081a4 /src/timezone/localtime.c
parentbdeed0944fadff3ea394d361d0137997fb4db953 (diff)
downloadpostgresql-19d477aa681b4927f95824d724a4197c696f8c75.tar.gz
postgresql-19d477aa681b4927f95824d724a4197c696f8c75.zip
Sync back-branch copies of the timezone code with IANA release tzcode2016c.
Back-patch commit 1c1a7cbd6a1600c9, along with subsequent portability fixes, into all active branches. Also, back-patch commits 696027727 and 596857043 (addition of zic -P option) into 9.1 and 9.2, just to reduce differences between the branches. src/timezone/ is now largely identical in all active branches, except that in 9.1, pgtz.c retains the initial-timezone-selection code that was moved over to initdb in 9.2. Ordinarily we wouldn't risk this much code churn in back branches, but it seems necessary in this case, because among the changes are two feature additions in the "zic" zone data file compiler (a larger limit on the number of allowed DST transitions, and addition of a "%z" escape in zone abbreviations). IANA have not yet started to use those features in their tzdata files, but presumably they will before too long. If we don't update then we'll be unable to adopt new timezone data. Also, installations built with --with-system-tzdata (which includes most distro-supplied builds, I believe) might fail even if we don't update our copies of the data files. There are assorted bug fixes too, mostly affecting obscure timezones or post-2037 dates. Discussion: <13601.1468868947@sss.pgh.pa.us>
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r--src/timezone/localtime.c971
1 files changed, 581 insertions, 390 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 19a24e1d960..a14215d6bde 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -23,7 +23,7 @@
#ifndef WILDABBR
-/*----------
+/*
* Someone might make incorrect use of a time zone abbreviation:
* 1. They might reference tzname[0] before calling tzset (explicitly
* or implicitly).
@@ -36,20 +36,23 @@
* 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
* manual page of what this "time zone abbreviation" means (doing this so
* that tzname[0] has the "normal" length of three characters).
- *----------
*/
#define WILDABBR " "
#endif /* !defined WILDABBR */
-static char wildabbr[] = WILDABBR;
+static const char wildabbr[] = WILDABBR;
static const char gmt[] = "GMT";
+/* The minimum and maximum finite time values. This assumes no padding. */
+static const pg_time_t time_t_min = MINVAL(pg_time_t, TYPE_BIT(pg_time_t));
+static const pg_time_t time_t_max = MAXVAL(pg_time_t, TYPE_BIT(pg_time_t));
+
/*
* The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
* We default to US rules as of 1999-08-17.
@@ -59,51 +62,35 @@ static const char gmt[] = "GMT";
*/
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+/* structs ttinfo, lsinfo, state have been moved to pgtz.h */
+
+enum r_type
+{
+ JULIAN_DAY, /* Jn = Julian day */
+ DAY_OF_YEAR, /* n = day of year */
+ MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
+};
+
struct rule
{
- int r_type; /* type of rule--see below */
+ enum r_type r_type; /* type of rule */
int r_day; /* day number of rule */
int r_week; /* week number of rule */
int r_mon; /* month number of rule */
- long r_time; /* transition time of rule */
+ int32 r_time; /* transition time of rule */
};
-#define JULIAN_DAY 0 /* Jn - Julian day */
-#define DAY_OF_YEAR 1 /* n - day of year */
-#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
-
/*
* Prototypes for static functions.
*/
-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 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);
-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;
+static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
+static bool increment_overflow(int *, int);
+static bool increment_overflow_time(pg_time_t *, int32);
+static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *,
+ struct pg_tm *);
+static bool typesequiv(struct state const *, int, int);
-#define gmtptr (&gmtmem)
-
-
-static int gmt_is_set = 0;
/*
* Section 4.12.3 of X3.159-1989 requires that
@@ -115,147 +102,281 @@ static int gmt_is_set = 0;
static struct pg_tm tm;
+/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */
+static void
+init_ttinfo(struct ttinfo * s, int32 gmtoff, bool isdst, int abbrind)
+{
+ s->tt_gmtoff = gmtoff;
+ s->tt_isdst = isdst;
+ s->tt_abbrind = abbrind;
+ s->tt_ttisstd = false;
+ s->tt_ttisgmt = false;
+}
-static long
+static int32
detzcode(const char *codep)
{
- long result;
+ int32 result;
int i;
+ int32 one = 1;
+ int32 halfmaxval = one << (32 - 2);
+ int32 maxval = halfmaxval - 1 + halfmaxval;
+ int32 minval = -1 - maxval;
- result = (codep[0] & 0x80) ? ~0L : 0;
- for (i = 0; i < 4; ++i)
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 4; ++i)
result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80)
+ {
+ /*
+ * Do two's-complement negation even on non-two's-complement machines.
+ * If the result would be minval - 1, return minval.
+ */
+ result -= !TWOS_COMPLEMENT(int32) &&result != 0;
+ result += minval;
+ }
return result;
}
-static pg_time_t
+static int64
detzcode64(const char *codep)
{
- pg_time_t result;
+ uint64 result;
int i;
+ int64 one = 1;
+ int64 halfmaxval = one << (64 - 2);
+ int64 maxval = halfmaxval - 1 + halfmaxval;
+ int64 minval = -TWOS_COMPLEMENT(int64) -maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 8; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
- result = (codep[0] & 0x80) ? (~(int64) 0) : 0;
- for (i = 0; i < 8; ++i)
- result = result * 256 + (codep[i] & 0xff);
+ if (codep[0] & 0x80)
+ {
+ /*
+ * Do two's-complement negation even on non-two's-complement machines.
+ * If the result would be minval - 1, return minval.
+ */
+ result -= !TWOS_COMPLEMENT(int64) &&result != 0;
+ result += minval;
+ }
return result;
}
-static int
-differ_by_repeat(pg_time_t t1, pg_time_t t0)
+static bool
+differ_by_repeat(const pg_time_t t1, const pg_time_t t0)
{
- if (TYPE_INTEGRAL(pg_time_t) &&
- TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS)
+ if (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, int doextend)
+/* Input buffer for data read from a compiled tz file. */
+union input_buffer
+{
+ /* The first part of the buffer, interpreted as a header. */
+ struct tzhead tzhead;
+
+ /* The entire buffer. */
+ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
+ + 4 * TZ_MAX_TIMES];
+};
+
+/* Local storage needed for 'tzloadbody'. */
+union local_storage
+{
+ /* We don't need the "fullname" member */
+
+ /* The results of analyzing the file's contents after it is opened. */
+ struct
+ {
+ /* The input buffer. */
+ union input_buffer u;
+
+ /* A temporary state used for parsing a TZ string in the file. */
+ struct state st;
+ } u;
+};
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ * format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+ * success, an errno value on failure.
+ * PG: If "canonname" is not NULL, then on success the canonical spelling of
+ * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
+ */
+static int
+tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
+ union local_storage * lsp)
{
- const char *p;
int i;
int fid;
int stored;
- int nread;
- union
+ ssize_t nread;
+ union input_buffer *up = &lsp->u.u;
+ int tzheadsize = sizeof(struct tzhead);
+
+ sp->goback = sp->goahead = false;
+
+ if (!name)
{
- struct tzhead tzhead;
- char buf[2 * sizeof(struct tzhead) +
- 2 * sizeof *sp +
- 4 * TZ_MAX_TIMES];
- } u;
+ name = TZDEFAULT;
+ if (!name)
+ return EINVAL;
+ }
- sp->goback = sp->goahead = FALSE;
- if (name == NULL && (name = TZDEFAULT) == NULL)
- return -1;
if (name[0] == ':')
++name;
+
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;
+ return ENOENT; /* pg_open_tzfile may not set errno */
+
+ nread = read(fid, up->buf, sizeof up->buf);
+ if (nread < tzheadsize)
+ {
+ int err = nread < 0 ? errno : EINVAL;
+
+ close(fid);
+ return err;
+ }
+ if (close(fid) < 0)
+ return errno;
for (stored = 4; stored <= 8; stored *= 2)
{
- int ttisstdcnt;
- int ttisgmtcnt;
-
- ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
- ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
- sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
- sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
- sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
- sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
- p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
- if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
- sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
- sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
- sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
- (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
- (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
- return -1;
- if (nread - (p - u.buf) <
- sp->timecnt * stored + /* ats */
- sp->timecnt + /* types */
- sp->typecnt * 6 + /* ttinfos */
- sp->charcnt + /* chars */
- sp->leapcnt * (stored + 4) + /* lsinfos */
- ttisstdcnt + /* ttisstds */
- ttisgmtcnt) /* ttisgmts */
- return -1;
+ int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int32 ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt);
+ int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int32 timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int32 typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int32 charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+
+ if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+ && 0 < typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+ && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+ && (ttisstdcnt == typecnt || ttisstdcnt == 0)
+ && (ttisgmtcnt == typecnt || ttisgmtcnt == 0)))
+ return EINVAL;
+ if (nread
+ < (tzheadsize /* struct tzhead */
+ + timecnt * stored /* ats */
+ + timecnt /* types */
+ + typecnt * 6 /* ttinfos */
+ + charcnt /* chars */
+ + leapcnt * (stored + 4) /* lsinfos */
+ + ttisstdcnt /* ttisstds */
+ + ttisgmtcnt)) /* ttisgmts */
+ return EINVAL;
+ sp->leapcnt = leapcnt;
+ sp->timecnt = timecnt;
+ sp->typecnt = typecnt;
+ sp->charcnt = charcnt;
+
+ /*
+ * Read transitions, discarding those out of pg_time_t range. But
+ * pretend the last transition before time_t_min occurred at
+ * time_t_min.
+ */
+ timecnt = 0;
for (i = 0; i < sp->timecnt; ++i)
{
- sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p);
+ int64 at
+ = stored == 4 ? detzcode(p) : detzcode64(p);
+
+ sp->types[i] = at <= time_t_max;
+ if (sp->types[i])
+ {
+ pg_time_t attime
+ = ((TYPE_SIGNED(pg_time_t) ? at < time_t_min : at < 0)
+ ? time_t_min : at);
+
+ if (timecnt && attime <= sp->ats[timecnt - 1])
+ {
+ if (attime < sp->ats[timecnt - 1])
+ return EINVAL;
+ sp->types[i - 1] = 0;
+ timecnt--;
+ }
+ sp->ats[timecnt++] = attime;
+ }
p += stored;
}
+
+ timecnt = 0;
for (i = 0; i < sp->timecnt; ++i)
{
- sp->types[i] = (unsigned char) *p++;
- if (sp->types[i] >= sp->typecnt)
- return -1;
+ unsigned char typ = *p++;
+
+ if (sp->typecnt <= typ)
+ return EINVAL;
+ if (sp->types[i])
+ sp->types[timecnt++] = typ;
}
+ sp->timecnt = timecnt;
for (i = 0; i < sp->typecnt; ++i)
{
struct ttinfo *ttisp;
+ unsigned char isdst,
+ abbrind;
ttisp = &sp->ttis[i];
ttisp->tt_gmtoff = detzcode(p);
p += 4;
- ttisp->tt_isdst = (unsigned char) *p++;
- if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
- return -1;
- ttisp->tt_abbrind = (unsigned char) *p++;
- if (ttisp->tt_abbrind < 0 ||
- ttisp->tt_abbrind > sp->charcnt)
- return -1;
+ isdst = *p++;
+ if (!(isdst < 2))
+ return EINVAL;
+ ttisp->tt_isdst = isdst;
+ abbrind = *p++;
+ if (!(abbrind < sp->charcnt))
+ return EINVAL;
+ ttisp->tt_abbrind = abbrind;
}
for (i = 0; i < sp->charcnt; ++i)
sp->chars[i] = *p++;
sp->chars[i] = '\0'; /* ensure '\0' at end */
+
+ /* Read leap seconds, discarding those out of pg_time_t range. */
+ leapcnt = 0;
for (i = 0; i < sp->leapcnt; ++i)
{
- struct lsinfo *lsisp;
+ int64 tr = stored == 4 ? detzcode(p) : detzcode64(p);
+ int32 corr = detzcode(p + stored);
- lsisp = &sp->lsis[i];
- lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p);
- p += stored;
- lsisp->ls_corr = detzcode(p);
- p += 4;
+ p += stored + 4;
+ if (tr <= time_t_max)
+ {
+ pg_time_t trans
+ = ((TYPE_SIGNED(pg_time_t) ? tr < time_t_min : tr < 0)
+ ? time_t_min : tr);
+
+ if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans)
+ {
+ if (trans < sp->lsis[leapcnt - 1].ls_trans)
+ return EINVAL;
+ leapcnt--;
+ }
+ sp->lsis[leapcnt].ls_trans = trans;
+ sp->lsis[leapcnt].ls_corr = corr;
+ leapcnt++;
+ }
}
+ sp->leapcnt = leapcnt;
+
for (i = 0; i < sp->typecnt; ++i)
{
struct ttinfo *ttisp;
ttisp = &sp->ttis[i];
if (ttisstdcnt == 0)
- ttisp->tt_ttisstd = FALSE;
+ ttisp->tt_ttisstd = false;
else
{
+ if (*p != true && *p != false)
+ return EINVAL;
ttisp->tt_ttisstd = *p++;
- if (ttisp->tt_ttisstd != TRUE &&
- ttisp->tt_ttisstd != FALSE)
- return -1;
}
}
for (i = 0; i < sp->typecnt; ++i)
@@ -264,99 +385,87 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
ttisp = &sp->ttis[i];
if (ttisgmtcnt == 0)
- ttisp->tt_ttisgmt = FALSE;
+ ttisp->tt_ttisgmt = false;
else
{
+ if (*p != true && *p != false)
+ return EINVAL;
ttisp->tt_ttisgmt = *p++;
- if (ttisp->tt_ttisgmt != TRUE &&
- ttisp->tt_ttisgmt != FALSE)
- 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))
+ if (up->tzhead.tzh_version[0] == '\0')
break;
+ nread -= p - up->buf;
+ memmove(up->buf, p, nread);
}
if (doextend && nread > 2 &&
- u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
sp->typecnt + 2 <= TZ_MAX_TYPES)
{
- struct state ts;
- int result;
+ struct state *ts = &lsp->u.st;
- 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)
+ up->buf[nread - 1] = '\0';
+ if (tzparse(&up->buf[1], ts, false)
+ && ts->typecnt == 2)
{
- 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)
+ /*
+ * Attempt to reuse existing abbreviations. Without this,
+ * America/Anchorage would stop working after 2037 when
+ * TZ_MAX_CHARS is 50, as sp->charcnt equals 42 (for LMT CAT CAWT
+ * CAPT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for
+ * AKST AKDT). Reusing means sp->charcnt can stay 42 in this
+ * example.
+ */
+ int gotabbr = 0;
+ int charcnt = sp->charcnt;
+
+ for (i = 0; i < 2; i++)
+ {
+ char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
+ int j;
+
+ for (j = 0; j < charcnt; j++)
+ if (strcmp(sp->chars + j, tsabbr) == 0)
+ {
+ ts->ttis[i].tt_abbrind = j;
+ gotabbr++;
+ break;
+ }
+ if (!(j < charcnt))
+ {
+ int tsabbrlen = strlen(tsabbr);
+
+ if (j + tsabbrlen < TZ_MAX_CHARS)
+ {
+ strcpy(sp->chars + j, tsabbr);
+ charcnt = j + tsabbrlen + 1;
+ ts->ttis[i].tt_abbrind = j;
+ gotabbr++;
+ }
+ }
+ }
+ if (gotabbr == 2)
{
- sp->ats[sp->timecnt] =
- ts.ats[i];
- sp->types[sp->timecnt] =
- sp->typecnt +
- ts.types[i];
- ++sp->timecnt;
- ++i;
+ sp->charcnt = charcnt;
+ for (i = 0; i < ts->timecnt; i++)
+ if (sp->ats[sp->timecnt - 1] < ts->ats[i])
+ break;
+ 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];
}
- sp->ttis[sp->typecnt++] = ts.ttis[0];
- sp->ttis[sp->typecnt++] = ts.ttis[1];
}
}
if (sp->timecnt > 1)
@@ -365,7 +474,7 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
if (typesequiv(sp, sp->types[i], sp->types[0]) &&
differ_by_repeat(sp->ats[i], sp->ats[0]))
{
- sp->goback = TRUE;
+ sp->goback = true;
break;
}
for (i = sp->timecnt - 2; i >= 0; --i)
@@ -374,22 +483,81 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
differ_by_repeat(sp->ats[sp->timecnt - 1],
sp->ats[i]))
{
- sp->goahead = TRUE;
+ sp->goahead = true;
+ break;
+ }
+ }
+
+ /*
+ * If type 0 is is unused in transitions, it's the type to use for early
+ * times.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ if (sp->types[i] == 0)
+ break;
+ i = i < sp->timecnt ? -1 : 0;
+
+ /*
+ * Absent the above, if there are transition times and the first
+ * transition is to a daylight time find the standard type less than and
+ * closest to the type of the first transition.
+ */
+ if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst)
+ {
+ i = sp->types[0];
+ while (--i >= 0)
+ if (!sp->ttis[i].tt_isdst)
+ break;
+ }
+
+ /*
+ * If no result yet, find the first standard type. If there is none, punt
+ * to type zero.
+ */
+ if (i < 0)
+ {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt)
+ {
+ i = 0;
break;
}
}
+ sp->defaulttype = i;
return 0;
}
-static int
+/* Load tz data from the file named NAME into *SP. Read extended
+ * format if DOEXTEND. Return 0 on success, an errno value on failure.
+ * PG: If "canonname" is not NULL, then on success the canonical spelling of
+ * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
+ */
+int
+tzload(const char *name, char *canonname, struct state * sp, bool doextend)
+{
+ union local_storage *lsp = malloc(sizeof *lsp);
+
+ if (!lsp)
+ return errno;
+ else
+ {
+ int err = tzloadbody(name, canonname, sp, doextend, lsp);
+
+ free(lsp);
+ return err;
+ }
+}
+
+static bool
typesequiv(const struct state * sp, int a, int b)
{
- int result;
+ bool result;
if (sp == NULL ||
a < 0 || a >= sp->typecnt ||
b < 0 || b >= sp->typecnt)
- result = FALSE;
+ result = false;
else
{
const struct ttinfo *ap = &sp->ttis[a];
@@ -484,19 +652,19 @@ getnum(const char *strp, int *nump, int min, int max)
* of seconds.
*/
static const char *
-getsecs(const char *strp, long *secsp)
+getsecs(const char *strp, int32 *secsp)
{
int num;
/*
- * `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
* "M10.4.6/26", which does not conform to Posix, but which specifies the
- * equivalent of ``02:00 on the first Sunday on or after 23 Oct''.
+ * equivalent of "02:00 on the first Sunday on or after 23 Oct".
*/
strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
if (strp == NULL)
return NULL;
- *secsp = num * (long) SECSPERHOUR;
+ *secsp = num * (int32) SECSPERHOUR;
if (*strp == ':')
{
++strp;
@@ -507,7 +675,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;
@@ -524,13 +692,13 @@ getsecs(const char *strp, long *secsp)
* Otherwise, return a pointer to the first character not part of the time.
*/
static const char *
-getoffset(const char *strp, long *offsetp)
+getoffset(const char *strp, int32 *offsetp)
{
- int neg = 0;
+ bool neg = false;
if (*strp == '-')
{
- neg = 1;
+ neg = true;
++strp;
}
else if (*strp == '+')
@@ -598,7 +766,7 @@ getrule(const char *strp, struct rule * rulep)
* Time specified.
*/
++strp;
- strp = getsecs(strp, &rulep->r_time);
+ strp = getoffset(strp, &rulep->r_time);
}
else
rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
@@ -606,16 +774,15 @@ getrule(const char *strp, struct rule * rulep)
}
/*
- * Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
- * year, a rule, and the offset from UTC at the time that rule takes effect,
- * calculate the Epoch-relative time that rule takes effect.
+ * Given a year, a rule, and the offset from UT at the time that rule takes
+ * effect, calculate the year-relative time that rule takes effect.
*/
-static pg_time_t
-transtime(pg_time_t janfirst, int year,
- const struct rule * rulep, long offset)
+static int32
+transtime(int year, const struct rule * rulep,
+ int32 offset)
{
- int leapyear;
- pg_time_t value = 0;
+ bool leapyear;
+ int32 value;
int i,
d,
m1,
@@ -624,6 +791,7 @@ transtime(pg_time_t janfirst, int year,
yy2,
dow;
+ INITIALIZE(value);
leapyear = isleap(year);
switch (rulep->r_type)
{
@@ -636,7 +804,7 @@ transtime(pg_time_t janfirst, int year,
* just add SECSPERDAY times the day number-1 to the time of
* January 1, midnight, to get the day.
*/
- value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ value = (rulep->r_day - 1) * SECSPERDAY;
if (leapyear && rulep->r_day >= 60)
value += SECSPERDAY;
break;
@@ -647,7 +815,7 @@ transtime(pg_time_t janfirst, int year,
* n - day of year. Just add SECSPERDAY times the day number to
* the time of January 1, midnight, to get the day.
*/
- value = janfirst + rulep->r_day * SECSPERDAY;
+ value = rulep->r_day * SECSPERDAY;
break;
case MONTH_NTH_DAY_OF_WEEK:
@@ -655,9 +823,6 @@ transtime(pg_time_t janfirst, int year,
/*
* Mm.n.d - nth "dth day" of month m.
*/
- value = janfirst;
- for (i = 0; i < rulep->r_mon - 1; ++i)
- value += mon_lengths[leapyear][i] * SECSPERDAY;
/*
* Use Zeller's Congruence to get day-of-week of first day of
@@ -682,7 +847,7 @@ transtime(pg_time_t janfirst, int year,
for (i = 1; i < rulep->r_week; ++i)
{
if (d + DAYSPERWEEK >=
- mon_lengths[leapyear][rulep->r_mon - 1])
+ mon_lengths[(int) leapyear][rulep->r_mon - 1])
break;
d += DAYSPERWEEK;
}
@@ -690,14 +855,16 @@ transtime(pg_time_t janfirst, int year,
/*
* "d" is the day-of-month (zero-origin) of the day we want.
*/
- value += d * SECSPERDAY;
+ value = d * SECSPERDAY;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
break;
}
/*
- * "value" is the Epoch-relative time of 00:00:00 UTC on the day in
- * question. To get the Epoch-relative time of the specified local time
- * on that day, add the transition time and the current offset from UTC.
+ * "value" is the year-relative time of 00:00:00 UT on the day in
+ * question. To get the year-relative time of the specified local time on
+ * that day, add the transition time and the current offset from UT.
*/
return value + rulep->r_time + offset;
}
@@ -705,38 +872,38 @@ transtime(pg_time_t janfirst, int year,
/*
* Given a POSIX section 8-style TZ string, fill in the rule tables as
* appropriate.
+ * Returns true on success, false on failure.
*/
-
-int
-tzparse(const char *name, struct state * sp, int lastditch)
+bool
+tzparse(const char *name, struct state * sp, bool lastditch)
{
const char *stdname;
const char *dstname = NULL;
size_t stdlen;
size_t dstlen;
- long stdoffset;
- long dstoffset;
- pg_time_t *atp;
- unsigned char *typep;
+ size_t charcnt;
+ int32 stdoffset;
+ int32 dstoffset;
char *cp;
- int load_result;
+ bool load_ok;
stdname = name;
if (lastditch)
{
+ /*
+ * This is intentionally somewhat different from the IANA code. We do
+ * not want to invoke tzload() in the lastditch case: we can't assume
+ * pg_open_tzfile() is sane yet, and we don't care about leap seconds
+ * anyway.
+ */
stdlen = strlen(name); /* length of standard zone name */
name += stdlen;
if (stdlen >= sizeof sp->chars)
stdlen = (sizeof sp->chars) - 1;
+ charcnt = stdlen + 1;
stdoffset = 0;
-
- /*
- * Unlike the original zic library, do NOT invoke tzload() here; we
- * can't assume pg_open_tzfile() is sane yet, and we don't care about
- * leap seconds anyway.
- */
- sp->goback = sp->goahead = FALSE;
- load_result = -1;
+ sp->goback = sp->goahead = false; /* simulate failed tzload() */
+ load_ok = false;
}
else
{
@@ -746,7 +913,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
stdname = name;
name = getqzname(name, '>');
if (*name != '>')
- return (-1);
+ return false;
stdlen = name - stdname;
name++;
}
@@ -755,14 +922,17 @@ tzparse(const char *name, struct state * sp, int lastditch)
name = getzname(name);
stdlen = name - stdname;
}
- if (*name == '\0')
- return -1;
+ if (*name == '\0') /* we allow empty STD abbrev, unlike IANA */
+ return false;
name = getoffset(name, &stdoffset);
if (name == NULL)
- return -1;
- load_result = tzload(TZDEFRULES, NULL, sp, FALSE);
+ return false;
+ charcnt = stdlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ load_ok = tzload(TZDEFRULES, NULL, sp, false) == 0;
}
- if (load_result != 0)
+ if (!load_ok)
sp->leapcnt = 0; /* so, we're off a little */
if (*name != '\0')
{
@@ -771,7 +941,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
dstname = ++name;
name = getqzname(name, '>');
if (*name != '>')
- return -1;
+ return false;
dstlen = name - dstname;
name++;
}
@@ -781,93 +951,105 @@ tzparse(const char *name, struct state * sp, int lastditch)
name = getzname(name);
dstlen = name - dstname; /* length of DST zone name */
}
+ if (!dstlen)
+ return false;
+ charcnt += dstlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
if (*name != '\0' && *name != ',' && *name != ';')
{
name = getoffset(name, &dstoffset);
if (name == NULL)
- return -1;
+ return false;
}
else
dstoffset = stdoffset - SECSPERHOUR;
- if (*name == '\0' && load_result != 0)
+ if (*name == '\0' && !load_ok)
name = TZDEFRULESTRING;
if (*name == ',' || *name == ';')
{
struct rule start;
struct rule end;
int year;
+ int yearlim;
+ int timecnt;
pg_time_t janfirst;
- pg_time_t starttime;
- pg_time_t endtime;
++name;
if ((name = getrule(name, &start)) == NULL)
- return -1;
+ return false;
if (*name++ != ',')
- return -1;
+ return false;
if ((name = getrule(name, &end)) == NULL)
- return -1;
+ return false;
if (*name != '\0')
- return -1;
+ return false;
sp->typecnt = 2; /* standard time and DST */
/*
* Two transitions per year, from EPOCH_YEAR forward.
*/
- sp->ttis[0].tt_gmtoff = -dstoffset;
- sp->ttis[0].tt_isdst = 1;
- sp->ttis[0].tt_abbrind = stdlen + 1;
- sp->ttis[1].tt_gmtoff = -stdoffset;
- sp->ttis[1].tt_isdst = 0;
- sp->ttis[1].tt_abbrind = 0;
- atp = sp->ats;
- typep = sp->types;
+ init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1);
+ init_ttinfo(&sp->ttis[1], -stdoffset, false, 0);
+ sp->defaulttype = 0;
+ timecnt = 0;
janfirst = 0;
- sp->timecnt = 0;
- for (year = EPOCH_YEAR;
- sp->timecnt + 2 <= TZ_MAX_TIMES;
- ++year)
+ yearlim = EPOCH_YEAR + YEARSPERREPEAT;
+ for (year = EPOCH_YEAR; year < yearlim; year++)
{
- pg_time_t newfirst;
-
- starttime = transtime(janfirst, year, &start,
- stdoffset);
- endtime = transtime(janfirst, year, &end,
- dstoffset);
- if (starttime > endtime)
+ int32
+ starttime = transtime(year, &start, stdoffset),
+ endtime = transtime(year, &end, dstoffset);
+ int32
+ yearsecs = (year_lengths[isleap(year)]
+ * SECSPERDAY);
+ bool reversed = endtime < starttime;
+
+ if (reversed)
{
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
+ int32 swap = starttime;
+
+ starttime = endtime;
+ endtime = swap;
}
- else
+ if (reversed
+ || (starttime < endtime
+ && (endtime - starttime
+ < (yearsecs
+ + (stdoffset - dstoffset)))))
{
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
+ if (TZ_MAX_TIMES - 2 < timecnt)
+ break;
+ yearlim = year + YEARSPERREPEAT + 1;
+ sp->ats[timecnt] = janfirst;
+ if (increment_overflow_time
+ (&sp->ats[timecnt], starttime))
+ break;
+ sp->types[timecnt++] = reversed;
+ sp->ats[timecnt] = janfirst;
+ if (increment_overflow_time
+ (&sp->ats[timecnt], endtime))
+ break;
+ sp->types[timecnt++] = !reversed;
}
- sp->timecnt += 2;
- newfirst = janfirst;
- newfirst += year_lengths[isleap(year)] *
- SECSPERDAY;
- if (newfirst <= janfirst)
+ if (increment_overflow_time(&janfirst, yearsecs))
break;
- janfirst = newfirst;
}
+ sp->timecnt = timecnt;
+ if (!timecnt)
+ sp->typecnt = 1; /* Perpetual DST. */
}
else
{
- long theirstdoffset;
- long theirdstoffset;
- long theiroffset;
- int isdst;
+ int32 theirstdoffset;
+ int32 theirdstoffset;
+ int32 theiroffset;
+ bool isdst;
int i;
int j;
if (*name != '\0')
- return -1;
+ return false;
/*
* Initial values of theirstdoffset and theirdstoffset.
@@ -898,7 +1080,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
/*
* Initially we're assumed to be in standard time.
*/
- isdst = FALSE;
+ isdst = false;
theiroffset = theirstdoffset;
/*
@@ -945,15 +1127,12 @@ tzparse(const char *name, struct state * sp, int lastditch)
}
/*
- * Finally, fill in ttis. ttisstd and ttisgmt need not be handled.
+ * Finally, fill in ttis.
*/
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = FALSE;
- sp->ttis[0].tt_abbrind = 0;
- sp->ttis[1].tt_gmtoff = -dstoffset;
- sp->ttis[1].tt_isdst = TRUE;
- sp->ttis[1].tt_abbrind = stdlen + 1;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
sp->typecnt = 2;
+ sp->defaulttype = 0;
}
}
else
@@ -961,75 +1140,61 @@ tzparse(const char *name, struct state * sp, int lastditch)
dstlen = 0;
sp->typecnt = 1; /* only standard time */
sp->timecnt = 0;
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = 0;
- sp->ttis[0].tt_abbrind = 0;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ sp->defaulttype = 0;
}
- sp->charcnt = stdlen + 1;
- if (dstlen != 0)
- sp->charcnt += dstlen + 1;
- if ((size_t) sp->charcnt > sizeof sp->chars)
- return -1;
+ sp->charcnt = charcnt;
cp = sp->chars;
- (void) strncpy(cp, stdname, stdlen);
+ memcpy(cp, stdname, stdlen);
cp += stdlen;
*cp++ = '\0';
if (dstlen != 0)
{
- (void) strncpy(cp, dstname, dstlen);
+ memcpy(cp, dstname, dstlen);
*(cp + dstlen) = '\0';
}
- return 0;
+ return true;
}
static void
gmtload(struct state * sp)
{
- if (tzload(gmt, NULL, sp, TRUE) != 0)
- (void) tzparse(gmt, sp, TRUE);
+ if (tzload(gmt, NULL, sp, true) != 0)
+ tzparse(gmt, sp, true);
}
/*
* 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--
+ * 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,
* but it *is* desirable.)
- *
- * The unused offset argument is for the benefit of mktime variants.
*/
static struct pg_tm *
-localsub(const pg_time_t *timep, long offset,
- struct pg_tm * tmp, const pg_tz *tz)
+localsub(struct state const * sp, pg_time_t const * timep,
+ struct pg_tm * tmp)
{
- 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 == NULL)
+ return gmtsub(timep, 0, tmp);
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;
+ pg_time_t years;
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;
+ years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
+ seconds = years * AVGSECSPERYEAR;
if (t < sp->ats[0])
newt += seconds;
else
@@ -1037,31 +1202,25 @@ localsub(const pg_time_t *timep, long offset,
if (newt < sp->ats[0] ||
newt > sp->ats[sp->timecnt - 1])
return NULL; /* "cannot happen" */
- result = localsub(&newt, offset, tmp, tz);
- if (result == tmp)
+ result = localsub(sp, &newt, tmp);
+ if (result)
{
- pg_time_t newy;
+ int64 newy;
- newy = tmp->tm_year;
+ newy = result->tm_year;
if (t < sp->ats[0])
- newy -= icycles * YEARSPERREPEAT;
+ newy -= years;
else
- newy += icycles * YEARSPERREPEAT;
- tmp->tm_year = newy;
- if (tmp->tm_year != newy)
+ newy += years;
+ if (!(INT_MIN <= newy && newy <= INT_MAX))
return NULL;
+ result->tm_year = newy;
}
return result;
}
if (sp->timecnt == 0 || t < sp->ats[0])
{
- i = 0;
- while (sp->ttis[i].tt_isdst)
- if (++i >= sp->typecnt)
- {
- i = 0;
- break;
- }
+ i = sp->defaulttype;
}
else
{
@@ -1082,8 +1241,11 @@ localsub(const pg_time_t *timep, long offset,
ttisp = &sp->ttis[i];
result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
- tmp->tm_isdst = ttisp->tt_isdst;
- tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
+ if (result)
+ {
+ result->tm_isdst = ttisp->tt_isdst;
+ result->tm_zone = (char *) &sp->chars[ttisp->tt_abbrind];
+ }
return result;
}
@@ -1091,28 +1253,35 @@ localsub(const pg_time_t *timep, long offset,
struct pg_tm *
pg_localtime(const pg_time_t *timep, const pg_tz *tz)
{
- return localsub(timep, 0L, &tm, tz);
+ return localsub(&tz->state, timep, &tm);
}
/*
* gmtsub is to gmtime as localsub is to localtime.
+ *
+ * Except we have a private "struct state" for GMT, so no sp is passed in.
*/
static struct pg_tm *
-gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
+gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
{
struct pg_tm *result;
+ /* GMT timezone state data is kept here */
+ static struct state gmtmem;
+ static bool gmt_is_set = false;
+#define gmtptr (&gmtmem)
+
if (!gmt_is_set)
{
- gmt_is_set = TRUE;
+ gmt_is_set = true;
gmtload(gmtptr);
}
result = timesub(timep, offset, gmtptr, tmp);
/*
- * Could get fancy here and deliver something such as "UTC+xxxx" or
- * "UTC-xxxx" if offset is non-zero, but this is no time for a treasure
+ * Could get fancy here and deliver something such as "UT+xxxx" or
+ * "UT-xxxx" if offset is non-zero, but this is no time for a treasure
* hunt.
*/
if (offset != 0)
@@ -1126,7 +1295,7 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
struct pg_tm *
pg_gmtime(const pg_time_t *timep)
{
- return gmtsub(timep, 0L, &tm);
+ return gmtsub(timep, 0, &tm);
}
/*
@@ -1140,24 +1309,23 @@ leaps_thru_end_of(const int y)
-(leaps_thru_end_of(-(y + 1)) + 1);
}
-
static struct pg_tm *
-timesub(const pg_time_t *timep, long offset,
+timesub(const pg_time_t *timep, int32 offset,
const struct state * sp, struct pg_tm * tmp)
{
const struct lsinfo *lp;
pg_time_t tdays;
int idays; /* unsigned would be so 2003 */
- long rem;
+ int64 rem;
int y;
const int *ip;
- long corr;
- int hit;
+ int64 corr;
+ bool hit;
int i;
corr = 0;
- hit = 0;
- i = sp->leapcnt;
+ hit = false;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
while (--i >= 0)
{
lp = &sp->lsis[i];
@@ -1184,7 +1352,7 @@ timesub(const pg_time_t *timep, long offset,
}
y = EPOCH_YEAR;
tdays = *timep / SECSPERDAY;
- rem = *timep - tdays * SECSPERDAY;
+ rem = *timep % SECSPERDAY;
while (tdays < 0 || tdays >= year_lengths[isleap(y)])
{
int newy;
@@ -1193,27 +1361,21 @@ timesub(const pg_time_t *timep, long offset,
int leapdays;
tdelta = tdays / DAYSPERLYEAR;
+ if (!((!TYPE_SIGNED(pg_time_t) ||INT_MIN <= tdelta)
+ && tdelta <= INT_MAX))
+ goto out_of_range;
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;
+ goto out_of_range;
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;
- }
/*
* Given the range, we can now fearlessly cast...
@@ -1233,18 +1395,18 @@ timesub(const pg_time_t *timep, long offset,
while (idays < 0)
{
if (increment_overflow(&y, -1))
- return NULL;
+ goto out_of_range;
idays += year_lengths[isleap(y)];
}
while (idays >= year_lengths[isleap(y)])
{
idays -= year_lengths[isleap(y)];
if (increment_overflow(&y, 1))
- return NULL;
+ goto out_of_range;
}
tmp->tm_year = y;
if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
- return NULL;
+ goto out_of_range;
tmp->tm_yday = idays;
/*
@@ -1275,20 +1437,49 @@ timesub(const pg_time_t *timep, long offset,
tmp->tm_isdst = 0;
tmp->tm_gmtoff = offset;
return tmp;
+
+out_of_range:
+ errno = EOVERFLOW;
+ return NULL;
}
/*
- * Simplified normalize logic courtesy Paul Eggert.
+ * Normalize logic courtesy Paul Eggert.
*/
-static int
-increment_overflow(int *number, int delta)
+static bool
+increment_overflow(int *ip, int j)
{
- int number0;
+ int const i = *ip;
+
+ /*----------
+ * If i >= 0 there can only be overflow if i + j > INT_MAX
+ * or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
+ * If i < 0 there can only be overflow if i + j < INT_MIN
+ * or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
+ *----------
+ */
+ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
+ return true;
+ *ip += j;
+ return false;
+}
- number0 = *number;
- *number += delta;
- return (*number < number0) != (delta < 0);
+static bool
+increment_overflow_time(pg_time_t *tp, int32 j)
+{
+ /*----------
+ * This is like
+ * 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
+ * except that it does the right thing even if *tp + j would overflow.
+ *----------
+ */
+ if (!(j < 0
+ ? (TYPE_SIGNED(pg_time_t) ? time_t_min - j <= *tp : -1 - j < *tp)
+ : *tp <= time_t_max - j))
+ return true;
+ *tp += j;
+ return false;
}
/*
@@ -1296,7 +1487,7 @@ increment_overflow(int *number, int delta)
*
* *timep and *tz are input arguments, the other parameters are output values.
*
- * When the function result is 1, *boundary is set to the time_t
+ * When the function result is 1, *boundary is set to the pg_time_t
* representation of the next DST transition time after *timep,
* *before_gmtoff and *before_isdst are set to the GMT offset and isdst
* state prevailing just before that boundary (in particular, the state
@@ -1453,8 +1644,8 @@ pg_next_dst_boundary(const pg_time_t *timep,
* the meaning in use at or most recently before that time, or the meaning
* in first use after that time if the abbrev was never used before that.
*
- * On success, returns TRUE and sets *gmtoff and *isdst. If the abbreviation
- * was never used at all in this zone, returns FALSE.
+ * On success, returns true and sets *gmtoff and *isdst. If the abbreviation
+ * was never used at all in this zone, returns false.
*
* Note: abbrev is matched case-sensitively; it should be all-upper-case.
*/
@@ -1490,7 +1681,7 @@ pg_interpret_timezone_abbrev(const char *abbrev,
abbrind++;
}
if (abbrind >= sp->charcnt)
- return FALSE; /* not there! */
+ return false; /* not there! */
/*
* Unlike pg_next_dst_boundary, we needn't sweat about extrapolation
@@ -1527,7 +1718,7 @@ pg_interpret_timezone_abbrev(const char *abbrev,
{
*gmtoff = ttisp->tt_gmtoff;
*isdst = ttisp->tt_isdst;
- return TRUE;
+ return true;
}
}
@@ -1541,23 +1732,23 @@ pg_interpret_timezone_abbrev(const char *abbrev,
{
*gmtoff = ttisp->tt_gmtoff;
*isdst = ttisp->tt_isdst;
- return TRUE;
+ return true;
}
}
- return FALSE; /* hm, not actually used in any interval? */
+ return false; /* hm, not actually used in any interval? */
}
/*
* If the given timezone uses only one GMT offset, store that offset
- * into *gmtoff and return TRUE, else return FALSE.
+ * into *gmtoff and return true, else return false.
*/
bool
pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff)
{
/*
* The zone could have more than one ttinfo, if it's historically used
- * more than one abbreviation. We return TRUE as long as they all have
+ * more than one abbreviation. We return true as long as they all have
* the same gmtoff.
*/
const struct state *sp;