aboutsummaryrefslogtreecommitdiff
path: root/src/timezone/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r--src/timezone/localtime.c126
1 files changed, 87 insertions, 39 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index e3029d841d7..96e62aff3b4 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -1,3 +1,5 @@
+/* Convert timestamp from pg_time_t to struct pg_tm. */
+
/*
* This file is in the public domain, so clarified as of
* 1996-06-05 by Arthur David Olson.
@@ -117,7 +119,7 @@ init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind)
}
static int32
-detzcode(const char *codep)
+detzcode(const char *const codep)
{
int32 result;
int i;
@@ -143,7 +145,7 @@ detzcode(const char *codep)
}
static int64
-detzcode64(const char *codep)
+detzcode64(const char *const codep)
{
uint64 result;
int i;
@@ -258,8 +260,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
int32 charcnt = detzcode(up->tzhead.tzh_charcnt);
char const *p = up->buf + tzheadsize;
+ /*
+ * Although tzfile(5) currently requires typecnt to be nonzero,
+ * support future formats that may allow zero typecnt in files that
+ * have a TZ string and no transitions.
+ */
if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
- && 0 < typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= typecnt && typecnt < TZ_MAX_TYPES
&& 0 <= timecnt && timecnt < TZ_MAX_TIMES
&& 0 <= charcnt && charcnt < TZ_MAX_CHARS
&& (ttisstdcnt == typecnt || ttisstdcnt == 0)
@@ -416,8 +423,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
struct state *ts = &lsp->u.st;
up->buf[nread - 1] = '\0';
- if (tzparse(&up->buf[1], ts, false)
- && ts->typecnt == 2)
+ if (tzparse(&up->buf[1], ts, false))
{
/*
* Attempt to reuse existing abbreviations. Without this,
@@ -430,7 +436,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
int gotabbr = 0;
int charcnt = sp->charcnt;
- for (i = 0; i < 2; i++)
+ for (i = 0; i < ts->typecnt; i++)
{
char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
int j;
@@ -455,7 +461,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
}
}
}
- if (gotabbr == 2)
+ if (gotabbr == ts->typecnt)
{
sp->charcnt = charcnt;
@@ -470,7 +476,8 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
sp->timecnt--;
for (i = 0; i < ts->timecnt; i++)
- if (sp->ats[sp->timecnt - 1] < ts->ats[i])
+ if (sp->timecnt == 0
+ || sp->ats[sp->timecnt - 1] < ts->ats[i])
break;
while (i < ts->timecnt
&& sp->timecnt < TZ_MAX_TIMES)
@@ -481,11 +488,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
sp->timecnt++;
i++;
}
- sp->ttis[sp->typecnt++] = ts->ttis[0];
- sp->ttis[sp->typecnt++] = ts->ttis[1];
+ for (i = 0; i < ts->typecnt; i++)
+ sp->ttis[sp->typecnt++] = ts->ttis[i];
}
}
}
+ if (sp->typecnt == 0)
+ return EINVAL;
if (sp->timecnt > 1)
{
for (i = 1; i < sp->timecnt; ++i)
@@ -507,6 +516,18 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
}
/*
+ * Infer sp->defaulttype from the data. Although this default type is
+ * always zero for data from recent tzdb releases, things are trickier for
+ * data from tzdb 2018e or earlier.
+ *
+ * The first set of heuristics work around bugs in 32-bit data generated
+ * by tzdb 2013c or earlier. The workaround is for zones like
+ * Australia/Macquarie where timestamps before the first transition have a
+ * time type that is not the earliest standard-time type. See:
+ * https://mm.icann.org/pipermail/tz/2013-May/019368.html
+ */
+
+ /*
* If type 0 is unused in transitions, it's the type to use for early
* times.
*/
@@ -529,6 +550,11 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
}
/*
+ * The next heuristics are for data generated by tzdb 2018e or earlier,
+ * for zones like EST5EDT where the first transition is to DST.
+ */
+
+ /*
* If no result yet, find the first standard type. If there is none, punt
* to type zero.
*/
@@ -542,7 +568,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
break;
}
}
+
+ /*
+ * A simple 'sp->defaulttype = 0;' would suffice here if we didn't have to
+ * worry about 2018e-or-earlier data. Even simpler would be to remove the
+ * defaulttype member and just use 0 in its place.
+ */
sp->defaulttype = i;
+
return 0;
}
@@ -601,10 +634,11 @@ 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
- * character.
+ * Given a pointer into a timezone string, scan until a character that is not
+ * a valid character in a time zone abbreviation is found.
+ * Return a pointer to that character.
*/
+
static const char *
getzname(const char *strp)
{
@@ -617,15 +651,17 @@ getzname(const char *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.
+ * Given a pointer into an extended timezone string, scan until the ending
+ * delimiter of the time zone abbreviation 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)
+getqzname(const char *strp, const int delim)
{
int c;
@@ -635,13 +671,14 @@ getqzname(const char *strp, int delim)
}
/*
- * Given a pointer into a time zone string, extract a number from that string.
+ * Given a pointer into a timezone string, extract a number from that string.
* Check that the number is within a specified range; if it is not, return
* NULL.
* Otherwise, return a pointer to the first character not part of the number.
*/
+
static const char *
-getnum(const char *strp, int *nump, int min, int max)
+getnum(const char *strp, int *const nump, const int min, const int max)
{
char c;
int num;
@@ -663,14 +700,15 @@ getnum(const char *strp, int *nump, int min, int max)
}
/*
- * Given a pointer into a time zone string, extract a number of seconds,
+ * Given a pointer into a timezone string, extract a number of seconds,
* in hh[:mm[:ss]] form, from the string.
* If any error occurs, return NULL.
* Otherwise, return a pointer to the first character not part of the number
* of seconds.
*/
+
static const char *
-getsecs(const char *strp, int32 *secsp)
+getsecs(const char *strp, int32 *const secsp)
{
int num;
@@ -704,13 +742,14 @@ getsecs(const char *strp, int32 *secsp)
}
/*
- * Given a pointer into a time zone string, extract an offset, in
+ * Given a pointer into a timezone string, extract an offset, in
* [+-]hh[:mm[:ss]] form, from the string.
* If any error occurs, return NULL.
* Otherwise, return a pointer to the first character not part of the time.
*/
+
static const char *
-getoffset(const char *strp, int32 *offsetp)
+getoffset(const char *strp, int32 *const offsetp)
{
bool neg = false;
@@ -730,13 +769,14 @@ getoffset(const char *strp, int32 *offsetp)
}
/*
- * Given a pointer into a time zone string, extract a rule in the form
+ * Given a pointer into a timezone string, extract a rule in the form
* 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.
*/
+
static const char *
-getrule(const char *strp, struct rule *rulep)
+getrule(const char *strp, struct rule *const rulep)
{
if (*strp == 'J')
{
@@ -795,9 +835,10 @@ getrule(const char *strp, struct rule *rulep)
* Given a year, a rule, and the offset from UT at the time that rule takes
* effect, calculate the year-relative time that rule takes effect.
*/
+
static int32
-transtime(int year, const struct rule *rulep,
- int32 offset)
+transtime(const int year, const struct rule *const rulep,
+ const int32 offset)
{
bool leapyear;
int32 value;
@@ -967,7 +1008,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
{
dstname = name;
name = getzname(name);
- dstlen = name - dstname; /* length of DST zone name */
+ dstlen = name - dstname; /* length of DST abbr. */
}
if (!dstlen)
return false;
@@ -1039,8 +1080,8 @@ tzparse(const char *name, struct state *sp, bool lastditch)
/*
* Two transitions per year, from EPOCH_YEAR forward.
*/
- init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1);
- init_ttinfo(&sp->ttis[1], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
sp->defaulttype = 0;
timecnt = 0;
janfirst = 0;
@@ -1089,19 +1130,15 @@ tzparse(const char *name, struct state *sp, bool lastditch)
if (!increment_overflow_time
(&sp->ats[timecnt],
janoffset + starttime))
- sp->types[timecnt++] = reversed;
- else if (janoffset)
- sp->defaulttype = reversed;
+ sp->types[timecnt++] = !reversed;
sp->ats[timecnt] = janfirst;
if (!increment_overflow_time
(&sp->ats[timecnt],
janoffset + endtime))
{
- sp->types[timecnt++] = !reversed;
+ sp->types[timecnt++] = reversed;
yearlim = year + YEARSPERREPEAT + 1;
}
- else if (janoffset)
- sp->defaulttype = !reversed;
}
if (increment_overflow_time
(&janfirst, janoffset + yearsecs))
@@ -1110,7 +1147,10 @@ tzparse(const char *name, struct state *sp, bool lastditch)
}
sp->timecnt = timecnt;
if (!timecnt)
+ {
+ sp->ttis[0] = sp->ttis[1];
sp->typecnt = 1; /* Perpetual DST. */
+ }
else if (YEARSPERREPEAT < year - yearbeg)
sp->goback = sp->goahead = true;
}
@@ -1179,7 +1219,6 @@ tzparse(const char *name, struct state *sp, bool lastditch)
* otherwise, add the standard time offset to the
* transition time.
*/
-
/*
* Transitions from DST to DDST will effectively disappear
* since POSIX provides for only one DST offset.
@@ -1233,7 +1272,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
}
static void
-gmtload(struct state *sp)
+gmtload(struct state *const sp)
{
if (tzload(gmt, NULL, sp, true) != 0)
tzparse(gmt, sp, true);
@@ -1248,7 +1287,7 @@ gmtload(struct state *sp)
*/
static struct pg_tm *
localsub(struct state const *sp, pg_time_t const *timep,
- struct pg_tm *tmp)
+ struct pg_tm *const tmp)
{
const struct ttinfo *ttisp;
int i;
@@ -1316,6 +1355,11 @@ localsub(struct state const *sp, pg_time_t const *timep,
}
ttisp = &sp->ttis[i];
+ /*
+ * To get (wrong) behavior that's compatible with System V Release 2.0
+ * you'd replace the statement below with t += ttisp->tt_gmtoff;
+ * timesub(&t, 0L, sp, tmp);
+ */
result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
if (result)
{
@@ -1338,8 +1382,10 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz)
*
* Except we have a private "struct state" for GMT, so no sp is passed in.
*/
+
static struct pg_tm *
-gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp)
+gmtsub(pg_time_t const *timep, int32 offset,
+ struct pg_tm *tmp)
{
struct pg_tm *result;
@@ -1354,6 +1400,7 @@ gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp)
return NULL; /* errno should be set by malloc */
gmtload(gmtptr);
}
+
result = timesub(timep, offset, gmtptr, tmp);
/*
@@ -1378,6 +1425,7 @@ pg_gmtime(const pg_time_t *timep)
* 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_nonneg(int y)
{