Commit 0171e72d authored by Tom Lane's avatar Tom Lane

Update timezone code to track the upstream changes since 2003. In particular

this adds support for 64-bit tzdata files, which is needed to support DST
calculations beyond 2038.  Add a regression test case to give some minimal
confidence that that really works.

Heikki Linnakangas
parent 2f67722d
...@@ -114,6 +114,31 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist'); ...@@ -114,6 +114,31 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist');
ERROR: time zone "america/does_not_exist" not recognized ERROR: time zone "america/does_not_exist" not recognized
SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist'; SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist';
ERROR: time zone "America/Does_not_exist" not recognized ERROR: time zone "America/Does_not_exist" not recognized
-- Daylight saving time for timestamps beyond 32-bit time_t range.
SELECT '20500710 173201 Europe/Helsinki'::timestamptz; -- DST
timestamptz
------------------------------
Sun Jul 10 07:32:01 2050 PDT
(1 row)
SELECT '20500110 173201 Europe/Helsinki'::timestamptz; -- non-DST
timestamptz
------------------------------
Mon Jan 10 07:32:01 2050 PST
(1 row)
SELECT '205000-07-10 17:32:01 Europe/Helsinki'::timestamptz; -- DST
timestamptz
--------------------------------
Thu Jul 10 07:32:01 205000 PDT
(1 row)
SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST
timestamptz
--------------------------------
Fri Jan 10 07:32:01 205000 PST
(1 row)
-- Check date conversion and date arithmetic -- Check date conversion and date arithmetic
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997');
......
...@@ -86,6 +86,13 @@ SELECT '19970710 173201' AT TIME ZONE 'America/New_York'; ...@@ -86,6 +86,13 @@ SELECT '19970710 173201' AT TIME ZONE 'America/New_York';
INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist');
SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist'; SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist';
-- Daylight saving time for timestamps beyond 32-bit time_t range.
SELECT '20500710 173201 Europe/Helsinki'::timestamptz; -- DST
SELECT '20500110 173201 Europe/Helsinki'::timestamptz; -- non-DST
SELECT '205000-07-10 17:32:01 Europe/Helsinki'::timestamptz; -- DST
SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST
-- Check date conversion and date arithmetic -- Check date conversion and date arithmetic
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
......
This is a PostgreSQL adapted version of the timezone library This is a PostgreSQL adapted version of the timezone library from:
from:
ftp://elsie.nci.nih.gov/pub/tzcode*.tar.gz ftp://elsie.nci.nih.gov/pub/tzcode*.tar.gz
The data files under data/ are an exact copy of the latest data set The code is currently synced with release 2007k. There are many cosmetic
from (and not so cosmetic) differences from the original tzcode library, but
diffs in the upstream version should usually be propagated to our version.
The data files under data/ are an exact copy of the latest data set from:
ftp://elsie.nci.nih.gov/pub/tzdata*.tar.gz ftp://elsie.nci.nih.gov/pub/tzdata*.tar.gz
......
/* /*
* This file is in the public domain, so clarified as of * This file is in the public domain, so clarified as of
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * 2006-07-17 by Arthur David Olson.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/ialloc.c,v 1.9 2007/10/26 13:30:10 tgl Exp $ * $PostgreSQL: pgsql/src/timezone/ialloc.c,v 1.10 2008/02/16 21:16:04 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
......
/* /*
* This file is in the public domain, so clarified as of * 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 * 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). * Leap second handling from Bradley White.
* POSIX-style TZ environment variable handling from Guy Harris * POSIX-style TZ environment variable handling from Guy Harris.
* (guy@auspex.com).
*/ */
/* this file needs to build in both frontend and backend contexts */ /* this file needs to build in both frontend and backend contexts */
...@@ -36,9 +35,9 @@ ...@@ -36,9 +35,9 @@
* 5. They might reference tm.TM_ZONE after calling offtime. * 5. They might reference tm.TM_ZONE after calling offtime.
* What's best to do in the above cases is open to debate; * 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 * 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. * 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 * manual page of what this "time zone abbreviation" means (doing this so
* that tzname[0] has the "normal" length of three characters). * that tzname[0] has the "normal" length of three characters).
*---------- *----------
...@@ -46,7 +45,7 @@ ...@@ -46,7 +45,7 @@
#define WILDABBR " " #define WILDABBR " "
#endif /* !defined WILDABBR */ #endif /* !defined WILDABBR */
static char wildabbr[] = "WILDABBR"; static char wildabbr[] = WILDABBR;
static const char gmt[] = "GMT"; static const char gmt[] = "GMT";
...@@ -77,18 +76,25 @@ struct rule ...@@ -77,18 +76,25 @@ struct rule
*/ */
static long detzcode(const char *codep); 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 *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 *getnum(const char *strp, int *nump, int min, int max);
static const char *getsecs(const char *strp, long *secsp); static const char *getsecs(const char *strp, long *secsp);
static const char *getoffset(const char *strp, long *offsetp); static const char *getoffset(const char *strp, long *offsetp);
static const char *getrule(const char *strp, struct rule * rulep); static const char *getrule(const char *strp, struct rule * rulep);
static void gmtload(struct state * sp); static void gmtload(struct state * sp);
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,
static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz); struct pg_tm *tmp);
static void timesub(const pg_time_t *timep, long offset, static struct pg_tm *localsub(const pg_time_t *timep, long offset,
const struct state * sp, struct pg_tm * tmp); 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, 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 */ /* GMT timezone */
static struct state gmtmem; static struct state gmtmem;
...@@ -103,7 +109,7 @@ static int gmt_is_set = 0; ...@@ -103,7 +109,7 @@ static int gmt_is_set = 0;
* Except for the strftime function, these functions [asctime, * Except for the strftime function, these functions [asctime,
* ctime, gmtime, localtime] return values in one of two static * ctime, gmtime, localtime] return values in one of two static
* objects: a broken-down time structure and an array of char. * 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; static struct pg_tm tm;
...@@ -115,18 +121,48 @@ detzcode(const char *codep) ...@@ -115,18 +121,48 @@ detzcode(const char *codep)
long result; long result;
int i; int i;
result = (codep[0] & 0x80) ? ~0L : 0L; result = (codep[0] & 0x80) ? ~0L : 0;
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
result = (result << 8) | (codep[i] & 0xff); result = (result << 8) | (codep[i] & 0xff);
return result; 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 int
tzload(const char *name, char *canonname, struct state * sp) tzload(const char *name, char *canonname, struct state * sp, int doextend)
{ {
const char *p; const char *p;
int i; int i;
int fid; 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) if (name == NULL && (name = TZDEFAULT) == NULL)
return -1; return -1;
...@@ -135,19 +171,14 @@ tzload(const char *name, char *canonname, struct state * sp) ...@@ -135,19 +171,14 @@ tzload(const char *name, char *canonname, struct state * sp)
fid = pg_open_tzfile(name, canonname); fid = pg_open_tzfile(name, canonname);
if (fid < 0) if (fid < 0)
return -1; 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 ttisstdcnt;
int ttisgmtcnt; int ttisgmtcnt;
i = read(fid, u.buf, sizeof u.buf);
if (close(fid) != 0)
return -1;
ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
...@@ -162,18 +193,19 @@ tzload(const char *name, char *canonname, struct state * sp) ...@@ -162,18 +193,19 @@ tzload(const char *name, char *canonname, struct state * sp)
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
return -1; return -1;
if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ if (nread - (p - u.buf) <
sp->timecnt * stored + /* ats */
sp->timecnt + /* types */ sp->timecnt + /* types */
sp->typecnt * (4 + 2) + /* ttinfos */ sp->typecnt * 6 + /* ttinfos */
sp->charcnt + /* chars */ sp->charcnt + /* chars */
sp->leapcnt * (4 + 4) + /* lsinfos */ sp->leapcnt * (stored + 4) + /* lsinfos */
ttisstdcnt + /* ttisstds */ ttisstdcnt + /* ttisstds */
ttisgmtcnt) /* ttisgmts */ ttisgmtcnt) /* ttisgmts */
return -1; return -1;
for (i = 0; i < sp->timecnt; ++i) for (i = 0; i < sp->timecnt; ++i)
{ {
sp->ats[i] = detzcode(p); sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p);
p += 4; p += stored;
} }
for (i = 0; i < sp->timecnt; ++i) for (i = 0; i < sp->timecnt; ++i)
{ {
...@@ -204,8 +236,8 @@ tzload(const char *name, char *canonname, struct state * sp) ...@@ -204,8 +236,8 @@ tzload(const char *name, char *canonname, struct state * sp)
struct lsinfo *lsisp; struct lsinfo *lsisp;
lsisp = &sp->lsis[i]; lsisp = &sp->lsis[i];
lsisp->ls_trans = detzcode(p); lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p);
p += 4; p += stored;
lsisp->ls_corr = detzcode(p); lsisp->ls_corr = detzcode(p);
p += 4; p += 4;
} }
...@@ -239,10 +271,127 @@ tzload(const char *name, char *canonname, struct state * sp) ...@@ -239,10 +271,127 @@ tzload(const char *name, char *canonname, struct state * sp)
return -1; 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; 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] = { static const int mon_lengths[2][MONSPERYEAR] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 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] = { ...@@ -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 * 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. * character.
*/ */
static const char * static const char *
...@@ -268,6 +417,24 @@ getzname(const char *strp) ...@@ -268,6 +417,24 @@ getzname(const char *strp)
return strp; return 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. * 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 * Check that the number is within a specified range; if it is not, return
...@@ -327,7 +494,7 @@ getsecs(const char *strp, long *secsp) ...@@ -327,7 +494,7 @@ getsecs(const char *strp, long *secsp)
if (*strp == ':') if (*strp == ':')
{ {
++strp; ++strp;
/* `SECSPERMIN' allows for leap seconds. */ /* `SECSPERMIN' allows for leap seconds. */
strp = getnum(strp, &num, 0, SECSPERMIN); strp = getnum(strp, &num, 0, SECSPERMIN);
if (strp == NULL) if (strp == NULL)
return NULL; return NULL;
...@@ -365,7 +532,7 @@ getoffset(const char *strp, long *offsetp) ...@@ -365,7 +532,7 @@ getoffset(const char *strp, long *offsetp)
/* /*
* Given a pointer into a time zone string, extract a rule in the form * 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. * If a valid rule is not found, return NULL.
* Otherwise, return a pointer to the first character not part of the rule. * 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) ...@@ -559,26 +726,47 @@ tzparse(const char *name, struct state * sp, int lastditch)
} }
else else
{ {
name = getzname(name); if (*name == '<')
stdlen = name - stdname; {
if (stdlen < 3) name++;
return -1; stdname = name;
name = getqzname(name, '>');
if (*name != '>')
return (-1);
stdlen = name - stdname;
name++;
}
else
{
name = getzname(name);
stdlen = name - stdname;
}
if (*name == '\0') if (*name == '\0')
return -1; return -1;
name = getoffset(name, &stdoffset); name = getoffset(name, &stdoffset);
if (name == NULL) if (name == NULL)
return -1; return -1;
load_result = tzload(TZDEFRULES, NULL, sp); load_result = tzload(TZDEFRULES, NULL, sp, FALSE);
} }
if (load_result != 0) if (load_result != 0)
sp->leapcnt = 0; /* so, we're off a little */ sp->leapcnt = 0; /* so, we're off a little */
if (*name != '\0') if (*name != '\0')
{ {
dstname = name; if (*name == '<')
name = getzname(name); {
dstlen = name - dstname; /* length of DST zone name */ dstname = ++name;
if (dstlen < 3) name = getqzname(name, '>');
return -1; 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 != ';') if (*name != '\0' && *name != ',' && *name != ';')
{ {
name = getoffset(name, &dstoffset); name = getoffset(name, &dstoffset);
...@@ -610,11 +798,8 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -610,11 +798,8 @@ tzparse(const char *name, struct state * sp, int lastditch)
sp->typecnt = 2; /* standard time and DST */ 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_gmtoff = -dstoffset;
sp->ttis[0].tt_isdst = 1; sp->ttis[0].tt_isdst = 1;
sp->ttis[0].tt_abbrind = stdlen + 1; sp->ttis[0].tt_abbrind = stdlen + 1;
...@@ -624,8 +809,13 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -624,8 +809,13 @@ tzparse(const char *name, struct state * sp, int lastditch)
atp = sp->ats; atp = sp->ats;
typep = sp->types; typep = sp->types;
janfirst = 0; 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, starttime = transtime(janfirst, year, &start,
stdoffset); stdoffset);
endtime = transtime(janfirst, year, &end, endtime = transtime(janfirst, year, &end,
...@@ -644,8 +834,13 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -644,8 +834,13 @@ tzparse(const char *name, struct state * sp, int lastditch)
*atp++ = endtime; *atp++ = endtime;
*typep++ = 1; /* DST ends */ *typep++ = 1; /* DST ends */
} }
janfirst += year_lengths[isleap(year)] * sp->timecnt += 2;
newfirst = janfirst;
newfirst += year_lengths[isleap(year)] *
SECSPERDAY; SECSPERDAY;
if (newfirst <= janfirst)
break;
janfirst = newfirst;
} }
} }
else else
...@@ -776,7 +971,7 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -776,7 +971,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
static void static void
gmtload(struct state * sp) gmtload(struct state * sp)
{ {
if (tzload(gmt, NULL, sp) != 0) if (tzload(gmt, NULL, sp, TRUE) != 0)
(void) tzparse(gmt, sp, TRUE); (void) tzparse(gmt, sp, TRUE);
} }
...@@ -784,20 +979,63 @@ gmtload(struct state * sp) ...@@ -784,20 +979,63 @@ gmtload(struct state * sp)
/* /*
* The easy way to behave "as if no library function calls" localtime * 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 * 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.) * but it *is* desirable.)
* *
* The unused offset argument is for the benefit of mktime variants. * The unused offset argument is for the benefit of mktime variants.
*/ */
static void static struct pg_tm *
localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz) localsub(const pg_time_t *timep, long offset,
struct pg_tm *tmp, const pg_tz *tz)
{ {
const struct state *sp; const struct state *sp;
const struct ttinfo *ttisp; const struct ttinfo *ttisp;
int i; int i;
struct pg_tm *result;
const pg_time_t t = *timep; const pg_time_t t = *timep;
sp = &tz->state; 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]) if (sp->timecnt == 0 || t < sp->ats[0])
{ {
i = 0; i = 0;
...@@ -810,39 +1048,49 @@ localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *t ...@@ -810,39 +1048,49 @@ localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *t
} }
else else
{ {
for (i = 1; i < sp->timecnt; ++i) int lo = 1;
if (t < sp->ats[i]) int hi = sp->timecnt;
break;
i = sp->types[i - 1]; 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]; 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_isdst = ttisp->tt_isdst;
tmp->tm_zone = &sp->chars[ttisp->tt_abbrind]; tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
return result;
} }
struct pg_tm * struct pg_tm *
pg_localtime(const pg_time_t *timep, const pg_tz *tz) pg_localtime(const pg_time_t *timep, const pg_tz *tz)
{ {
localsub(timep, 0L, &tm, tz); return localsub(timep, 0L, &tm, tz);
return &tm;
} }
/* /*
* gmtsub is to gmtime as localsub is to localtime. * gmtsub is to gmtime as localsub is to localtime.
*/ */
static void static struct pg_tm *
gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp) gmtsub(const pg_time_t *timep, long offset, struct pg_tm *tmp)
{ {
struct pg_tm *result;
if (!gmt_is_set) if (!gmt_is_set)
{ {
gmt_is_set = TRUE; gmt_is_set = TRUE;
gmtload(gmtptr); 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 * 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) ...@@ -853,28 +1101,37 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
tmp->tm_zone = wildabbr; tmp->tm_zone = wildabbr;
else else
tmp->tm_zone = gmtptr->chars; tmp->tm_zone = gmtptr->chars;
return result;
} }
struct pg_tm * struct pg_tm *
pg_gmtime(const pg_time_t *timep) pg_gmtime(const pg_time_t *timep)
{ {
gmtsub(timep, 0L, &tm); return gmtsub(timep, 0L, &tm);
return &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, 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; const struct lsinfo *lp;
pg_time_t tdays;
/* expand days to 64 bits to support full Julian-day range */ int idays; /* unsigned would be so 2003 */
int64 days;
int idays;
long rem; long rem;
int y; int y;
int yleap;
const int *ip; const int *ip;
long corr; long corr;
int hit; int hit;
...@@ -907,74 +1164,111 @@ timesub(const pg_time_t *timep, long offset, ...@@ -907,74 +1164,111 @@ timesub(const pg_time_t *timep, long offset,
break; break;
} }
} }
days = *timep / SECSPERDAY; y = EPOCH_YEAR;
rem = *timep % SECSPERDAY; tdays = *timep / SECSPERDAY;
#ifdef mc68k rem = *timep - tdays * SECSPERDAY;
if (*timep == 0x80000000) while (tdays < 0 || tdays >= year_lengths[isleap(y)])
{ {
/* int newy;
* A 3B1 muffs the division on the most negative number. pg_time_t tdelta;
*/ int idelta;
days = -24855; int leapdays;
rem = -11648;
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) while (rem < 0)
{ {
rem += SECSPERDAY; rem += SECSPERDAY;
--days; --idays;
} }
while (rem >= SECSPERDAY) while (rem >= SECSPERDAY)
{ {
rem -= SECSPERDAY; rem -= SECSPERDAY;
++days; ++idays;
} }
tmp->tm_hour = (int) (rem / SECSPERHOUR); while (idays < 0)
rem = rem % SECSPERHOUR; {
tmp->tm_min = (int) (rem / SECSPERMIN); 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 * The "extra" mods below avoid overflow problems.
* "... ??:59:60" et seq.
*/ */
tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; tmp->tm_wday = EPOCH_WDAY +
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); ((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) if (tmp->tm_wday < 0)
tmp->tm_wday += DAYSPERWEEK; 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 * A positive leap second requires a special representation. This uses
* assumptions as Postgres' Julian-date routines about the placement of * "... ??:59:60" et seq.
* 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) tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
while (days < 0 || days >= (int64) year_lengths[yleap = isleap(y)]) ip = mon_lengths[isleap(y)];
{ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
int newy; idays -= ip[tmp->tm_mon];
tmp->tm_mday = (int) (idays + 1);
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_isdst = 0; tmp->tm_isdst = 0;
tmp->tm_gmtoff = offset; 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, ...@@ -1027,6 +1321,48 @@ pg_next_dst_boundary(const pg_time_t *timep,
*before_isdst = ttisp->tt_isdst; *before_isdst = ttisp->tt_isdst;
return 0; 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]) if (t > sp->ats[sp->timecnt - 1])
{ {
/* No known transition >= t, so use last known segment's type */ /* No known transition >= t, so use last known segment's type */
...@@ -1058,9 +1394,20 @@ pg_next_dst_boundary(const pg_time_t *timep, ...@@ -1058,9 +1394,20 @@ pg_next_dst_boundary(const pg_time_t *timep,
return 1; return 1;
} }
/* Else search to find the containing segment */ /* Else search to find the containing segment */
for (i = 1; i < sp->timecnt; ++i) {
if (t <= sp->ats[i]) int lo = 1;
break; 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]; j = sp->types[i - 1];
ttisp = &sp->ttis[j]; ttisp = &sp->ttis[j];
*before_gmtoff = ttisp->tt_gmtoff; *before_gmtoff = ttisp->tt_gmtoff;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.58 2008/02/11 19:55:11 mha Exp $ * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.59 2008/02/16 21:16:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -287,7 +287,7 @@ score_timezone(const char *tzname, struct tztry * tt) ...@@ -287,7 +287,7 @@ score_timezone(const char *tzname, struct tztry * tt)
* Load timezone directly. Don't use pg_tzset, because we don't want all * Load timezone directly. Don't use pg_tzset, because we don't want all
* timezones loaded in the cache at startup. * timezones loaded in the cache at startup.
*/ */
if (tzload(tzname, NULL, &tz.state) != 0) if (tzload(tzname, NULL, &tz.state, TRUE) != 0)
{ {
if (tzname[0] == ':' || tzparse(tzname, &tz.state, FALSE) != 0) if (tzname[0] == ':' || tzparse(tzname, &tz.state, FALSE) != 0)
{ {
...@@ -1191,7 +1191,7 @@ pg_tzset(const char *name) ...@@ -1191,7 +1191,7 @@ pg_tzset(const char *name)
return &tzp->tz; return &tzp->tz;
} }
if (tzload(uppername, canonname, &tzstate) != 0) if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
{ {
if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0) if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
{ {
...@@ -1463,7 +1463,8 @@ pg_tzenumerate_next(pg_tzenum *dir) ...@@ -1463,7 +1463,8 @@ pg_tzenumerate_next(pg_tzenum *dir)
* Load this timezone using tzload() not pg_tzset(), so we don't fill * Load this timezone using tzload() not pg_tzset(), so we don't fill
* the cache * the cache
*/ */
if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state) != 0) if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
TRUE) != 0)
{ {
/* Zone could not be loaded, ignore it */ /* Zone could not be loaded, ignore it */
continue; continue;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.21 2008/01/01 19:46:01 momjian Exp $ * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.22 2008/02/16 21:16:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,6 +43,8 @@ struct state ...@@ -43,6 +43,8 @@ struct state
int timecnt; int timecnt;
int typecnt; int typecnt;
int charcnt; int charcnt;
int goback;
int goahead;
pg_time_t ats[TZ_MAX_TIMES]; pg_time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES]; struct ttinfo ttis[TZ_MAX_TYPES];
...@@ -64,7 +66,8 @@ struct pg_tz ...@@ -64,7 +66,8 @@ struct pg_tz
extern int pg_open_tzfile(const char *name, char *canonname); extern int pg_open_tzfile(const char *name, char *canonname);
/* in localtime.c */ /* in localtime.c */
extern int tzload(const char *name, char *canonname, struct state * sp); extern int tzload(const char *name, char *canonname, struct state * sp,
int doextend);
extern int tzparse(const char *name, struct state * sp, int lastditch); extern int tzparse(const char *name, struct state * sp, int lastditch);
#endif /* _PGTZ_H */ #endif /* _PGTZ_H */
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
/* /*
* This file is in the public domain, so clarified as of * 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 * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/private.h,v 1.11 2005/02/23 04:34:21 momjian Exp $ * $PostgreSQL: pgsql/src/timezone/private.h,v 1.12 2008/02/16 21:16:04 tgl Exp $
*/ */
/* /*
...@@ -17,12 +17,13 @@ ...@@ -17,12 +17,13 @@
* Thank you! * Thank you!
*/ */
#include <limits.h> /* for CHAR_BIT */ #include <limits.h> /* for CHAR_BIT et al. */
#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ #include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
#include <unistd.h> /* for F_OK and R_OK */ #include <unistd.h> /* for F_OK and R_OK */
#include "pgtime.h" #include "pgtime.h"
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
#ifndef WIFEXITED #ifndef WIFEXITED
#define WIFEXITED(status) (((status) & 0xff) == 0) #define WIFEXITED(status) (((status) & 0xff) == 0)
...@@ -34,22 +35,6 @@ ...@@ -34,22 +35,6 @@
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9) #define is_digit(c) ((unsigned)(c) - '0' <= 9)
/*
* SunOS 4.1.1 headers lack EXIT_SUCCESS.
*/
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif /* !defined EXIT_SUCCESS */
/*
* SunOS 4.1.1 headers lack EXIT_FAILURE.
*/
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif /* !defined EXIT_FAILURE */
/* /*
* SunOS 4.1.1 libraries lack remove. * SunOS 4.1.1 libraries lack remove.
*/ */
...@@ -70,7 +55,7 @@ extern char *imalloc(int n); ...@@ -70,7 +55,7 @@ extern char *imalloc(int n);
extern void *irealloc(void *pointer, int size); extern void *irealloc(void *pointer, int size);
extern void icfree(char *pointer); extern void icfree(char *pointer);
extern void ifree(char *pointer); extern void ifree(char *pointer);
extern char *scheck(const char *string, const char *format); extern const char *scheck(const char *string, const char *format);
/* /*
...@@ -93,6 +78,15 @@ extern char *scheck(const char *string, const char *format); ...@@ -93,6 +78,15 @@ extern char *scheck(const char *string, const char *format);
#define TYPE_SIGNED(type) (((type) -1) < 0) #define TYPE_SIGNED(type) (((type) -1) < 0)
#endif /* !defined TYPE_SIGNED */ #endif /* !defined TYPE_SIGNED */
/*
* Since the definition of TYPE_INTEGRAL contains floating point numbers,
* it cannot be used in preprocessor directives.
*/
#ifndef TYPE_INTEGRAL
#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
#endif /* !defined TYPE_INTEGRAL */
#ifndef INT_STRLEN_MAXIMUM #ifndef INT_STRLEN_MAXIMUM
/* /*
* 302 / 1000 is log10(2.0) rounded up. * 302 / 1000 is log10(2.0) rounded up.
...@@ -107,6 +101,26 @@ extern char *scheck(const char *string, const char *format); ...@@ -107,6 +101,26 @@ extern char *scheck(const char *string, const char *format);
#undef _ #undef _
#define _(msgid) (msgid) #define _(msgid) (msgid)
#ifndef YEARSPERREPEAT
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#endif /* !defined YEARSPERREPEAT */
/*
** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
*/
#ifndef AVGSECSPERYEAR
#define AVGSECSPERYEAR 31556952L
#endif /* !defined AVGSECSPERYEAR */
#ifndef SECSPERREPEAT
#define SECSPERREPEAT ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR)
#endif /* !defined SECSPERREPEAT */
#ifndef SECSPERREPEAT_BITS
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */
/* /*
* UNIX was a registered trademark of The Open Group in 2003. * UNIX was a registered trademark of The Open Group in 2003.
*/ */
......
/* /*
* This file is in the public domain, so clarified as of * This file is in the public domain, so clarified as of
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * 2006-07-17 by Arthur David Olson.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/scheck.c,v 1.8 2007/10/26 13:30:10 tgl Exp $ * $PostgreSQL: pgsql/src/timezone/scheck.c,v 1.9 2008/02/16 21:16:04 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
...@@ -11,18 +11,17 @@ ...@@ -11,18 +11,17 @@
#include "private.h" #include "private.h"
char * const char *
scheck(const char *string, const char *format) scheck(const char *string, const char *format)
{ {
char *fbuf; char *fbuf;
const char *fp; const char *fp;
char *tp; char *tp;
int c; int c;
char *result; const char *result;
char dummy; char dummy;
static char nada;
result = &nada; result = "";
if (string == NULL || format == NULL) if (string == NULL || format == NULL)
return result; return result;
fbuf = imalloc((int) (2 * strlen(format) + 4)); fbuf = imalloc((int) (2 * strlen(format) + 4));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* duplicated in all such forms and that any documentation, * duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such * advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed * distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the * by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived * University may not be used to endorse or promote products derived
* from this software without specific prior written permission. * from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.11 2006/07/14 14:52:27 momjian Exp $ * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.12 2008/02/16 21:16:04 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -92,6 +92,7 @@ static char *_add(const char *, char *, const char *); ...@@ -92,6 +92,7 @@ static char *_add(const char *, char *, const char *);
static char *_conv(int, const char *, char *, const char *); static char *_conv(int, const char *, char *, const char *);
static char *_fmt(const char *, const struct pg_tm *, char *, static char *_fmt(const char *, const struct pg_tm *, char *,
const char *, int *); const char *, int *);
static char * _yconv(int, int, int, int, char *, const char *);
#define IN_NONE 0 #define IN_NONE 0
#define IN_SOME 1 #define IN_SOME 1
...@@ -160,8 +161,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -160,8 +161,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
* ...whereas now POSIX 1003.2 calls for something * ...whereas now POSIX 1003.2 calls for something
* completely different. (ado, 1993-05-24) * completely different. (ado, 1993-05-24)
*/ */
pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
"%02d", pt, ptlim); pt, ptlim);
continue; continue;
case 'c': case 'c':
{ {
...@@ -213,7 +214,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -213,7 +214,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
* This used to be... _conv(t->tm_hour % 12 ? t->tm_hour * This used to be... _conv(t->tm_hour % 12 ? t->tm_hour
* % 12 : 12, 2, ' '); ...and has been changed to the * % 12 : 12, 2, ' '); ...and has been changed to the
* below to match SunOS 4.1.1 and Arnold Robbins' strftime * below to match SunOS 4.1.1 and Arnold Robbins' strftime
* version 3.0. That is, "%k" and "%l" have been swapped. * version 3.0. That is, "%k" and "%l" have been swapped.
* (ado, 1993-05-24) * (ado, 1993-05-24)
*/ */
pt = _conv(t->tm_hour, "%2d", pt, ptlim); pt = _conv(t->tm_hour, "%2d", pt, ptlim);
...@@ -289,7 +290,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -289,7 +290,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
case 'G': /* ISO 8601 year (four digits) */ case 'G': /* ISO 8601 year (four digits) */
case 'g': /* ISO 8601 year (two digits) */ case 'g': /* ISO 8601 year (two digits) */
/* /*
* From Arnold Robbins' strftime version 3.0: "the week number of the * From Arnold Robbins' strftime version 3.0: "the week number of the
* year (the first Monday as the first day of week 1) as a decimal number * year (the first Monday as the first day of week 1) as a decimal number
* (01-53)." * (01-53)."
* (ado, 1993-05-24) * (ado, 1993-05-24)
...@@ -302,17 +303,19 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -302,17 +303,19 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
* might also contain days from the previous year and the week before week * might also contain days from the previous year and the week before week
* 01 of a year is the last week (52 or 53) of the previous year even if * 01 of a year is the last week (52 or 53) of the previous year even if
* it contains days from the new year. A week starts with Monday (day 1) * it contains days from the new year. A week starts with Monday (day 1)
* and ends with Sunday (day 7). For example, the first week of the year * and ends with Sunday (day 7). For example, the first week of the year
* 1997 lasts from 1996-12-30 to 1997-01-05..." * 1997 lasts from 1996-12-30 to 1997-01-05..."
* (ado, 1996-01-02) * (ado, 1996-01-02)
*/ */
{ {
int year; int year;
int base;
int yday; int yday;
int wday; int wday;
int w; int w;
year = t->tm_year + TM_YEAR_BASE; year = t->tm_year;
base = TM_YEAR_BASE;
yday = t->tm_yday; yday = t->tm_yday;
wday = t->tm_wday; wday = t->tm_wday;
for (;;) for (;;)
...@@ -321,7 +324,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -321,7 +324,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
int bot; int bot;
int top; int top;
len = isleap(year) ? len = isleap_sum(year, base) ?
DAYSPERLYEAR : DAYSPERLYEAR :
DAYSPERNYEAR; DAYSPERNYEAR;
...@@ -342,7 +345,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -342,7 +345,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
top += len; top += len;
if (yday >= top) if (yday >= top)
{ {
++year; ++base;
w = 1; w = 1;
break; break;
} }
...@@ -352,8 +355,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -352,8 +355,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
DAYSPERWEEK); DAYSPERWEEK);
break; break;
} }
--year; --base;
yday += isleap(year) ? yday += isleap_sum(year, base) ?
DAYSPERLYEAR : DAYSPERLYEAR :
DAYSPERNYEAR; DAYSPERNYEAR;
} }
...@@ -363,11 +366,11 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -363,11 +366,11 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
else if (*format == 'g') else if (*format == 'g')
{ {
*warnp = IN_ALL; *warnp = IN_ALL;
pt = _conv(year % 100, "%02d", pt = _yconv(year, base, 0, 1,
pt, ptlim); pt, ptlim);
} }
else else
pt = _conv(year, "%04d", pt = _yconv(year, base, 1, 1,
pt, ptlim); pt, ptlim);
} }
continue; continue;
...@@ -405,12 +408,12 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -405,12 +408,12 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
continue; continue;
case 'y': case 'y':
*warnp = IN_ALL; *warnp = IN_ALL;
pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
"%02d", pt, ptlim); pt, ptlim);
continue; continue;
case 'Y': case 'Y':
pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
pt, ptlim); pt, ptlim);
continue; continue;
case 'Z': case 'Z':
if (t->tm_zone != NULL) if (t->tm_zone != NULL)
...@@ -480,3 +483,43 @@ _add(const char *str, char *pt, const char *ptlim) ...@@ -480,3 +483,43 @@ _add(const char *str, char *pt, const char *ptlim)
++pt; ++pt;
return pt; return pt;
} }
/*
* POSIX and the C Standard are unclear or inconsistent about
* what %C and %y do if the year is negative or exceeds 9999.
* Use the convention that %C concatenated with %y yields the
* same output as %Y, and that %Y contains at least 4 bytes,
* with more only if necessary.
*/
static char *
_yconv(const int a, const int b, const int convert_top,
const int convert_yy, char *pt, const char * const ptlim)
{
int lead;
int trail;
#define DIVISOR 100
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
if (trail < 0 && lead > 0)
{
trail += DIVISOR;
--lead;
}
else if (lead < 0 && trail > 0)
{
trail -= DIVISOR;
++lead;
}
if (convert_top)
{
if (lead == 0 && trail < 0)
pt = _add("-0", pt, ptlim);
else pt = _conv(lead, "%02d", pt, ptlim);
}
if (convert_yy)
pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
return pt;
}
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
/* /*
* This file is in the public domain, so clarified as of * 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 * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/tzfile.h,v 1.6 2005/10/15 02:49:51 momjian Exp $ * $PostgreSQL: pgsql/src/timezone/tzfile.h,v 1.7 2008/02/16 21:16:04 tgl Exp $
*/ */
/* /*
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
struct tzhead struct tzhead
{ {
char tzh_magic[4]; /* TZ_MAGIC */ char tzh_magic[4]; /* TZ_MAGIC */
char tzh_reserved[16]; /* reserved for future use */ char tzh_version[1]; /* '\0' or '2' as of 2005 */
char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_leapcnt[4]; /* coded number of leap seconds */
...@@ -69,17 +70,21 @@ struct tzhead ...@@ -69,17 +70,21 @@ struct tzhead
*/ */
/* /*
* In the current implementation, "tzset()" refuses to deal with files that * If tzh_version is '2' or greater, the above is followed by a second instance
* exceed any of the limits below. * of tzhead and a second instance of the data in which each coded transition
* time uses 8 rather than 4 chars,
* then a POSIX-TZ-environment-variable-style string for use in handling
* instants after the last transition time stored in the file
* (with nothing between the newlines if there is no POSIX representation for
* such instants).
*/ */
/* /*
* The TZ_MAX_TIMES value below is enough to handle a bit more than a * In the current implementation, "tzset()" refuses to deal with files that
* year's worth of solar time (corrected daily to the nearest second) or * exceed any of the limits below.
* 138 years of Pacific Presidential Election time
* (where there are three time zone transitions every fourth year).
*/ */
#define TZ_MAX_TIMES 370
#define TZ_MAX_TIMES 1200
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
...@@ -124,11 +129,20 @@ struct tzhead ...@@ -124,11 +129,20 @@ struct tzhead
#define EPOCH_YEAR 1970 #define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY #define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/* /*
* Accurate only for the past couple of centuries; * Since everything in isleap is modulo 400 (or a factor of 400), we know that
* that will probably do. * isleap(y) == isleap(y % 400)
* and so
* isleap(a + b) == isleap((a + b) % 400)
* or
* isleap(a + b) == isleap(a % 400 + b % 400)
* This is true even if % means modulo rather than Fortran remainder
* (which is allowed by C89 but not C99).
* We use this to avoid addition overflow problems.
*/ */
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined TZFILE_H */ #endif /* !defined TZFILE_H */
/* /*
* This file is in the public domain, so clarified as of * This file is in the public domain, so clarified as of
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * 2006-07-17 by Arthur David Olson.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/zic.c,v 1.22 2007/10/26 13:30:10 tgl Exp $ * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.23 2008/02/16 21:16:04 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
...@@ -21,6 +21,14 @@ extern char *optarg; ...@@ -21,6 +21,14 @@ extern char *optarg;
#include "pgtz.h" #include "pgtz.h"
#include "tzfile.h" #include "tzfile.h"
#define ZIC_VERSION '2'
typedef int64 zic_t;
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifdef HAVE_SYS_STAT_H #ifdef HAVE_SYS_STAT_H
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
...@@ -33,11 +41,11 @@ extern char *optarg; ...@@ -33,11 +41,11 @@ extern char *optarg;
#endif #endif
#endif #endif
static char elsieid[] = "@(#)zic.c 7.115"; static char elsieid[] = "@(#)zic.c 8.17";
/* /*
* On some ancient hosts, predicates like `isspace(C)' are defined * On some ancient hosts, predicates like `isspace(C)' are defined
* only if isascii(C) || C == EOF. Modern hosts obey the C Standard, * only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
* which says they are defined only if C == ((unsigned char) C) || C == EOF. * which says they are defined only if C == ((unsigned char) C) || C == EOF.
* Neither the C Standard nor Posix require that `isascii' exist. * Neither the C Standard nor Posix require that `isascii' exist.
* For portability, we check both ancient and modern requirements. * For portability, we check both ancient and modern requirements.
...@@ -48,6 +56,11 @@ static char elsieid[] = "@(#)zic.c 7.115"; ...@@ -48,6 +56,11 @@ static char elsieid[] = "@(#)zic.c 7.115";
#define isascii(x) 1 #define isascii(x) 1
#endif #endif
#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long))
#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */
#define end(cp) (strchr((cp), '\0'))
struct rule struct rule
{ {
const char *r_filename; const char *r_filename;
...@@ -57,6 +70,8 @@ struct rule ...@@ -57,6 +70,8 @@ struct rule
int r_loyear; /* for example, 1986 */ int r_loyear; /* for example, 1986 */
int r_hiyear; /* for example, 1986 */ int r_hiyear; /* for example, 1986 */
const char *r_yrtype; const char *r_yrtype;
int r_lowasnum;
int r_hiwasnum;
int r_month; /* 0..11 */ int r_month; /* 0..11 */
...@@ -73,7 +88,7 @@ struct rule ...@@ -73,7 +88,7 @@ struct rule
const char *r_abbrvar; /* variable part of abbreviation */ const char *r_abbrvar; /* variable part of abbreviation */
int r_todo; /* a rule to do (used in outzone) */ int r_todo; /* a rule to do (used in outzone) */
pg_time_t r_temp; /* used in outzone */ zic_t r_temp; /* used in outzone */
}; };
/* /*
...@@ -100,7 +115,7 @@ struct zone ...@@ -100,7 +115,7 @@ struct zone
int z_nrules; int z_nrules;
struct rule z_untilrule; struct rule z_untilrule;
pg_time_t z_untiltime; zic_t z_untiltime;
}; };
extern int link(const char *fromname, const char *toname); extern int link(const char *fromname, const char *toname);
...@@ -114,7 +129,7 @@ static int ciequal(const char *ap, const char *bp); ...@@ -114,7 +129,7 @@ static int ciequal(const char *ap, const char *bp);
static void convert(long val, char *buf); static void convert(long val, char *buf);
static void dolink(const char *fromfile, const char *tofile); static void dolink(const char *fromfile, const char *tofile);
static void doabbr(char *abbr, const char *format, static void doabbr(char *abbr, const char *format,
const char *letters, int isdst); const char *letters, int isdst, int doquotes);
static void eat(const char *name, int num); static void eat(const char *name, int num);
static void eats(const char *name, int num, static void eats(const char *name, int num,
const char *rname, int rnum); const char *rname, int rnum);
...@@ -148,20 +163,23 @@ static void rulesub(struct rule * rp, ...@@ -148,20 +163,23 @@ static void rulesub(struct rule * rp,
static void setboundaries(void); static void setboundaries(void);
static pg_time_t tadd(const pg_time_t t1, long t2); static pg_time_t tadd(const pg_time_t t1, long t2);
static void usage(void); static void usage(void);
static void writezone(const char *name); static void writezone(const char *name, const char *string);
static int yearistype(int year, const char *type); static int yearistype(int year, const char *type);
static int charcnt; static int charcnt;
static int errors; static int errors;
static const char *filename; static const char *filename;
static int leapcnt; static int leapcnt;
static int leapseen;
static int leapminyear;
static int leapmaxyear;
static int linenum; static int linenum;
static pg_time_t max_time; static int max_abbrvar_len;
static int max_format_len;
static zic_t max_time;
static int max_year; static int max_year;
static int max_year_representable; static zic_t min_time;
static pg_time_t min_time;
static int min_year; static int min_year;
static int min_year_representable;
static int noise; static int noise;
static const char *rfilename; static const char *rfilename;
static int rlinenum; static int rlinenum;
...@@ -352,7 +370,7 @@ static const int len_years[2] = { ...@@ -352,7 +370,7 @@ static const int len_years[2] = {
static struct attype static struct attype
{ {
pg_time_t at; zic_t at;
unsigned char type; unsigned char type;
} attypes[TZ_MAX_TIMES]; } attypes[TZ_MAX_TIMES];
static long gmtoffs[TZ_MAX_TYPES]; static long gmtoffs[TZ_MAX_TYPES];
...@@ -361,7 +379,7 @@ static unsigned char abbrinds[TZ_MAX_TYPES]; ...@@ -361,7 +379,7 @@ static unsigned char abbrinds[TZ_MAX_TYPES];
static char ttisstds[TZ_MAX_TYPES]; static char ttisstds[TZ_MAX_TYPES];
static char ttisgmts[TZ_MAX_TYPES]; static char ttisgmts[TZ_MAX_TYPES];
static char chars[TZ_MAX_CHARS]; static char chars[TZ_MAX_CHARS];
static pg_time_t trans[TZ_MAX_LEAPS]; static zic_t trans[TZ_MAX_LEAPS];
static long corr[TZ_MAX_LEAPS]; static long corr[TZ_MAX_LEAPS];
static char roll[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS];
...@@ -378,7 +396,7 @@ memcheck(char *ptr) ...@@ -378,7 +396,7 @@ memcheck(char *ptr)
(void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"),
progname, e); progname, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return ptr; return ptr;
} }
...@@ -438,9 +456,11 @@ warning(const char *string) ...@@ -438,9 +456,11 @@ warning(const char *string)
static void static void
usage(void) usage(void)
{ {
(void) fprintf(stderr, _("%s: usage is %s [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"), (void) fprintf(stderr, _("%s: usage is %s \
[ --version ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
progname, progname); progname, progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static const char *psxrules; static const char *psxrules;
...@@ -448,7 +468,6 @@ static const char *lcltime; ...@@ -448,7 +468,6 @@ static const char *lcltime;
static const char *directory; static const char *directory;
static const char *leapsec; static const char *leapsec;
static const char *yitcommand; static const char *yitcommand;
static int sflag = FALSE;
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
...@@ -461,11 +480,16 @@ main(int argc, char *argv[]) ...@@ -461,11 +480,16 @@ main(int argc, char *argv[])
(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif /* !WIN32 */ #endif /* !WIN32 */
progname = argv[0]; progname = argv[0];
if (TYPE_BIT(zic_t) < 64) {
(void) fprintf(stderr, "%s: %s\n", progname,
_("wild compilation-time specification of zic_t"));
exit(EXIT_FAILURE);
}
for (i = 1; i < argc; ++i) for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) if (strcmp(argv[i], "--version") == 0)
{ {
(void) printf("%s\n", elsieid); (void) printf("%s\n", elsieid);
(void) exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1)
switch (c) switch (c)
...@@ -480,7 +504,7 @@ main(int argc, char *argv[]) ...@@ -480,7 +504,7 @@ main(int argc, char *argv[])
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: More than one -d option specified\n"), _("%s: More than one -d option specified\n"),
progname); progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'l': case 'l':
...@@ -491,7 +515,7 @@ main(int argc, char *argv[]) ...@@ -491,7 +515,7 @@ main(int argc, char *argv[])
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: More than one -l option specified\n"), _("%s: More than one -l option specified\n"),
progname); progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'p': case 'p':
...@@ -502,7 +526,7 @@ main(int argc, char *argv[]) ...@@ -502,7 +526,7 @@ main(int argc, char *argv[])
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: More than one -p option specified\n"), _("%s: More than one -p option specified\n"),
progname); progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'y': case 'y':
...@@ -513,7 +537,7 @@ main(int argc, char *argv[]) ...@@ -513,7 +537,7 @@ main(int argc, char *argv[])
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: More than one -y option specified\n"), _("%s: More than one -y option specified\n"),
progname); progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'L': case 'L':
...@@ -524,14 +548,14 @@ main(int argc, char *argv[]) ...@@ -524,14 +548,14 @@ main(int argc, char *argv[])
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: More than one -L option specified\n"), _("%s: More than one -L option specified\n"),
progname); progname);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'v': case 'v':
noise = TRUE; noise = TRUE;
break; break;
case 's': case 's':
sflag = TRUE; (void) printf("%s: -s ignored\n", progname);
break; break;
} }
if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
...@@ -552,7 +576,7 @@ main(int argc, char *argv[]) ...@@ -552,7 +576,7 @@ main(int argc, char *argv[])
for (i = optind; i < argc; ++i) for (i = optind; i < argc; ++i)
infile(argv[i]); infile(argv[i]);
if (errors) if (errors)
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
associate(); associate();
for (i = 0; i < nzones; i = j) for (i = 0; i < nzones; i = j)
{ {
...@@ -571,6 +595,11 @@ main(int argc, char *argv[]) ...@@ -571,6 +595,11 @@ main(int argc, char *argv[])
{ {
eat(links[i].l_filename, links[i].l_linenum); eat(links[i].l_filename, links[i].l_linenum);
dolink(links[i].l_from, links[i].l_to); dolink(links[i].l_from, links[i].l_to);
if (noise)
for (j = 0; j < nlinks; ++j)
if (strcmp(links[i].l_to,
links[j].l_from) == 0)
warning(_("link to link"));
} }
if (lcltime != NULL) if (lcltime != NULL)
{ {
...@@ -586,26 +615,26 @@ main(int argc, char *argv[]) ...@@ -586,26 +615,26 @@ main(int argc, char *argv[])
} }
static void static void
dolink(const char *fromfile, const char *tofile) dolink(const char *fromfield, const char *tofield)
{ {
char *fromname; char *fromname;
char *toname; char *toname;
if (fromfile[0] == '/') if (fromfield[0] == '/')
fromname = ecpyalloc(fromfile); fromname = ecpyalloc(fromfield);
else else
{ {
fromname = ecpyalloc(directory); fromname = ecpyalloc(directory);
fromname = ecatalloc(fromname, "/"); fromname = ecatalloc(fromname, "/");
fromname = ecatalloc(fromname, fromfile); fromname = ecatalloc(fromname, fromfield);
} }
if (tofile[0] == '/') if (tofield[0] == '/')
toname = ecpyalloc(tofile); toname = ecpyalloc(tofield);
else else
{ {
toname = ecpyalloc(directory); toname = ecpyalloc(directory);
toname = ecatalloc(toname, "/"); toname = ecatalloc(toname, "/");
toname = ecatalloc(toname, tofile); toname = ecatalloc(toname, tofield);
} }
/* /*
...@@ -619,7 +648,7 @@ dolink(const char *fromfile, const char *tofile) ...@@ -619,7 +648,7 @@ dolink(const char *fromfile, const char *tofile)
int result; int result;
if (mkdirs(toname) != 0) if (mkdirs(toname) != 0)
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
result = link(fromname, toname); result = link(fromname, toname);
#ifdef HAVE_SYMLINK #ifdef HAVE_SYMLINK
...@@ -627,12 +656,12 @@ dolink(const char *fromfile, const char *tofile) ...@@ -627,12 +656,12 @@ dolink(const char *fromfile, const char *tofile)
access(fromname, F_OK) == 0 && access(fromname, F_OK) == 0 &&
!itsdir(fromname)) !itsdir(fromname))
{ {
const char *s = tofile; const char *s = tofield;
char *symlinkcontents = NULL; char *symlinkcontents = NULL;
while ((s = strchr(s + 1, '/')) != NULL) while ((s = strchr(s + 1, '/')) != NULL)
symlinkcontents = ecatalloc(symlinkcontents, "../"); symlinkcontents = ecatalloc(symlinkcontents, "../");
symlinkcontents = ecatalloc(symlinkcontents, fromfile); symlinkcontents = ecatalloc(symlinkcontents, fromfield);
result = symlink(symlinkcontents, toname); result = symlink(symlinkcontents, toname);
if (result == 0) if (result == 0)
...@@ -647,52 +676,24 @@ dolink(const char *fromfile, const char *tofile) ...@@ -647,52 +676,24 @@ dolink(const char *fromfile, const char *tofile)
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: Cannot link from %s to %s: %s\n"), _("%s: Cannot link from %s to %s: %s\n"),
progname, fromname, toname, e); progname, fromname, toname, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
ifree(fromname); ifree(fromname);
ifree(toname); ifree(toname);
} }
#ifndef INT_MAX #define TIME_T_BITS_IN_FILE 64
#define INT_MAX ((int) (((unsigned)~0)>>1))
#endif /* !defined INT_MAX */
#ifndef INT_MIN
#define INT_MIN ((int) ~(((unsigned)~0)>>1))
#endif /* !defined INT_MIN */
/*
* The tz file format currently allows at most 32-bit quantities.
* This restriction should be removed before signed 32-bit values
* wrap around in 2038, but unfortunately this will require a
* change to the tz file format.
*/
#define TIME_T_BITS_IN_FILE 32
static void static void
setboundaries(void) setboundaries(void)
{ {
/* int i;
* pg_time_t is always signed, but might be only 32 bits ...
*/ min_time = -1;
min_time = ~(pg_time_t) 0; for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
min_time <<= TYPE_BIT(pg_time_t) -1; min_time *= 2;
max_time = ~(pg_time_t) 0 - min_time; max_time = -(min_time + 1);
/*
* For the moment, hard-wire the range as 1901 to 2038. We cannot go
* wider without adopting an incompatible zone file format, which is a
* step I'd just as soon not take just yet.
*/
min_time = Max(min_time, (pg_time_t) INT_MIN);
max_time = Min(max_time, (pg_time_t) INT_MAX);
min_year = TM_YEAR_BASE + pg_gmtime(&min_time)->tm_year;
max_year = TM_YEAR_BASE + pg_gmtime(&max_time)->tm_year;
min_year_representable = min_year;
max_year_representable = max_year;
} }
static int static int
...@@ -807,7 +808,7 @@ associate(void) ...@@ -807,7 +808,7 @@ associate(void)
} }
} }
if (errors) if (errors)
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static void static void
...@@ -833,7 +834,7 @@ infile(const char *name) ...@@ -833,7 +834,7 @@ infile(const char *name)
(void) fprintf(stderr, _("%s: Cannot open %s: %s\n"), (void) fprintf(stderr, _("%s: Cannot open %s: %s\n"),
progname, name, e); progname, name, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
wantcont = FALSE; wantcont = FALSE;
for (num = 1;; ++num) for (num = 1;; ++num)
...@@ -845,7 +846,7 @@ infile(const char *name) ...@@ -845,7 +846,7 @@ infile(const char *name)
if (cp == NULL) if (cp == NULL)
{ {
error(_("line too long")); error(_("line too long"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
*cp = '\0'; *cp = '\0';
fields = getfields(buf); fields = getfields(buf);
...@@ -896,7 +897,7 @@ infile(const char *name) ...@@ -896,7 +897,7 @@ infile(const char *name)
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
ifree((char *) fields); ifree((char *) fields);
...@@ -905,7 +906,7 @@ infile(const char *name) ...@@ -905,7 +906,7 @@ infile(const char *name)
{ {
(void) fprintf(stderr, _("%s: Error reading %s\n"), (void) fprintf(stderr, _("%s: Error reading %s\n"),
progname, filename); progname, filename);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (fp != stdin && fclose(fp)) if (fp != stdin && fclose(fp))
{ {
...@@ -913,7 +914,7 @@ infile(const char *name) ...@@ -913,7 +914,7 @@ infile(const char *name)
(void) fprintf(stderr, _("%s: Error closing %s: %s\n"), (void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
progname, filename, e); progname, filename, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (wantcont) if (wantcont)
error(_("expected continuation line not found")); error(_("expected continuation line not found"));
...@@ -930,8 +931,8 @@ infile(const char *name) ...@@ -930,8 +931,8 @@ infile(const char *name)
static long static long
gethms(const char *string, const char *errstring, int signable) gethms(const char *string, const char *errstring, int signable)
{ {
int hh, long hh;
mm, int mm,
ss, ss,
sign; sign;
...@@ -946,29 +947,34 @@ gethms(const char *string, const char *errstring, int signable) ...@@ -946,29 +947,34 @@ gethms(const char *string, const char *errstring, int signable)
} }
else else
sign = 1; sign = 1;
if (sscanf(string, scheck(string, "%d"), &hh) == 1) if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
mm = ss = 0; mm = ss = 0;
else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
ss = 0; ss = 0;
else if (sscanf(string, scheck(string, "%d:%d:%d"), else if (sscanf(string, scheck(string, "%ld:%d:%d"),
&hh, &mm, &ss) != 3) &hh, &mm, &ss) != 3)
{ {
error(errstring); error(errstring);
return 0; return 0;
} }
if ((hh < 0 || hh >= HOURSPERDAY || if (hh < 0 ||
mm < 0 || mm >= MINSPERHOUR || mm < 0 || mm >= MINSPERHOUR ||
ss < 0 || ss > SECSPERMIN) && ss < 0 || ss > SECSPERMIN)
!(hh == HOURSPERDAY && mm == 0 && ss == 0))
{ {
error(errstring); error(errstring);
return 0; return 0;
} }
if (noise && hh == HOURSPERDAY) if (LONG_MAX / SECSPERHOUR < hh) {
error(_("time overflow"));
return 0;
}
if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
warning(_("24:00 not handled by pre-1998 versions of zic")); warning(_("24:00 not handled by pre-1998 versions of zic"));
return eitol(sign) * if (noise && (hh > HOURSPERDAY ||
(eitol(hh * MINSPERHOUR + mm) * (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
eitol(SECSPERMIN) + eitol(ss)); warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
return oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
} }
static void static void
...@@ -993,6 +999,8 @@ inrule(char **fields, int nfields) ...@@ -993,6 +999,8 @@ inrule(char **fields, int nfields)
fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
r.r_name = ecpyalloc(fields[RF_NAME]); r.r_name = ecpyalloc(fields[RF_NAME]);
r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar))
max_abbrvar_len = strlen(r.r_abbrvar);
rules = (struct rule *) (void *) erealloc((char *) rules, rules = (struct rule *) (void *) erealloc((char *) rules,
(int) ((nrules + 1) * sizeof *rules)); (int) ((nrules + 1) * sizeof *rules));
rules[nrules++] = r; rules[nrules++] = r;
...@@ -1105,6 +1113,8 @@ inzsub(char **fields, int nfields, int iscont) ...@@ -1105,6 +1113,8 @@ inzsub(char **fields, int nfields, int iscont)
} }
z.z_rule = ecpyalloc(fields[i_rule]); z.z_rule = ecpyalloc(fields[i_rule]);
z.z_format = ecpyalloc(fields[i_format]); z.z_format = ecpyalloc(fields[i_format]);
if (max_format_len < strlen(z.z_format))
max_format_len = strlen(z.z_format);
hasuntil = nfields > i_untilyear; hasuntil = nfields > i_untilyear;
if (hasuntil) if (hasuntil)
{ {
...@@ -1154,7 +1164,7 @@ inleap(char **fields, int nfields) ...@@ -1154,7 +1164,7 @@ inleap(char **fields, int nfields)
day; day;
long dayoff, long dayoff,
tod; tod;
pg_time_t t; zic_t t;
if (nfields != LEAP_FIELDS) if (nfields != LEAP_FIELDS)
{ {
...@@ -1171,6 +1181,11 @@ inleap(char **fields, int nfields) ...@@ -1171,6 +1181,11 @@ inleap(char **fields, int nfields)
error(_("invalid leaping year")); error(_("invalid leaping year"));
return; return;
} }
if (!leapseen || leapmaxyear < year)
leapmaxyear = year;
if (!leapseen || leapminyear > year)
leapminyear = year;
leapseen = TRUE;
j = EPOCH_YEAR; j = EPOCH_YEAR;
while (j != year) while (j != year)
{ {
...@@ -1217,7 +1232,7 @@ inleap(char **fields, int nfields) ...@@ -1217,7 +1232,7 @@ inleap(char **fields, int nfields)
error(_("time too large")); error(_("time too large"));
return; return;
} }
t = (pg_time_t) dayoff *SECSPERDAY; t = (zic_t) dayoff *SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
cp = fields[LP_CORR]; cp = fields[LP_CORR];
...@@ -1339,7 +1354,8 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1339,7 +1354,8 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
*/ */
cp = loyearp; cp = loyearp;
lp = byword(cp, begin_years); lp = byword(cp, begin_years);
if (lp != NULL) rp->r_lowasnum = lp == NULL;
if (!rp->r_lowasnum)
switch ((int) lp->l_value) switch ((int) lp->l_value)
{ {
case YR_MINIMUM: case YR_MINIMUM:
...@@ -1352,22 +1368,17 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1352,22 +1368,17 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1)
{ {
error(_("invalid starting year")); error(_("invalid starting year"));
return; return;
} }
else if (noise)
{
if (rp->r_loyear < min_year_representable)
warning(_("starting year too low to be represented"));
else if (rp->r_loyear > max_year_representable)
warning(_("starting year too high to be represented"));
}
cp = hiyearp; cp = hiyearp;
if ((lp = byword(cp, end_years)) != NULL) lp = byword(cp, end_years);
rp->r_hiwasnum = lp == NULL;
if (!rp->r_hiwasnum)
switch ((int) lp->l_value) switch ((int) lp->l_value)
{ {
case YR_MINIMUM: case YR_MINIMUM:
...@@ -1383,20 +1394,13 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1383,20 +1394,13 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1)
{ {
error(_("invalid ending year")); error(_("invalid ending year"));
return; return;
} }
else if (noise)
{
if (rp->r_loyear < min_year_representable)
warning(_("ending year too low to be represented"));
else if (rp->r_loyear > max_year_representable)
warning(_("ending year too high to be represented"));
}
if (rp->r_loyear > rp->r_hiyear) if (rp->r_loyear > rp->r_hiyear)
{ {
error(_("starting year greater than ending year")); error(_("starting year greater than ending year"));
...@@ -1413,8 +1417,6 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1413,8 +1417,6 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
} }
rp->r_yrtype = ecpyalloc(typep); rp->r_yrtype = ecpyalloc(typep);
} }
if (rp->r_loyear < min_year && rp->r_loyear > 0)
min_year = rp->r_loyear;
/* /*
* Day work. Accept things such as: 1 last-Sunday Sun<=20 Sun>=7 * Day work. Accept things such as: 1 last-Sunday Sun<=20 Sun>=7
...@@ -1470,12 +1472,22 @@ static void ...@@ -1470,12 +1472,22 @@ static void
convert(long val, char *buf) convert(long val, char *buf)
{ {
int i; int i;
long shift; int shift;
for (i = 0, shift = 24; i < 4; ++i, shift -= 8) for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
buf[i] = val >> shift; buf[i] = val >> shift;
} }
static void
convert64(zic_t val, char *buf)
{
int i;
int shift;
for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
buf[i] = val >> shift;
}
static void static void
puttzcode(long val, FILE *fp) puttzcode(long val, FILE *fp)
{ {
...@@ -1485,26 +1497,43 @@ puttzcode(long val, FILE *fp) ...@@ -1485,26 +1497,43 @@ puttzcode(long val, FILE *fp)
(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
} }
static void
puttzcode64(zic_t val, FILE *fp)
{
char buf[8];
convert64(val, buf);
(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
}
static int static int
atcomp(const void *avp, const void *bvp) atcomp(const void *avp, const void *bvp)
{ {
if (((struct attype *) avp)->at < ((struct attype *) bvp)->at) const zic_t a = ((const struct attype *) avp)->at;
return -1; const zic_t b = ((const struct attype *) bvp)->at;
else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
return 1; return (a < b) ? -1 : (a > b);
else }
return 0;
static int
is32(zic_t x)
{
return x == ((zic_t) ((int32) x));
} }
static void static void
writezone(const char *name) writezone(const char *name, const char *string)
{ {
FILE *fp; FILE *fp;
int i, int i,
j; j;
int leapcnt32, leapi32;
int timecnt32, timei32;
int pass;
static char *fullname; static char *fullname;
static const struct tzhead tzh0;
static struct tzhead tzh; static struct tzhead tzh;
pg_time_t ats[TZ_MAX_TIMES]; zic_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES];
/* /*
...@@ -1555,6 +1584,38 @@ writezone(const char *name) ...@@ -1555,6 +1584,38 @@ writezone(const char *name)
ats[i] = attypes[i].at; ats[i] = attypes[i].at;
types[i] = attypes[i].type; types[i] = attypes[i].type;
} }
/*
* Correct for leap seconds.
*/
for (i = 0; i < timecnt; ++i) {
j = leapcnt;
while (--j >= 0)
if (ats[i] > trans[j] - corr[j]) {
ats[i] = tadd(ats[i], corr[j]);
break;
}
}
/*
* Figure out 32-bit-limited starts and counts.
*/
timecnt32 = timecnt;
timei32 = 0;
leapcnt32 = leapcnt;
leapi32 = 0;
while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
--timecnt32;
while (timecnt32 > 0 && !is32(ats[timei32]))
{
--timecnt32;
++timei32;
}
while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
--leapcnt32;
while (leapcnt32 > 0 && !is32(trans[leapi32]))
{
--leapcnt32;
++leapi32;
}
fullname = erealloc(fullname, fullname = erealloc(fullname,
(int) (strlen(directory) + 1 + strlen(name) + 1)); (int) (strlen(directory) + 1 + strlen(name) + 1));
(void) sprintf(fullname, "%s/%s", directory, name); (void) sprintf(fullname, "%s/%s", directory, name);
...@@ -1568,7 +1629,7 @@ writezone(const char *name) ...@@ -1568,7 +1629,7 @@ writezone(const char *name)
(void) fprintf(stderr, _("%s: Cannot remove %s: %s\n"), (void) fprintf(stderr, _("%s: Cannot remove %s: %s\n"),
progname, fullname, e); progname, fullname, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((fp = fopen(fullname, "wb")) == NULL) if ((fp = fopen(fullname, "wb")) == NULL)
{ {
...@@ -1580,104 +1641,383 @@ writezone(const char *name) ...@@ -1580,104 +1641,383 @@ writezone(const char *name)
(void) fprintf(stderr, _("%s: Cannot create %s: %s\n"), (void) fprintf(stderr, _("%s: Cannot create %s: %s\n"),
progname, fullname, e); progname, fullname, e);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); for (pass = 1; pass <= 2; ++pass) {
convert(eitol(typecnt), tzh.tzh_ttisstdcnt); register int thistimei, thistimecnt;
convert(eitol(leapcnt), tzh.tzh_leapcnt); register int thisleapi, thisleapcnt;
convert(eitol(timecnt), tzh.tzh_timecnt); register int thistimelim, thisleaplim;
convert(eitol(typecnt), tzh.tzh_typecnt); int writetype[TZ_MAX_TIMES];
convert(eitol(charcnt), tzh.tzh_charcnt); int typemap[TZ_MAX_TYPES];
(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); register int thistypecnt;
#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp) char thischars[TZ_MAX_CHARS];
DO(tzh_magic); char thischarcnt;
DO(tzh_reserved); int indmap[TZ_MAX_CHARS];
DO(tzh_ttisgmtcnt);
DO(tzh_ttisstdcnt); if (pass == 1) {
DO(tzh_leapcnt); thistimei = timei32;
DO(tzh_timecnt); thistimecnt = timecnt32;
DO(tzh_typecnt); thisleapi = leapi32;
DO(tzh_charcnt); thisleapcnt = leapcnt32;
} else {
thistimei = 0;
thistimecnt = timecnt;
thisleapi = 0;
thisleapcnt = leapcnt;
}
thistimelim = thistimei + thistimecnt;
thisleaplim = thisleapi + thisleapcnt;
for (i = 0; i < typecnt; ++i)
writetype[i] = thistimecnt == timecnt;
if (thistimecnt == 0) {
/*
** No transition times fall in the current
** (32- or 64-bit) window.
*/
if (typecnt != 0)
writetype[typecnt - 1] = TRUE;
} else {
for (i = thistimei - 1; i < thistimelim; ++i)
if (i >= 0)
writetype[types[i]] = TRUE;
/*
** For America/Godthab and Antarctica/Palmer
*/
if (thistimei == 0)
writetype[0] = TRUE;
}
thistypecnt = 0;
for (i = 0; i < typecnt; ++i)
typemap[i] = writetype[i] ? thistypecnt++ : -1;
for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
indmap[i] = -1;
thischarcnt = 0;
for (i = 0; i < typecnt; ++i) {
register char * thisabbr;
if (!writetype[i])
continue;
if (indmap[abbrinds[i]] >= 0)
continue;
thisabbr = &chars[abbrinds[i]];
for (j = 0; j < thischarcnt; ++j)
if (strcmp(&thischars[j], thisabbr) == 0)
break;
if (j == thischarcnt) {
(void) strcpy(&thischars[(int) thischarcnt],
thisabbr);
thischarcnt += strlen(thisabbr) + 1;
}
indmap[abbrinds[i]] = j;
}
#define DO(field) (void) fwrite((void *) tzh.field, \
(size_t) sizeof tzh.field, (size_t) 1, fp)
tzh = tzh0;
(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
tzh.tzh_version[0] = ZIC_VERSION;
convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
convert(eitol(thistimecnt), tzh.tzh_timecnt);
convert(eitol(thistypecnt), tzh.tzh_typecnt);
convert(eitol(thischarcnt), tzh.tzh_charcnt);
DO(tzh_magic);
DO(tzh_version);
DO(tzh_reserved);
DO(tzh_ttisgmtcnt);
DO(tzh_ttisstdcnt);
DO(tzh_leapcnt);
DO(tzh_timecnt);
DO(tzh_typecnt);
DO(tzh_charcnt);
#undef DO #undef DO
for (i = 0; i < timecnt; ++i) for (i = thistimei; i < thistimelim; ++i)
{ if (pass == 1)
j = leapcnt; puttzcode((long) ats[i], fp);
while (--j >= 0) else puttzcode64(ats[i], fp);
if (ats[i] >= trans[j]) for (i = thistimei; i < thistimelim; ++i) {
{ unsigned char uc;
ats[i] = tadd(ats[i], corr[j]);
break; uc = typemap[types[i]];
(void) fwrite((void *) &uc,
(size_t) sizeof uc,
(size_t) 1,
fp);
}
for (i = 0; i < typecnt; ++i)
if (writetype[i]) {
puttzcode(gmtoffs[i], fp);
(void) putc(isdsts[i], fp);
(void) putc((unsigned char) indmap[abbrinds[i]], fp);
} }
puttzcode((long) ats[i], fp); if (thischarcnt != 0)
(void) fwrite((void *) thischars,
(size_t) sizeof thischars[0],
(size_t) thischarcnt, fp);
for (i = thisleapi; i < thisleaplim; ++i) {
register zic_t todo;
if (roll[i]) {
if (timecnt == 0 || trans[i] < ats[0]) {
j = 0;
while (isdsts[j])
if (++j >= typecnt) {
j = 0;
break;
}
} else {
j = 1;
while (j < timecnt &&
trans[i] >= ats[j])
++j;
j = types[j - 1];
}
todo = tadd(trans[i], -gmtoffs[j]);
} else todo = trans[i];
if (pass == 1)
puttzcode((long) todo, fp);
else puttzcode64(todo, fp);
puttzcode(corr[i], fp);
}
for (i = 0; i < typecnt; ++i)
if (writetype[i])
(void) putc(ttisstds[i], fp);
for (i = 0; i < typecnt; ++i)
if (writetype[i])
(void) putc(ttisgmts[i], fp);
}
(void) fprintf(fp, "\n%s\n", string);
if (ferror(fp) || fclose(fp)) {
(void) fprintf(stderr, _("%s: Error writing %s\n"),
progname, fullname);
exit(EXIT_FAILURE);
} }
if (timecnt > 0) }
(void) fwrite((void *) types, (size_t) sizeof types[0],
(size_t) timecnt, fp); static void
for (i = 0; i < typecnt; ++i) doabbr(char *abbr, const char *format, const char *letters, int isdst,
int doquotes)
{
char * cp;
char * slashp;
int len;
slashp = strchr(format, '/');
if (slashp == NULL)
{ {
puttzcode((long) gmtoffs[i], fp); if (letters == NULL)
(void) putc(isdsts[i], fp); (void) strcpy(abbr, format);
(void) putc(abbrinds[i], fp); else
(void) sprintf(abbr, format, letters);
} }
if (charcnt != 0) else if (isdst)
(void) fwrite((void *) chars, (size_t) sizeof chars[0], (void) strcpy(abbr, slashp + 1);
(size_t) charcnt, fp); else
for (i = 0; i < leapcnt; ++i) {
if (slashp > format)
(void) strncpy(abbr, format,
(unsigned) (slashp - format));
abbr[slashp - format] = '\0';
}
if (!doquotes)
return;
for (cp = abbr; *cp != '\0'; ++cp)
if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
break;
len = strlen(abbr);
if (len > 0 && *cp == '\0')
return;
abbr[len + 2] = '\0';
abbr[len + 1] = '>';
for ( ; len > 0; --len)
abbr[len] = abbr[len - 1];
abbr[0] = '<';
}
static void
updateminmax(int x)
{
if (min_year > x)
min_year = x;
if (max_year < x)
max_year = x;
}
static int
stringoffset(char *result, long offset)
{
int hours;
int minutes;
int seconds;
result[0] = '\0';
if (offset < 0) {
(void) strcpy(result, "-");
offset = -offset;
}
seconds = offset % SECSPERMIN;
offset /= SECSPERMIN;
minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR;
hours = offset;
if (hours >= HOURSPERDAY) {
result[0] = '\0';
return -1;
}
(void) sprintf(end(result), "%d", hours);
if (minutes != 0 || seconds != 0) {
(void) sprintf(end(result), ":%02d", minutes);
if (seconds != 0)
(void) sprintf(end(result), ":%02d", seconds);
}
return 0;
}
static int
stringrule(char *result, const struct rule *rp, long dstoff, long gmtoff)
{
long tod;
result = end(result);
if (rp->r_dycode == DC_DOM)
{
int month, total;
if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
return -1;
total = 0;
for (month = 0; month < rp->r_month; ++month)
total += len_months[0][month];
(void) sprintf(result, "J%d", total + rp->r_dayofmonth);
}
else
{ {
if (roll[i]) int week;
if (rp->r_dycode == DC_DOWGEQ)
{ {
if (timecnt == 0 || trans[i] < ats[0]) week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
{ if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth)
j = 0; return -1;
while (isdsts[j]) }
if (++j >= typecnt) else if (rp->r_dycode == DC_DOWLEQ)
{ {
j = 0; if (rp->r_dayofmonth == len_months[1][rp->r_month])
break; week = 5;
} else {
} week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
else if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth)
{ return -1;
j = 1;
while (j < timecnt && trans[i] >= ats[j])
++j;
j = types[j - 1];
} }
puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
} }
else else
puttzcode((long) trans[i], fp); return -1; /* "cannot happen" */
puttzcode((long) corr[i], fp); (void) sprintf(result, "M%d.%d.%d",
rp->r_month + 1, week, rp->r_wday);
}
tod = rp->r_tod;
if (rp->r_todisgmt)
tod += gmtoff;
if (rp->r_todisstd && rp->r_stdoff == 0)
tod += dstoff;
if (tod < 0)
{
result[0] = '\0';
return -1;
} }
for (i = 0; i < typecnt; ++i) if (tod != 2 * SECSPERMIN * MINSPERHOUR)
(void) putc(ttisstds[i], fp);
for (i = 0; i < typecnt; ++i)
(void) putc(ttisgmts[i], fp);
if (ferror(fp) || fclose(fp))
{ {
(void) fprintf(stderr, _("%s: Error writing %s\n"), (void) strcat(result, "/");
progname, fullname); if (stringoffset(end(result), tod) != 0)
(void) exit(EXIT_FAILURE); return -1;
} }
return 0;
} }
static void static void
doabbr(char *abbr, const char *format, const char *letters, int isdst) stringzone(char *result, const struct zone *zpfirst, int zonecount)
{ {
if (strchr(format, '/') == NULL) const struct zone * zp;
struct rule * rp;
struct rule * stdrp;
struct rule * dstrp;
int i;
const char * abbrvar;
result[0] = '\0';
zp = zpfirst + zonecount - 1;
stdrp = dstrp = NULL;
for (i = 0; i < zp->z_nrules; ++i)
{ {
if (letters == NULL) rp = &zp->z_rules[i];
(void) strcpy(abbr, format); if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
continue;
if (rp->r_yrtype != NULL)
continue;
if (rp->r_stdoff == 0) {
if (stdrp == NULL)
stdrp = rp;
else return;
}
else else
(void) sprintf(abbr, format, letters); {
if (dstrp == NULL)
dstrp = rp;
else return;
}
} }
else if (isdst) if (stdrp == NULL && dstrp == NULL)
(void) strcpy(abbr, strchr(format, '/') + 1);
else
{ {
(void) strcpy(abbr, format); /*
*strchr(abbr, '/') = '\0'; * There are no rules running through "max".
* Let's find the latest rule.
*/
for (i = 0; i < zp->z_nrules; ++i)
{
rp = &zp->z_rules[i];
if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
(rp->r_hiyear == stdrp->r_hiyear &&
rp->r_month > stdrp->r_month))
stdrp = rp;
}
if (stdrp != NULL && stdrp->r_stdoff != 0)
return; /* We end up in DST (a POSIX no-no). */
/*
* Horrid special case: if year is 2037,
* presume this is a zone handled on a year-by-year basis;
* do not try to apply a rule to the zone.
*/
if (stdrp != NULL && stdrp->r_hiyear == 2037)
return;
}
if (stdrp == NULL && zp->z_nrules != 0)
return;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
result[0] = '\0';
return;
}
if (dstrp == NULL)
return;
doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
if (stringoffset(end(result),
-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
result[0] = '\0';
return;
}
(void) strcat(result, ",");
if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
result[0] = '\0';
return;
}
(void) strcat(result, ",");
if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
result[0] = '\0';
return;
} }
} }
...@@ -1690,8 +2030,8 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1690,8 +2030,8 @@ outzone(const struct zone * zpfirst, int zonecount)
j; j;
int usestart, int usestart,
useuntil; useuntil;
pg_time_t starttime = 0; zic_t starttime = 0;
pg_time_t untiltime = 0; zic_t untiltime = 0;
long gmtoff; long gmtoff;
long stdoff; long stdoff;
int year; int year;
...@@ -1699,7 +2039,17 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1699,7 +2039,17 @@ outzone(const struct zone * zpfirst, int zonecount)
int startttisstd; int startttisstd;
int startttisgmt; int startttisgmt;
int type; int type;
char startbuf[BUFSIZ]; char *startbuf;
char *ab;
char *envvar;
int max_abbr_len;
int max_envvar_len;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
startbuf = emalloc(max_abbr_len + 1);
ab = emalloc(max_abbr_len + 1);
envvar = emalloc(max_envvar_len + 1);
/* /*
* Now. . .finally. . .generate some useful data! * Now. . .finally. . .generate some useful data!
...@@ -1709,11 +2059,61 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1709,11 +2059,61 @@ outzone(const struct zone * zpfirst, int zonecount)
charcnt = 0; charcnt = 0;
/* /*
* Thanks to Earl Chew (earl@dnd.icp.nec.com.au) for noting the need to * Thanks to Earl Chew for noting the need to
* unconditionally initialize startttisstd. * unconditionally initialize startttisstd.
*/ */
startttisstd = FALSE; startttisstd = FALSE;
startttisgmt = FALSE; startttisgmt = FALSE;
min_year = max_year = EPOCH_YEAR;
if (leapseen)
{
updateminmax(leapminyear);
updateminmax(leapmaxyear);
}
for (i = 0; i < zonecount; ++i)
{
zp = &zpfirst[i];
if (i < zonecount - 1)
updateminmax(zp->z_untilrule.r_loyear);
for (j = 0; j < zp->z_nrules; ++j)
{
rp = &zp->z_rules[j];
if (rp->r_lowasnum)
updateminmax(rp->r_loyear);
if (rp->r_hiwasnum)
updateminmax(rp->r_hiyear);
}
}
/*
* Generate lots of data if a rule can't cover all future times.
*/
stringzone(envvar, zpfirst, zonecount);
if (noise && envvar[0] == '\0') {
char * wp;
wp = ecpyalloc(_("no POSIX environment variable for zone"));
wp = ecatalloc(wp, " ");
wp = ecatalloc(wp, zpfirst->z_name);
warning(wp);
ifree(wp);
}
if (envvar[0] == '\0')
{
if (min_year >= INT_MIN + YEARSPERREPEAT)
min_year -= YEARSPERREPEAT;
else min_year = INT_MIN;
if (max_year <= INT_MAX - YEARSPERREPEAT)
max_year += YEARSPERREPEAT;
else max_year = INT_MAX;
}
/*
* For the benefit of older systems,
* generate data from 1900 through 2037.
*/
if (min_year > 1900)
min_year = 1900;
if (max_year < 2037)
max_year = 2037;
for (i = 0; i < zonecount; ++i) for (i = 0; i < zonecount; ++i)
{ {
/* /*
...@@ -1733,7 +2133,7 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1733,7 +2133,7 @@ outzone(const struct zone * zpfirst, int zonecount)
{ {
stdoff = zp->z_stdoff; stdoff = zp->z_stdoff;
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp->z_format,
(char *) NULL, stdoff != 0); (char *) NULL, stdoff != 0, FALSE);
type = addtype(oadd(zp->z_gmtoff, stdoff), type = addtype(oadd(zp->z_gmtoff, stdoff),
startbuf, stdoff != 0, startttisstd, startbuf, stdoff != 0, startttisstd,
startttisgmt); startttisgmt);
...@@ -1769,10 +2169,9 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1769,10 +2169,9 @@ outzone(const struct zone * zpfirst, int zonecount)
for (;;) for (;;)
{ {
int k; int k;
pg_time_t jtime, zic_t jtime,
ktime = 0; ktime = 0;
long offset; long offset;
char buf[BUFSIZ];
if (useuntil) if (useuntil)
{ {
...@@ -1832,24 +2231,27 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1832,24 +2231,27 @@ outzone(const struct zone * zpfirst, int zonecount)
stdoff); stdoff);
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp->z_format,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != 0); rp->r_stdoff != 0,
FALSE);
continue; continue;
} }
if (*startbuf == '\0' && if (*startbuf == '\0' &&
startoff == oadd(zp->z_gmtoff, startoff == oadd(zp->z_gmtoff, stdoff))
stdoff))
{ {
doabbr(startbuf, zp->z_format, doabbr(startbuf,
zp->z_format,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != 0); rp->r_stdoff !=
0,
FALSE);
} }
} }
eats(zp->z_filename, zp->z_linenum, eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum); rp->r_filename, rp->r_linenum);
doabbr(buf, zp->z_format, rp->r_abbrvar, doabbr(ab, zp->z_format, rp->r_abbrvar,
rp->r_stdoff != 0); rp->r_stdoff != 0, FALSE);
offset = oadd(zp->z_gmtoff, rp->r_stdoff); offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, buf, rp->r_stdoff != 0, type = addtype(offset, ab, rp->r_stdoff != 0,
rp->r_todisstd, rp->r_todisgmt); rp->r_todisstd, rp->r_todisgmt);
addtt(ktime, type); addtt(ktime, type);
} }
...@@ -1886,11 +2288,14 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -1886,11 +2288,14 @@ outzone(const struct zone * zpfirst, int zonecount)
starttime = tadd(starttime, -gmtoff); starttime = tadd(starttime, -gmtoff);
} }
} }
writezone(zpfirst->z_name); writezone(zpfirst->z_name, envvar);
ifree(startbuf);
ifree(ab);
ifree(envvar);
} }
static void static void
addtt(const pg_time_t starttime, int type) addtt(const zic_t starttime, int type)
{ {
if (starttime <= min_time || if (starttime <= min_time ||
(timecnt == 1 && attypes[0].at < min_time)) (timecnt == 1 && attypes[0].at < min_time))
...@@ -1910,7 +2315,7 @@ addtt(const pg_time_t starttime, int type) ...@@ -1910,7 +2315,7 @@ addtt(const pg_time_t starttime, int type)
if (timecnt >= TZ_MAX_TIMES) if (timecnt >= TZ_MAX_TIMES)
{ {
error(_("too many transitions?!")); error(_("too many transitions?!"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
attypes[timecnt].at = starttime; attypes[timecnt].at = starttime;
attypes[timecnt].type = type; attypes[timecnt].type = type;
...@@ -1927,17 +2332,17 @@ addtype(long gmtoff, const char *abbr, int isdst, ...@@ -1927,17 +2332,17 @@ addtype(long gmtoff, const char *abbr, int isdst,
if (isdst != TRUE && isdst != FALSE) if (isdst != TRUE && isdst != FALSE)
{ {
error(_("internal error - addtype called with bad isdst")); error(_("internal error - addtype called with bad isdst"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (ttisstd != TRUE && ttisstd != FALSE) if (ttisstd != TRUE && ttisstd != FALSE)
{ {
error(_("internal error - addtype called with bad ttisstd")); error(_("internal error - addtype called with bad ttisstd"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (ttisgmt != TRUE && ttisgmt != FALSE) if (ttisgmt != TRUE && ttisgmt != FALSE)
{ {
error(_("internal error - addtype called with bad ttisgmt")); error(_("internal error - addtype called with bad ttisgmt"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* /*
...@@ -1959,7 +2364,11 @@ addtype(long gmtoff, const char *abbr, int isdst, ...@@ -1959,7 +2364,11 @@ addtype(long gmtoff, const char *abbr, int isdst,
if (typecnt >= TZ_MAX_TYPES) if (typecnt >= TZ_MAX_TYPES)
{ {
error(_("too many local time types")); error(_("too many local time types"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
}
if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
error(_("UTC offset out of range"));
exit(EXIT_FAILURE);
} }
gmtoffs[i] = gmtoff; gmtoffs[i] = gmtoff;
isdsts[i] = isdst; isdsts[i] = isdst;
...@@ -1977,7 +2386,7 @@ addtype(long gmtoff, const char *abbr, int isdst, ...@@ -1977,7 +2386,7 @@ addtype(long gmtoff, const char *abbr, int isdst,
} }
static void static void
leapadd(const pg_time_t t, int positive, int rolling, int count) leapadd(const zic_t t, int positive, int rolling, int count)
{ {
int i; int i;
int j; int j;
...@@ -1985,7 +2394,7 @@ leapadd(const pg_time_t t, int positive, int rolling, int count) ...@@ -1985,7 +2394,7 @@ leapadd(const pg_time_t t, int positive, int rolling, int count)
if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS)
{ {
error(_("too many leap seconds")); error(_("too many leap seconds"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (i = 0; i < leapcnt; ++i) for (i = 0; i < leapcnt; ++i)
if (t <= trans[i]) if (t <= trans[i])
...@@ -1993,7 +2402,7 @@ leapadd(const pg_time_t t, int positive, int rolling, int count) ...@@ -1993,7 +2402,7 @@ leapadd(const pg_time_t t, int positive, int rolling, int count)
if (t == trans[i]) if (t == trans[i])
{ {
error(_("repeated leap second moment")); error(_("repeated leap second moment"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
} }
...@@ -2051,7 +2460,7 @@ yearistype(int year, const char *type) ...@@ -2051,7 +2460,7 @@ yearistype(int year, const char *type)
(void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"),
progname, buf, result); progname, buf, result);
for (;;) for (;;)
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static int static int
...@@ -2130,7 +2539,8 @@ getfields(char *cp) ...@@ -2130,7 +2539,8 @@ getfields(char *cp)
nsubs = 0; nsubs = 0;
for (;;) for (;;)
{ {
while (isascii(*cp) && isspace((unsigned char) *cp)) while (isascii((unsigned char) *cp) &&
isspace((unsigned char) *cp))
++cp; ++cp;
if (*cp == '\0' || *cp == '#') if (*cp == '\0' || *cp == '#')
break; break;
...@@ -2144,7 +2554,10 @@ getfields(char *cp) ...@@ -2144,7 +2554,10 @@ getfields(char *cp)
if (*dp != '\0') if (*dp != '\0')
++dp; ++dp;
else else
{
error(_("Odd number of quotation marks")); error(_("Odd number of quotation marks"));
exit(1);
}
} while (*cp != '\0' && *cp != '#' && } while (*cp != '\0' && *cp != '#' &&
(!isascii(*cp) || !isspace((unsigned char) *cp))); (!isascii(*cp) || !isspace((unsigned char) *cp)));
if (isascii(*cp) && isspace((unsigned char) *cp)) if (isascii(*cp) && isspace((unsigned char) *cp))
...@@ -2164,15 +2577,15 @@ oadd(long t1, long t2) ...@@ -2164,15 +2577,15 @@ oadd(long t1, long t2)
if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1))
{ {
error(_("time overflow")); error(_("time overflow"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return t; return t;
} }
static pg_time_t static zic_t
tadd(const pg_time_t t1, long t2) tadd(const zic_t t1, long t2)
{ {
pg_time_t t; zic_t t;
if (t1 == max_time && t2 > 0) if (t1 == max_time && t2 > 0)
return max_time; return max_time;
...@@ -2182,7 +2595,7 @@ tadd(const pg_time_t t1, long t2) ...@@ -2182,7 +2595,7 @@ tadd(const pg_time_t t1, long t2)
if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1))
{ {
error(_("time overflow")); error(_("time overflow"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return t; return t;
} }
...@@ -2192,14 +2605,14 @@ tadd(const pg_time_t t1, long t2) ...@@ -2192,14 +2605,14 @@ tadd(const pg_time_t t1, long t2)
* 1970, 00:00 LOCAL time - in that year that the rule refers to. * 1970, 00:00 LOCAL time - in that year that the rule refers to.
*/ */
static pg_time_t static zic_t
rpytime(const struct rule * rp, int wantedy) rpytime(const struct rule * rp, int wantedy)
{ {
int y, int y,
m, m,
i; i;
long dayoff; /* with a nod to Margaret O. */ long dayoff; /* with a nod to Margaret O. */
pg_time_t t; zic_t t;
if (wantedy == INT_MIN) if (wantedy == INT_MIN)
return min_time; return min_time;
...@@ -2236,7 +2649,7 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2236,7 +2649,7 @@ rpytime(const struct rule * rp, int wantedy)
else else
{ {
error(_("use of 2/29 in non leap-year")); error(_("use of 2/29 in non leap-year"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
--i; --i;
...@@ -2276,16 +2689,16 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2276,16 +2689,16 @@ rpytime(const struct rule * rp, int wantedy)
} }
if (i < 0 || i >= len_months[isleap(y)][m]) if (i < 0 || i >= len_months[isleap(y)][m])
{ {
error(_("no day in month matches rule")); if (noise)
(void) exit(EXIT_FAILURE); warning(_("rule goes past start/end of month--\
will not work with pre-2004 versions of zic"));
} }
} }
if (dayoff < min_time / SECSPERDAY) if (dayoff < min_time / SECSPERDAY)
return min_time; return min_time;
if (dayoff > max_time / SECSPERDAY) if (dayoff > max_time / SECSPERDAY)
return max_time; return max_time;
t = (pg_time_t) dayoff *SECSPERDAY; t = (zic_t) dayoff *SECSPERDAY;
return tadd(t, rp->r_tod); return tadd(t, rp->r_tod);
} }
...@@ -2294,11 +2707,50 @@ newabbr(const char *string) ...@@ -2294,11 +2707,50 @@ newabbr(const char *string)
{ {
int i; int i;
if (strcmp(string, GRANDPARENTED) != 0)
{
const char * cp;
char * wp;
/*
* Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics
* optionally followed by a + or - and a number from 1 to 14.
*/
cp = string;
wp = NULL;
while (isascii((unsigned char) *cp) &&
isalpha((unsigned char) *cp))
++cp;
if (cp - string == 0)
wp = _("time zone abbreviation lacks alphabetic at start");
if (noise && cp - string > 3)
wp = _("time zone abbreviation has more than 3 alphabetics");
if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
wp = _("time zone abbreviation has too many alphabetics");
if (wp == NULL && (*cp == '+' || *cp == '-')) {
++cp;
if (isascii((unsigned char) *cp) &&
isdigit((unsigned char) *cp))
if (*cp++ == '1' &&
*cp >= '0' && *cp <= '4')
++cp;
}
if (*cp != '\0')
wp = _("time zone abbreviation differs from POSIX standard");
if (wp != NULL) {
wp = ecpyalloc(wp);
wp = ecatalloc(wp, " (");
wp = ecatalloc(wp, string);
wp = ecatalloc(wp, ")");
warning(wp);
ifree(wp);
}
}
i = strlen(string) + 1; i = strlen(string) + 1;
if (charcnt + i > TZ_MAX_CHARS) if (charcnt + i > TZ_MAX_CHARS)
{ {
error(_("too many, or too long, time zone abbreviations")); error(_("too many, or too long, time zone abbreviations"));
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
(void) strcpy(&chars[charcnt], string); (void) strcpy(&chars[charcnt], string);
charcnt += eitol(i); charcnt += eitol(i);
...@@ -2366,7 +2818,7 @@ eitol(int i) ...@@ -2366,7 +2818,7 @@ eitol(int i)
(void) fprintf(stderr, (void) fprintf(stderr,
_("%s: %d did not sign extend correctly\n"), _("%s: %d did not sign extend correctly\n"),
progname, i); progname, i);
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return l; return l;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment