mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-07 19:47:50 +08:00
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
This commit is contained in:
parent
2f67722dda
commit
0171e72d4d
@ -114,6 +114,31 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist');
|
||||
ERROR: time zone "america/does_not_exist" not recognized
|
||||
SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist';
|
||||
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
|
||||
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
|
||||
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997');
|
||||
|
@ -86,6 +86,13 @@ SELECT '19970710 173201' AT TIME ZONE 'America/New_York';
|
||||
INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 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
|
||||
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
This is a PostgreSQL adapted version of the timezone library
|
||||
from:
|
||||
This is a PostgreSQL adapted version of the timezone library from:
|
||||
|
||||
ftp://elsie.nci.nih.gov/pub/tzcode*.tar.gz
|
||||
|
||||
The data files under data/ are an exact copy of the latest data set
|
||||
from
|
||||
The code is currently synced with release 2007k. There are many cosmetic
|
||||
(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
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* 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
|
||||
* $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"
|
||||
|
@ -1,15 +1,14 @@
|
||||
/*
|
||||
* This file is in the public domain, so clarified as of
|
||||
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
|
||||
* 1996-06-05 by Arthur David Olson.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.19 2007/11/15 21:14:46 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.20 2008/02/16 21:16:04 tgl Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
|
||||
* POSIX-style TZ environment variable handling from Guy Harris
|
||||
* (guy@auspex.com).
|
||||
* Leap second handling from Bradley White.
|
||||
* POSIX-style TZ environment variable handling from Guy Harris.
|
||||
*/
|
||||
|
||||
/* this file needs to build in both frontend and backend contexts */
|
||||
@ -36,9 +35,9 @@
|
||||
* 5. They might reference tm.TM_ZONE after calling offtime.
|
||||
* What's best to do in the above cases is open to debate;
|
||||
* for now, we just set things up so that in any of the five cases
|
||||
* WILDABBR is used. Another possibility: initialize tzname[0] to the
|
||||
* WILDABBR is used. Another possibility: initialize tzname[0] to the
|
||||
* string "tzname[0] used before set", and similarly for the other cases.
|
||||
* And another: initialize tzname[0] to "ERA", with an explanation in the
|
||||
* And another: initialize tzname[0] to "ERA", with an explanation in the
|
||||
* manual page of what this "time zone abbreviation" means (doing this so
|
||||
* that tzname[0] has the "normal" length of three characters).
|
||||
*----------
|
||||
@ -46,7 +45,7 @@
|
||||
#define WILDABBR " "
|
||||
#endif /* !defined WILDABBR */
|
||||
|
||||
static char wildabbr[] = "WILDABBR";
|
||||
static char wildabbr[] = WILDABBR;
|
||||
|
||||
static const char gmt[] = "GMT";
|
||||
|
||||
@ -77,18 +76,25 @@ struct rule
|
||||
*/
|
||||
|
||||
static long detzcode(const char *codep);
|
||||
static pg_time_t detzcode64(const char *codep);
|
||||
static int differ_by_repeat(pg_time_t t1, pg_time_t t0);
|
||||
static const char *getzname(const char *strp);
|
||||
static const char *getqzname(const char *strp, int delim);
|
||||
static const char *getnum(const char *strp, int *nump, int min, int max);
|
||||
static const char *getsecs(const char *strp, long *secsp);
|
||||
static const char *getoffset(const char *strp, long *offsetp);
|
||||
static const char *getrule(const char *strp, struct rule * rulep);
|
||||
static void gmtload(struct state * sp);
|
||||
static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp);
|
||||
static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz);
|
||||
static void timesub(const pg_time_t *timep, long offset,
|
||||
const struct state * sp, struct pg_tm * tmp);
|
||||
static struct pg_tm *gmtsub(const pg_time_t *timep, long offset,
|
||||
struct pg_tm *tmp);
|
||||
static struct pg_tm *localsub(const pg_time_t *timep, long offset,
|
||||
struct pg_tm *tmp, const pg_tz *tz);
|
||||
static int increment_overflow(int *number, int delta);
|
||||
static pg_time_t transtime(pg_time_t janfirst, int year,
|
||||
const struct rule * rulep, long offset);
|
||||
const struct rule *rulep, long offset);
|
||||
static int typesequiv(const struct state *sp, int a, int b);
|
||||
static struct pg_tm *timesub(const pg_time_t *timep, long offset,
|
||||
const struct state *sp, struct pg_tm *tmp);
|
||||
|
||||
/* GMT timezone */
|
||||
static struct state gmtmem;
|
||||
@ -103,7 +109,7 @@ static int gmt_is_set = 0;
|
||||
* Except for the strftime function, these functions [asctime,
|
||||
* ctime, gmtime, localtime] return values in one of two static
|
||||
* objects: a broken-down time structure and an array of char.
|
||||
* Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
|
||||
* Thanks to Paul Eggert for noting this.
|
||||
*/
|
||||
|
||||
static struct pg_tm tm;
|
||||
@ -115,18 +121,48 @@ detzcode(const char *codep)
|
||||
long result;
|
||||
int i;
|
||||
|
||||
result = (codep[0] & 0x80) ? ~0L : 0L;
|
||||
result = (codep[0] & 0x80) ? ~0L : 0;
|
||||
for (i = 0; i < 4; ++i)
|
||||
result = (result << 8) | (codep[i] & 0xff);
|
||||
return result;
|
||||
}
|
||||
|
||||
static pg_time_t
|
||||
detzcode64(const char *codep)
|
||||
{
|
||||
pg_time_t result;
|
||||
int i;
|
||||
|
||||
result = (codep[0] & 0x80) ? (~(int64) 0) : 0;
|
||||
for (i = 0; i < 8; ++i)
|
||||
result = result * 256 + (codep[i] & 0xff);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
differ_by_repeat(pg_time_t t1, pg_time_t t0)
|
||||
{
|
||||
if (TYPE_INTEGRAL(pg_time_t) &&
|
||||
TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_time_t) < SECSPERREPEAT_BITS)
|
||||
return 0;
|
||||
return t1 - t0 == SECSPERREPEAT;
|
||||
}
|
||||
|
||||
int
|
||||
tzload(const char *name, char *canonname, struct state * sp)
|
||||
tzload(const char *name, char *canonname, struct state * sp, int doextend)
|
||||
{
|
||||
const char *p;
|
||||
int i;
|
||||
int fid;
|
||||
int stored;
|
||||
int nread;
|
||||
union
|
||||
{
|
||||
struct tzhead tzhead;
|
||||
char buf[2 * sizeof(struct tzhead) +
|
||||
2 * sizeof *sp +
|
||||
4 * TZ_MAX_TIMES];
|
||||
} u;
|
||||
|
||||
if (name == NULL && (name = TZDEFAULT) == NULL)
|
||||
return -1;
|
||||
@ -135,19 +171,14 @@ tzload(const char *name, char *canonname, struct state * sp)
|
||||
fid = pg_open_tzfile(name, canonname);
|
||||
if (fid < 0)
|
||||
return -1;
|
||||
nread = read(fid, u.buf, sizeof u.buf);
|
||||
if (close(fid) != 0 || nread <= 0)
|
||||
return -1;
|
||||
for (stored = 4; stored <= 8; stored *= 2)
|
||||
{
|
||||
struct tzhead *tzhp;
|
||||
union
|
||||
{
|
||||
struct tzhead tzhead;
|
||||
char buf[sizeof *sp + sizeof *tzhp];
|
||||
} u;
|
||||
int ttisstdcnt;
|
||||
int ttisgmtcnt;
|
||||
|
||||
i = read(fid, u.buf, sizeof u.buf);
|
||||
if (close(fid) != 0)
|
||||
return -1;
|
||||
ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
|
||||
ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
|
||||
sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
|
||||
@ -162,18 +193,19 @@ tzload(const char *name, char *canonname, struct state * sp)
|
||||
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
|
||||
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
|
||||
return -1;
|
||||
if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
|
||||
if (nread - (p - u.buf) <
|
||||
sp->timecnt * stored + /* ats */
|
||||
sp->timecnt + /* types */
|
||||
sp->typecnt * (4 + 2) + /* ttinfos */
|
||||
sp->typecnt * 6 + /* ttinfos */
|
||||
sp->charcnt + /* chars */
|
||||
sp->leapcnt * (4 + 4) + /* lsinfos */
|
||||
sp->leapcnt * (stored + 4) + /* lsinfos */
|
||||
ttisstdcnt + /* ttisstds */
|
||||
ttisgmtcnt) /* ttisgmts */
|
||||
return -1;
|
||||
for (i = 0; i < sp->timecnt; ++i)
|
||||
{
|
||||
sp->ats[i] = detzcode(p);
|
||||
p += 4;
|
||||
sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p);
|
||||
p += stored;
|
||||
}
|
||||
for (i = 0; i < sp->timecnt; ++i)
|
||||
{
|
||||
@ -204,8 +236,8 @@ tzload(const char *name, char *canonname, struct state * sp)
|
||||
struct lsinfo *lsisp;
|
||||
|
||||
lsisp = &sp->lsis[i];
|
||||
lsisp->ls_trans = detzcode(p);
|
||||
p += 4;
|
||||
lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p);
|
||||
p += stored;
|
||||
lsisp->ls_corr = detzcode(p);
|
||||
p += 4;
|
||||
}
|
||||
@ -239,10 +271,127 @@ tzload(const char *name, char *canonname, struct state * sp)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Out-of-sort ats should mean we're running on a
|
||||
* signed time_t system but using a data file with
|
||||
* unsigned values (or vice versa).
|
||||
*/
|
||||
for (i = 0; i < sp->timecnt - 2; ++i)
|
||||
if (sp->ats[i] > sp->ats[i + 1])
|
||||
{
|
||||
++i;
|
||||
if (TYPE_SIGNED(pg_time_t))
|
||||
{
|
||||
/*
|
||||
* Ignore the end (easy).
|
||||
*/
|
||||
sp->timecnt = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Ignore the beginning (harder).
|
||||
*/
|
||||
int j;
|
||||
|
||||
for (j = 0; j + i < sp->timecnt; ++j)
|
||||
{
|
||||
sp->ats[j] = sp->ats[j + i];
|
||||
sp->types[j] = sp->types[j + i];
|
||||
}
|
||||
sp->timecnt = j;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is an old file, we're done.
|
||||
*/
|
||||
if (u.tzhead.tzh_version[0] == '\0')
|
||||
break;
|
||||
nread -= p - u.buf;
|
||||
for (i = 0; i < nread; ++i)
|
||||
u.buf[i] = p[i];
|
||||
/*
|
||||
* If this is a narrow integer time_t system, we're done.
|
||||
*/
|
||||
if (stored >= (int) sizeof(pg_time_t) && TYPE_INTEGRAL(pg_time_t))
|
||||
break;
|
||||
}
|
||||
if (doextend && nread > 2 &&
|
||||
u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
|
||||
sp->typecnt + 2 <= TZ_MAX_TYPES)
|
||||
{
|
||||
struct state ts;
|
||||
int result;
|
||||
|
||||
u.buf[nread - 1] = '\0';
|
||||
result = tzparse(&u.buf[1], &ts, FALSE);
|
||||
if (result == 0 && ts.typecnt == 2 &&
|
||||
sp->charcnt + ts.charcnt <= TZ_MAX_CHARS)
|
||||
{
|
||||
for (i = 0; i < 2; ++i)
|
||||
ts.ttis[i].tt_abbrind +=
|
||||
sp->charcnt;
|
||||
for (i = 0; i < ts.charcnt; ++i)
|
||||
sp->chars[sp->charcnt++] =
|
||||
ts.chars[i];
|
||||
i = 0;
|
||||
while (i < ts.timecnt &&
|
||||
ts.ats[i] <=
|
||||
sp->ats[sp->timecnt - 1])
|
||||
++i;
|
||||
while (i < ts.timecnt &&
|
||||
sp->timecnt < TZ_MAX_TIMES)
|
||||
{
|
||||
sp->ats[sp->timecnt] =
|
||||
ts.ats[i];
|
||||
sp->types[sp->timecnt] =
|
||||
sp->typecnt +
|
||||
ts.types[i];
|
||||
++sp->timecnt;
|
||||
++i;
|
||||
}
|
||||
sp->ttis[sp->typecnt++] = ts.ttis[0];
|
||||
sp->ttis[sp->typecnt++] = ts.ttis[1];
|
||||
}
|
||||
}
|
||||
i = 2 * YEARSPERREPEAT;
|
||||
sp->goback = sp->goahead = sp->timecnt > i;
|
||||
sp->goback = sp->goback &&
|
||||
typesequiv(sp, sp->types[i], sp->types[0]) &&
|
||||
differ_by_repeat(sp->ats[i], sp->ats[0]);
|
||||
sp->goahead = sp->goahead &&
|
||||
typesequiv(sp, sp->types[sp->timecnt - 1],
|
||||
sp->types[sp->timecnt - 1 - i]) &&
|
||||
differ_by_repeat(sp->ats[sp->timecnt - 1],
|
||||
sp->ats[sp->timecnt - 1 - i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
typesequiv(const struct state *sp, int a, int b)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (sp == NULL ||
|
||||
a < 0 || a >= sp->typecnt ||
|
||||
b < 0 || b >= sp->typecnt)
|
||||
result = FALSE;
|
||||
else
|
||||
{
|
||||
const struct ttinfo *ap = &sp->ttis[a];
|
||||
const struct ttinfo *bp = &sp->ttis[b];
|
||||
|
||||
result = ap->tt_gmtoff == bp->tt_gmtoff &&
|
||||
ap->tt_isdst == bp->tt_isdst &&
|
||||
ap->tt_ttisstd == bp->tt_ttisstd &&
|
||||
ap->tt_ttisgmt == bp->tt_ttisgmt &&
|
||||
strcmp(&sp->chars[ap->tt_abbrind],
|
||||
&sp->chars[bp->tt_abbrind]) == 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const int mon_lengths[2][MONSPERYEAR] = {
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
@ -254,7 +403,7 @@ static const int year_lengths[2] = {
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, scan until a character that is not
|
||||
* a valid character in a zone name is found. Return a pointer to that
|
||||
* a valid character in a zone name is found. Return a pointer to that
|
||||
* character.
|
||||
*/
|
||||
static const char *
|
||||
@ -268,6 +417,24 @@ getzname(const char *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.
|
||||
* Check that the number is within a specified range; if it is not, return
|
||||
@ -327,7 +494,7 @@ getsecs(const char *strp, long *secsp)
|
||||
if (*strp == ':')
|
||||
{
|
||||
++strp;
|
||||
/* `SECSPERMIN' allows for leap seconds. */
|
||||
/* `SECSPERMIN' allows for leap seconds. */
|
||||
strp = getnum(strp, &num, 0, SECSPERMIN);
|
||||
if (strp == NULL)
|
||||
return NULL;
|
||||
@ -365,7 +532,7 @@ getoffset(const char *strp, long *offsetp)
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, extract a rule in the form
|
||||
* date[/time]. See POSIX section 8 for the format of "date" and "time".
|
||||
* date[/time]. See POSIX section 8 for the format of "date" and "time".
|
||||
* If a valid rule is not found, return NULL.
|
||||
* Otherwise, return a pointer to the first character not part of the rule.
|
||||
*/
|
||||
@ -559,26 +726,47 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
||||
}
|
||||
else
|
||||
{
|
||||
name = getzname(name);
|
||||
stdlen = name - stdname;
|
||||
if (stdlen < 3)
|
||||
return -1;
|
||||
if (*name == '<')
|
||||
{
|
||||
name++;
|
||||
stdname = name;
|
||||
name = getqzname(name, '>');
|
||||
if (*name != '>')
|
||||
return (-1);
|
||||
stdlen = name - stdname;
|
||||
name++;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = getzname(name);
|
||||
stdlen = name - stdname;
|
||||
}
|
||||
if (*name == '\0')
|
||||
return -1;
|
||||
name = getoffset(name, &stdoffset);
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
load_result = tzload(TZDEFRULES, NULL, sp);
|
||||
load_result = tzload(TZDEFRULES, NULL, sp, FALSE);
|
||||
}
|
||||
if (load_result != 0)
|
||||
sp->leapcnt = 0; /* so, we're off a little */
|
||||
if (*name != '\0')
|
||||
{
|
||||
dstname = name;
|
||||
name = getzname(name);
|
||||
dstlen = name - dstname; /* length of DST zone name */
|
||||
if (dstlen < 3)
|
||||
return -1;
|
||||
if (*name == '<')
|
||||
{
|
||||
dstname = ++name;
|
||||
name = getqzname(name, '>');
|
||||
if (*name != '>')
|
||||
return -1;
|
||||
dstlen = name - dstname;
|
||||
name++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dstname = name;
|
||||
name = getzname(name);
|
||||
dstlen = name - dstname; /* length of DST zone name */
|
||||
}
|
||||
if (*name != '\0' && *name != ',' && *name != ';')
|
||||
{
|
||||
name = getoffset(name, &dstoffset);
|
||||
@ -610,11 +798,8 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
||||
sp->typecnt = 2; /* standard time and DST */
|
||||
|
||||
/*
|
||||
* Two transitions per year, from EPOCH_YEAR to 2037.
|
||||
* Two transitions per year, from EPOCH_YEAR forward.
|
||||
*/
|
||||
sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
|
||||
if (sp->timecnt > TZ_MAX_TIMES)
|
||||
return -1;
|
||||
sp->ttis[0].tt_gmtoff = -dstoffset;
|
||||
sp->ttis[0].tt_isdst = 1;
|
||||
sp->ttis[0].tt_abbrind = stdlen + 1;
|
||||
@ -624,8 +809,13 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
||||
atp = sp->ats;
|
||||
typep = sp->types;
|
||||
janfirst = 0;
|
||||
for (year = EPOCH_YEAR; year <= 2037; ++year)
|
||||
sp->timecnt = 0;
|
||||
for (year = EPOCH_YEAR;
|
||||
sp->timecnt + 2 <= TZ_MAX_TIMES;
|
||||
++year)
|
||||
{
|
||||
pg_time_t newfirst;
|
||||
|
||||
starttime = transtime(janfirst, year, &start,
|
||||
stdoffset);
|
||||
endtime = transtime(janfirst, year, &end,
|
||||
@ -644,8 +834,13 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
||||
*atp++ = endtime;
|
||||
*typep++ = 1; /* DST ends */
|
||||
}
|
||||
janfirst += year_lengths[isleap(year)] *
|
||||
sp->timecnt += 2;
|
||||
newfirst = janfirst;
|
||||
newfirst += year_lengths[isleap(year)] *
|
||||
SECSPERDAY;
|
||||
if (newfirst <= janfirst)
|
||||
break;
|
||||
janfirst = newfirst;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -776,7 +971,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
||||
static void
|
||||
gmtload(struct state * sp)
|
||||
{
|
||||
if (tzload(gmt, NULL, sp) != 0)
|
||||
if (tzload(gmt, NULL, sp, TRUE) != 0)
|
||||
(void) tzparse(gmt, sp, TRUE);
|
||||
}
|
||||
|
||||
@ -784,20 +979,63 @@ gmtload(struct state * sp)
|
||||
/*
|
||||
* The easy way to behave "as if no library function calls" localtime
|
||||
* is to not call it--so we drop its guts into "localsub", which can be
|
||||
* freely called. (And no, the PANS doesn't require the above behavior--
|
||||
* freely called. (And no, the PANS doesn't require the above behavior--
|
||||
* but it *is* desirable.)
|
||||
*
|
||||
* The unused offset argument is for the benefit of mktime variants.
|
||||
*/
|
||||
static void
|
||||
localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz)
|
||||
static struct pg_tm *
|
||||
localsub(const pg_time_t *timep, long offset,
|
||||
struct pg_tm *tmp, const pg_tz *tz)
|
||||
{
|
||||
const struct state *sp;
|
||||
const struct ttinfo *ttisp;
|
||||
int i;
|
||||
struct pg_tm *result;
|
||||
const pg_time_t t = *timep;
|
||||
|
||||
sp = &tz->state;
|
||||
if ((sp->goback && t < sp->ats[0]) ||
|
||||
(sp->goahead && t > sp->ats[sp->timecnt - 1]))
|
||||
{
|
||||
pg_time_t newt = t;
|
||||
pg_time_t seconds;
|
||||
pg_time_t tcycles;
|
||||
int64 icycles;
|
||||
|
||||
if (t < sp->ats[0])
|
||||
seconds = sp->ats[0] - t;
|
||||
else seconds = t - sp->ats[sp->timecnt - 1];
|
||||
--seconds;
|
||||
tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
|
||||
++tcycles;
|
||||
icycles = tcycles;
|
||||
if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
|
||||
return NULL;
|
||||
seconds = icycles;
|
||||
seconds *= YEARSPERREPEAT;
|
||||
seconds *= AVGSECSPERYEAR;
|
||||
if (t < sp->ats[0])
|
||||
newt += seconds;
|
||||
else newt -= seconds;
|
||||
if (newt < sp->ats[0] ||
|
||||
newt > sp->ats[sp->timecnt - 1])
|
||||
return NULL; /* "cannot happen" */
|
||||
result = localsub(&newt, offset, tmp, tz);
|
||||
if (result == tmp)
|
||||
{
|
||||
pg_time_t newy;
|
||||
|
||||
newy = tmp->tm_year;
|
||||
if (t < sp->ats[0])
|
||||
newy -= icycles * YEARSPERREPEAT;
|
||||
else newy += icycles * YEARSPERREPEAT;
|
||||
tmp->tm_year = newy;
|
||||
if (tmp->tm_year != newy)
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (sp->timecnt == 0 || t < sp->ats[0])
|
||||
{
|
||||
i = 0;
|
||||
@ -810,39 +1048,49 @@ localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *t
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 1; i < sp->timecnt; ++i)
|
||||
if (t < sp->ats[i])
|
||||
break;
|
||||
i = sp->types[i - 1];
|
||||
int lo = 1;
|
||||
int hi = sp->timecnt;
|
||||
|
||||
while (lo < hi)
|
||||
{
|
||||
int mid = (lo + hi) >> 1;
|
||||
|
||||
if (t < sp->ats[mid])
|
||||
hi = mid;
|
||||
else lo = mid + 1;
|
||||
}
|
||||
i = (int) sp->types[lo - 1];
|
||||
}
|
||||
ttisp = &sp->ttis[i];
|
||||
|
||||
timesub(&t, ttisp->tt_gmtoff, sp, tmp);
|
||||
result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
|
||||
tmp->tm_isdst = ttisp->tt_isdst;
|
||||
tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
struct pg_tm *
|
||||
pg_localtime(const pg_time_t *timep, const pg_tz *tz)
|
||||
{
|
||||
localsub(timep, 0L, &tm, tz);
|
||||
return &tm;
|
||||
return localsub(timep, 0L, &tm, tz);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gmtsub is to gmtime as localsub is to localtime.
|
||||
*/
|
||||
static void
|
||||
gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
|
||||
static struct pg_tm *
|
||||
gmtsub(const pg_time_t *timep, long offset, struct pg_tm *tmp)
|
||||
{
|
||||
struct pg_tm *result;
|
||||
|
||||
if (!gmt_is_set)
|
||||
{
|
||||
gmt_is_set = TRUE;
|
||||
gmtload(gmtptr);
|
||||
}
|
||||
timesub(timep, offset, gmtptr, tmp);
|
||||
result = timesub(timep, offset, gmtptr, tmp);
|
||||
|
||||
/*
|
||||
* Could get fancy here and deliver something such as "UTC+xxxx" or
|
||||
@ -853,28 +1101,37 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
|
||||
tmp->tm_zone = wildabbr;
|
||||
else
|
||||
tmp->tm_zone = gmtptr->chars;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct pg_tm *
|
||||
pg_gmtime(const pg_time_t *timep)
|
||||
{
|
||||
gmtsub(timep, 0L, &tm);
|
||||
return &tm;
|
||||
return gmtsub(timep, 0L, &tm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of leap years through the end of the given year
|
||||
* where, to make the math easy, the answer for year zero is defined as zero.
|
||||
*/
|
||||
static int
|
||||
leaps_thru_end_of(const int y)
|
||||
{
|
||||
return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
|
||||
-(leaps_thru_end_of(-(y + 1)) + 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static struct pg_tm *
|
||||
timesub(const pg_time_t *timep, long offset,
|
||||
const struct state * sp, struct pg_tm * tmp)
|
||||
const struct state *sp, struct pg_tm *tmp)
|
||||
{
|
||||
const struct lsinfo *lp;
|
||||
|
||||
/* expand days to 64 bits to support full Julian-day range */
|
||||
int64 days;
|
||||
int idays;
|
||||
pg_time_t tdays;
|
||||
int idays; /* unsigned would be so 2003 */
|
||||
long rem;
|
||||
int y;
|
||||
int yleap;
|
||||
const int *ip;
|
||||
long corr;
|
||||
int hit;
|
||||
@ -907,74 +1164,111 @@ timesub(const pg_time_t *timep, long offset,
|
||||
break;
|
||||
}
|
||||
}
|
||||
days = *timep / SECSPERDAY;
|
||||
rem = *timep % SECSPERDAY;
|
||||
#ifdef mc68k
|
||||
if (*timep == 0x80000000)
|
||||
y = EPOCH_YEAR;
|
||||
tdays = *timep / SECSPERDAY;
|
||||
rem = *timep - tdays * SECSPERDAY;
|
||||
while (tdays < 0 || tdays >= year_lengths[isleap(y)])
|
||||
{
|
||||
/*
|
||||
* A 3B1 muffs the division on the most negative number.
|
||||
*/
|
||||
days = -24855;
|
||||
rem = -11648;
|
||||
int newy;
|
||||
pg_time_t tdelta;
|
||||
int idelta;
|
||||
int leapdays;
|
||||
|
||||
tdelta = tdays / DAYSPERLYEAR;
|
||||
idelta = tdelta;
|
||||
if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
|
||||
return NULL;
|
||||
if (idelta == 0)
|
||||
idelta = (tdays < 0) ? -1 : 1;
|
||||
newy = y;
|
||||
if (increment_overflow(&newy, idelta))
|
||||
return NULL;
|
||||
leapdays = leaps_thru_end_of(newy - 1) -
|
||||
leaps_thru_end_of(y - 1);
|
||||
tdays -= ((pg_time_t) newy - y) * DAYSPERNYEAR;
|
||||
tdays -= leapdays;
|
||||
y = newy;
|
||||
}
|
||||
#endif /* defined mc68k */
|
||||
rem += (offset - corr);
|
||||
{
|
||||
long seconds;
|
||||
|
||||
seconds = tdays * SECSPERDAY + 0.5;
|
||||
tdays = seconds / SECSPERDAY;
|
||||
rem += seconds - tdays * SECSPERDAY;
|
||||
}
|
||||
/*
|
||||
* Given the range, we can now fearlessly cast...
|
||||
*/
|
||||
idays = tdays;
|
||||
rem += offset - corr;
|
||||
while (rem < 0)
|
||||
{
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
--idays;
|
||||
}
|
||||
while (rem >= SECSPERDAY)
|
||||
{
|
||||
rem -= SECSPERDAY;
|
||||
++days;
|
||||
++idays;
|
||||
}
|
||||
while (idays < 0)
|
||||
{
|
||||
if (increment_overflow(&y, -1))
|
||||
return NULL;
|
||||
idays += year_lengths[isleap(y)];
|
||||
}
|
||||
while (idays >= year_lengths[isleap(y)])
|
||||
{
|
||||
idays -= year_lengths[isleap(y)];
|
||||
if (increment_overflow(&y, 1))
|
||||
return NULL;
|
||||
}
|
||||
tmp->tm_year = y;
|
||||
if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
|
||||
return NULL;
|
||||
tmp->tm_yday = idays;
|
||||
/*
|
||||
* The "extra" mods below avoid overflow problems.
|
||||
*/
|
||||
tmp->tm_wday = EPOCH_WDAY +
|
||||
((y - EPOCH_YEAR) % DAYSPERWEEK) *
|
||||
(DAYSPERNYEAR % DAYSPERWEEK) +
|
||||
leaps_thru_end_of(y - 1) -
|
||||
leaps_thru_end_of(EPOCH_YEAR - 1) +
|
||||
idays;
|
||||
tmp->tm_wday %= DAYSPERWEEK;
|
||||
if (tmp->tm_wday < 0)
|
||||
tmp->tm_wday += DAYSPERWEEK;
|
||||
tmp->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
rem = rem % SECSPERHOUR;
|
||||
rem %= SECSPERHOUR;
|
||||
tmp->tm_min = (int) (rem / SECSPERMIN);
|
||||
|
||||
/*
|
||||
* A positive leap second requires a special representation. This uses
|
||||
* A positive leap second requires a special representation. This uses
|
||||
* "... ??:59:60" et seq.
|
||||
*/
|
||||
tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
|
||||
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
|
||||
if (tmp->tm_wday < 0)
|
||||
tmp->tm_wday += DAYSPERWEEK;
|
||||
y = EPOCH_YEAR;
|
||||
|
||||
/*
|
||||
* Note: the point of adding 4800 is to ensure we make the same
|
||||
* assumptions as Postgres' Julian-date routines about the placement of
|
||||
* leap years in centuries BC, at least back to 4713BC which is as far as
|
||||
* we'll go. This is effectively extending Gregorian timekeeping into
|
||||
* pre-Gregorian centuries, which is a tad bogus but it conforms to the
|
||||
* SQL spec...
|
||||
*/
|
||||
#define LEAPS_THRU_END_OF(y) (((y) + 4800) / 4 - ((y) + 4800) / 100 + ((y) + 4800) / 400)
|
||||
while (days < 0 || days >= (int64) year_lengths[yleap = isleap(y)])
|
||||
{
|
||||
int newy;
|
||||
|
||||
newy = y + days / DAYSPERNYEAR;
|
||||
if (days < 0)
|
||||
--newy;
|
||||
days -= ((int64) (newy - y)) * DAYSPERNYEAR +
|
||||
LEAPS_THRU_END_OF(newy - 1) -
|
||||
LEAPS_THRU_END_OF(y - 1);
|
||||
y = newy;
|
||||
}
|
||||
tmp->tm_year = y - TM_YEAR_BASE;
|
||||
idays = (int) days; /* no longer have a range problem */
|
||||
tmp->tm_yday = idays;
|
||||
ip = mon_lengths[yleap];
|
||||
for (i = 0; idays >= ip[i]; ++i)
|
||||
idays -= ip[i];
|
||||
tmp->tm_mon = i;
|
||||
tmp->tm_mday = idays + 1;
|
||||
ip = mon_lengths[isleap(y)];
|
||||
for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
|
||||
idays -= ip[tmp->tm_mon];
|
||||
tmp->tm_mday = (int) (idays + 1);
|
||||
tmp->tm_isdst = 0;
|
||||
tmp->tm_gmtoff = offset;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simplified normalize logic courtesy Paul Eggert.
|
||||
*/
|
||||
|
||||
static int
|
||||
increment_overflow(int *number, int delta)
|
||||
{
|
||||
int number0;
|
||||
|
||||
number0 = *number;
|
||||
*number += delta;
|
||||
return (*number < number0) != (delta < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1027,6 +1321,48 @@ pg_next_dst_boundary(const pg_time_t *timep,
|
||||
*before_isdst = ttisp->tt_isdst;
|
||||
return 0;
|
||||
}
|
||||
if ((sp->goback && t < sp->ats[0]) ||
|
||||
(sp->goahead && t > sp->ats[sp->timecnt - 1]))
|
||||
{
|
||||
/* For values outside the transition table, extrapolate */
|
||||
pg_time_t newt = t;
|
||||
pg_time_t seconds;
|
||||
pg_time_t tcycles;
|
||||
int64 icycles;
|
||||
int result;
|
||||
|
||||
if (t < sp->ats[0])
|
||||
seconds = sp->ats[0] - t;
|
||||
else seconds = t - sp->ats[sp->timecnt - 1];
|
||||
--seconds;
|
||||
tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
|
||||
++tcycles;
|
||||
icycles = tcycles;
|
||||
if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
|
||||
return -1;
|
||||
seconds = icycles;
|
||||
seconds *= YEARSPERREPEAT;
|
||||
seconds *= AVGSECSPERYEAR;
|
||||
if (t < sp->ats[0])
|
||||
newt += seconds;
|
||||
else newt -= seconds;
|
||||
if (newt < sp->ats[0] ||
|
||||
newt > sp->ats[sp->timecnt - 1])
|
||||
return -1; /* "cannot happen" */
|
||||
|
||||
result = pg_next_dst_boundary(&newt, before_gmtoff,
|
||||
before_isdst,
|
||||
boundary,
|
||||
after_gmtoff,
|
||||
after_isdst,
|
||||
tz);
|
||||
if (t < sp->ats[0])
|
||||
*boundary -= seconds;
|
||||
else
|
||||
*boundary += seconds;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (t > sp->ats[sp->timecnt - 1])
|
||||
{
|
||||
/* No known transition >= t, so use last known segment's type */
|
||||
@ -1058,9 +1394,20 @@ pg_next_dst_boundary(const pg_time_t *timep,
|
||||
return 1;
|
||||
}
|
||||
/* Else search to find the containing segment */
|
||||
for (i = 1; i < sp->timecnt; ++i)
|
||||
if (t <= sp->ats[i])
|
||||
break;
|
||||
{
|
||||
int lo = 1;
|
||||
int hi = sp->timecnt;
|
||||
|
||||
while (lo < hi)
|
||||
{
|
||||
int mid = (lo + hi) >> 1;
|
||||
|
||||
if (t < sp->ats[mid])
|
||||
hi = mid;
|
||||
else lo = mid + 1;
|
||||
}
|
||||
i = lo;
|
||||
}
|
||||
j = sp->types[i - 1];
|
||||
ttisp = &sp->ttis[j];
|
||||
*before_gmtoff = ttisp->tt_gmtoff;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
*
|
||||
* 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)
|
||||
* Load timezone directly. Don't use pg_tzset, because we don't want all
|
||||
* 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)
|
||||
{
|
||||
@ -1191,7 +1191,7 @@ pg_tzset(const char *name)
|
||||
return &tzp->tz;
|
||||
}
|
||||
|
||||
if (tzload(uppername, canonname, &tzstate) != 0)
|
||||
if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
|
||||
{
|
||||
if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
|
||||
{
|
||||
@ -1463,7 +1463,8 @@ pg_tzenumerate_next(pg_tzenum *dir)
|
||||
* Load this timezone using tzload() not pg_tzset(), so we don't fill
|
||||
* 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 */
|
||||
continue;
|
||||
|
@ -9,7 +9,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
*
|
||||
* 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
|
||||
int timecnt;
|
||||
int typecnt;
|
||||
int charcnt;
|
||||
int goback;
|
||||
int goahead;
|
||||
pg_time_t ats[TZ_MAX_TIMES];
|
||||
unsigned char types[TZ_MAX_TIMES];
|
||||
struct ttinfo ttis[TZ_MAX_TYPES];
|
||||
@ -64,7 +66,8 @@ struct pg_tz
|
||||
extern int pg_open_tzfile(const char *name, char *canonname);
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* _PGTZ_H */
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
/*
|
||||
* This file is in the public domain, so clarified as of
|
||||
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
|
||||
* 1996-06-05 by Arthur David Olson.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/timezone/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 @@
|
||||
* 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 <unistd.h> /* for F_OK and R_OK */
|
||||
|
||||
#include "pgtime.h"
|
||||
|
||||
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
|
||||
|
||||
#ifndef WIFEXITED
|
||||
#define WIFEXITED(status) (((status) & 0xff) == 0)
|
||||
@ -34,22 +35,6 @@
|
||||
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
|
||||
#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.
|
||||
*/
|
||||
@ -70,7 +55,7 @@ extern char *imalloc(int n);
|
||||
extern void *irealloc(void *pointer, int size);
|
||||
extern void icfree(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);
|
||||
#define TYPE_SIGNED(type) (((type) -1) < 0)
|
||||
#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
|
||||
/*
|
||||
* 302 / 1000 is log10(2.0) rounded up.
|
||||
@ -107,6 +101,26 @@ extern char *scheck(const char *string, const char *format);
|
||||
#undef _
|
||||
#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.
|
||||
*/
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* 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
|
||||
* $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"
|
||||
@ -11,18 +11,17 @@
|
||||
#include "private.h"
|
||||
|
||||
|
||||
char *
|
||||
const char *
|
||||
scheck(const char *string, const char *format)
|
||||
{
|
||||
char *fbuf;
|
||||
const char *fp;
|
||||
char *tp;
|
||||
int c;
|
||||
char *result;
|
||||
const char *result;
|
||||
char dummy;
|
||||
static char nada;
|
||||
|
||||
result = &nada;
|
||||
result = "";
|
||||
if (string == NULL || format == NULL)
|
||||
return result;
|
||||
fbuf = imalloc((int) (2 * strlen(format) + 4));
|
||||
|
@ -7,7 +7,7 @@
|
||||
* duplicated in all such forms and that any documentation,
|
||||
* advertising materials, and other materials related to such
|
||||
* 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
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
@ -15,7 +15,7 @@
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* 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"
|
||||
@ -92,6 +92,7 @@ static char *_add(const char *, char *, const char *);
|
||||
static char *_conv(int, const char *, char *, const char *);
|
||||
static char *_fmt(const char *, const struct pg_tm *, char *,
|
||||
const char *, int *);
|
||||
static char * _yconv(int, int, int, int, char *, const char *);
|
||||
|
||||
#define IN_NONE 0
|
||||
#define IN_SOME 1
|
||||
@ -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
|
||||
* completely different. (ado, 1993-05-24)
|
||||
*/
|
||||
pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
|
||||
"%02d", pt, ptlim);
|
||||
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
|
||||
pt, ptlim);
|
||||
continue;
|
||||
case 'c':
|
||||
{
|
||||
@ -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
|
||||
* % 12 : 12, 2, ' '); ...and has been changed to the
|
||||
* 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)
|
||||
*/
|
||||
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,
|
||||
case 'G': /* ISO 8601 year (four 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
|
||||
* (01-53)."
|
||||
* (ado, 1993-05-24)
|
||||
@ -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
|
||||
* 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)
|
||||
* 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..."
|
||||
* (ado, 1996-01-02)
|
||||
*/
|
||||
{
|
||||
int year;
|
||||
int base;
|
||||
int yday;
|
||||
int wday;
|
||||
int w;
|
||||
|
||||
year = t->tm_year + TM_YEAR_BASE;
|
||||
year = t->tm_year;
|
||||
base = TM_YEAR_BASE;
|
||||
yday = t->tm_yday;
|
||||
wday = t->tm_wday;
|
||||
for (;;)
|
||||
@ -321,7 +324,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
||||
int bot;
|
||||
int top;
|
||||
|
||||
len = isleap(year) ?
|
||||
len = isleap_sum(year, base) ?
|
||||
DAYSPERLYEAR :
|
||||
DAYSPERNYEAR;
|
||||
|
||||
@ -342,7 +345,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
||||
top += len;
|
||||
if (yday >= top)
|
||||
{
|
||||
++year;
|
||||
++base;
|
||||
w = 1;
|
||||
break;
|
||||
}
|
||||
@ -352,8 +355,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
||||
DAYSPERWEEK);
|
||||
break;
|
||||
}
|
||||
--year;
|
||||
yday += isleap(year) ?
|
||||
--base;
|
||||
yday += isleap_sum(year, base) ?
|
||||
DAYSPERLYEAR :
|
||||
DAYSPERNYEAR;
|
||||
}
|
||||
@ -363,11 +366,11 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
||||
else if (*format == 'g')
|
||||
{
|
||||
*warnp = IN_ALL;
|
||||
pt = _conv(year % 100, "%02d",
|
||||
pt = _yconv(year, base, 0, 1,
|
||||
pt, ptlim);
|
||||
}
|
||||
else
|
||||
pt = _conv(year, "%04d",
|
||||
pt = _yconv(year, base, 1, 1,
|
||||
pt, ptlim);
|
||||
}
|
||||
continue;
|
||||
@ -405,12 +408,12 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
||||
continue;
|
||||
case 'y':
|
||||
*warnp = IN_ALL;
|
||||
pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
|
||||
"%02d", pt, ptlim);
|
||||
pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
|
||||
pt, ptlim);
|
||||
continue;
|
||||
case 'Y':
|
||||
pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
|
||||
pt, ptlim);
|
||||
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
|
||||
pt, ptlim);
|
||||
continue;
|
||||
case 'Z':
|
||||
if (t->tm_zone != NULL)
|
||||
@ -480,3 +483,43 @@ _add(const char *str, char *pt, const char *ptlim)
|
||||
++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 @@
|
||||
|
||||
/*
|
||||
* This file is in the public domain, so clarified as of
|
||||
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
|
||||
* 1996-06-05 by Arthur David Olson.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/timezone/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 @@
|
||||
struct tzhead
|
||||
{
|
||||
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_ttisstdcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_leapcnt[4]; /* coded number of leap seconds */
|
||||
@ -68,18 +69,22 @@ struct tzhead
|
||||
*----------
|
||||
*/
|
||||
|
||||
/*
|
||||
* If tzh_version is '2' or greater, the above is followed by a second instance
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/*
|
||||
* In the current implementation, "tzset()" refuses to deal with files that
|
||||
* exceed any of the limits below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The TZ_MAX_TIMES value below is enough to handle a bit more than a
|
||||
* year's worth of solar time (corrected daily to the nearest second) or
|
||||
* 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 */
|
||||
|
||||
@ -124,11 +129,20 @@ struct tzhead
|
||||
#define EPOCH_YEAR 1970
|
||||
#define EPOCH_WDAY TM_THURSDAY
|
||||
|
||||
/*
|
||||
* Accurate only for the past couple of centuries;
|
||||
* that will probably do.
|
||||
*/
|
||||
|
||||
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
|
||||
|
||||
/*
|
||||
* Since everything in isleap is modulo 400 (or a factor of 400), we know that
|
||||
* 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_sum(a, b) isleap((a) % 400 + (b) % 400)
|
||||
|
||||
#endif /* !defined TZFILE_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user