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
|
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';
|
|||||||
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');
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
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
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* 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"
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* 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 */
|
||||||
@ -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
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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;
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
(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)
|
|||||||
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)
|
|||||||
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}
|
||||||
@ -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
|
||||||
@ -558,27 +725,48 @@ tzparse(const char *name, struct state * sp, int lastditch)
|
|||||||
load_result = -1;
|
load_result = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (*name == '<')
|
||||||
|
{
|
||||||
|
name++;
|
||||||
|
stdname = name;
|
||||||
|
name = getqzname(name, '>');
|
||||||
|
if (*name != '>')
|
||||||
|
return (-1);
|
||||||
|
stdlen = name - stdname;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
name = getzname(name);
|
name = getzname(name);
|
||||||
stdlen = name - stdname;
|
stdlen = name - stdname;
|
||||||
if (stdlen < 3)
|
}
|
||||||
return -1;
|
|
||||||
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')
|
||||||
|
{
|
||||||
|
if (*name == '<')
|
||||||
|
{
|
||||||
|
dstname = ++name;
|
||||||
|
name = getqzname(name, '>');
|
||||||
|
if (*name != '>')
|
||||||
|
return -1;
|
||||||
|
dstlen = name - dstname;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
dstname = name;
|
dstname = name;
|
||||||
name = getzname(name);
|
name = getzname(name);
|
||||||
dstlen = name - dstname; /* length of DST zone name */
|
dstlen = name - dstname; /* length of DST zone name */
|
||||||
if (dstlen < 3)
|
}
|
||||||
return -1;
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
*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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,15 +984,58 @@ gmtload(struct state * sp)
|
|||||||
*
|
*
|
||||||
* 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
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
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,31 +1164,83 @@ 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;
|
||||||
}
|
}
|
||||||
#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)
|
while (rem < 0)
|
||||||
{
|
{
|
||||||
rem += SECSPERDAY;
|
rem += SECSPERDAY;
|
||||||
--days;
|
--idays;
|
||||||
}
|
}
|
||||||
while (rem >= SECSPERDAY)
|
while (rem >= SECSPERDAY)
|
||||||
{
|
{
|
||||||
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);
|
tmp->tm_hour = (int) (rem / SECSPERHOUR);
|
||||||
rem = rem % SECSPERHOUR;
|
rem %= SECSPERHOUR;
|
||||||
tmp->tm_min = (int) (rem / SECSPERMIN);
|
tmp->tm_min = (int) (rem / SECSPERMIN);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -939,42 +1248,27 @@ timesub(const pg_time_t *timep, long offset,
|
|||||||
* "... ??:59:60" et seq.
|
* "... ??:59:60" et seq.
|
||||||
*/
|
*/
|
||||||
tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
|
tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
|
||||||
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
|
ip = mon_lengths[isleap(y)];
|
||||||
if (tmp->tm_wday < 0)
|
for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
|
||||||
tmp->tm_wday += DAYSPERWEEK;
|
idays -= ip[tmp->tm_mon];
|
||||||
y = EPOCH_YEAR;
|
tmp->tm_mday = (int) (idays + 1);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
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,
|
|||||||
*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,
|
|||||||
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 @@
|
|||||||
* 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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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 @@
|
|||||||
* 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
|
|||||||
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
|
|||||||
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 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
* 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 @@
|
|||||||
/* 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);
|
|||||||
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);
|
|||||||
#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);
|
|||||||
#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.
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
#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));
|
||||||
|
@ -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 *);
|
|||||||
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,
|
|||||||
* ...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':
|
||||||
{
|
{
|
||||||
@ -308,11 +309,13 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,11 +408,11 @@ _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':
|
||||||
@ -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 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
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 */
|
||||||
@ -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
|
* In the current implementation, "tzset()" refuses to deal with files that
|
||||||
* exceed any of the limits below.
|
* exceed any of the limits below.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
#define TZ_MAX_TIMES 1200
|
||||||
* 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_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
|
|||||||
#define EPOCH_YEAR 1970
|
#define EPOCH_YEAR 1970
|
||||||
#define EPOCH_WDAY TM_THURSDAY
|
#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))
|
#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 */
|
#endif /* !defined TZFILE_H */
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user