Time Zone code
==============
-The code in this directory is currently synced with tzcode release 2019b.
+The code in this directory is currently synced with tzcode release 2020a.
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. Here are some notes about that.
* We need the code to follow Postgres' portability conventions; this
includes relying on configure's results rather than hand-hacked
-#defines (see private.h).
+#defines (see private.h in particular).
+
+* Similarly, avoid relying on <stdint.h> features that may not exist on old
+systems. In particular this means using Postgres' definitions of the int32
+and int64 typedefs, not int_fast32_t/int_fast64_t. Likewise we use
+PG_INT32_MIN/MAX not INT32_MIN/MAX. (Once we desupport all PG versions
+that don't require C99, it'd be practical to rely on <stdint.h> and remove
+this set of diffs; but that day is not yet.)
* Since Postgres is typically built on a system that has its own copy
of the <time.h> functions, we must avoid conflicting with those. This
-e 's|^\*/| */|' \
-e 's/\bregister[ \t]//g' \
-e 's/\bATTRIBUTE_PURE[ \t]//g' \
+ -e 's/int_fast32_t/int32/g' \
+ -e 's/int_fast64_t/int64/g' \
+ -e 's/intmax_t/int64/g' \
+ -e 's/INT32_MIN/PG_INT32_MIN/g' \
+ -e 's/INT32_MAX/PG_INT32_MAX/g' \
+ -e 's/INTMAX_MIN/PG_INT64_MIN/g' \
+ -e 's/INTMAX_MAX/PG_INT64_MAX/g' \
-e 's/struct[ \t]+tm\b/struct pg_tm/g' \
-e 's/\btime_t\b/pg_time_t/g' \
-e 's/lineno/lineno_t/g' \
static void usage(FILE *stream, int status) pg_attribute_noreturn();
static void addtt(zic_t starttime, int type);
static int addtype(zic_t, char const *, bool, bool, bool);
-static void leapadd(zic_t, bool, int, int);
+static void leapadd(zic_t, int, int);
static void adjleap(void);
static void associate(void);
static void dolink(const char *, const char *, bool);
static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring);
static zic_t getsave(char *, bool *);
+static void inexpires(char **, int);
static void infile(const char *filename);
static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields);
#define LC_ZONE 1
#define LC_LINK 2
#define LC_LEAP 3
+#define LC_EXPIRES 4
/*
* Which fields are which on a Zone line.
#define LP_ROLL 6
#define LEAP_FIELDS 7
+/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+#define EXPIRES_FIELDS 5
+
/*
* Year synonyms.
*/
};
static struct lookup const leap_line_codes[] = {
{"Leap", LC_LEAP},
+ {"Expires", LC_EXPIRES},
{NULL, 0}
};
static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+/* The time specified by an Expires line, or negative if no such line. */
+static zic_t leapexpires = -1;
+
+/* The time specified by an #expires comment, or negative if no such line. */
+static zic_t comment_leapexpires = -1;
+
/* Set the time range of the output to TIMERANGE.
Return true if successful. */
static bool
}
if (nfields == 0)
{
- /* nothing to do */
+ if (name == leapsec && *buf == '#')
+ sscanf(buf, "#expires " INT64_FORMAT, &comment_leapexpires);
}
else if (wantcont)
{
inleap(fields, nfields);
wantcont = false;
break;
+ case LC_EXPIRES:
+ inexpires(fields, nfields);
+ wantcont = false;
+ break;
default: /* "cannot happen" */
fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"),
return hasuntil;
}
-static void
-inleap(char **fields, int nfields)
+static zic_t
+getleapdatetime(char **fields, int nfields, bool expire_line)
{
const char *cp;
const struct lookup *lp;
zic_t t;
char xs;
- if (nfields != LEAP_FIELDS)
- {
- error(_("wrong number of fields on Leap line"));
- return;
- }
dayoff = 0;
cp = fields[LP_YEAR];
if (sscanf(cp, "%d%c", &year, &xs) != 1)
* Leapin' Lizards!
*/
error(_("invalid leaping year"));
- return;
+ return -1;
+ }
+ if (!expire_line)
+ {
+ if (!leapseen || leapmaxyear < year)
+ leapmaxyear = year;
+ if (!leapseen || leapminyear > year)
+ leapminyear = year;
+ leapseen = true;
}
- if (!leapseen || leapmaxyear < year)
- leapmaxyear = year;
- if (!leapseen || leapminyear > year)
- leapminyear = year;
- leapseen = true;
j = EPOCH_YEAR;
while (j != year)
{
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL)
{
error(_("invalid month name"));
- return;
+ return -1;
}
month = lp->l_value;
j = TM_JANUARY;
day <= 0 || day > len_months[isleap(year)][month])
{
error(_("invalid day of month"));
- return;
+ return -1;
}
dayoff = oadd(dayoff, day - 1);
if (dayoff < min_time / SECSPERDAY)
{
error(_("time too small"));
- return;
+ return -1;
}
if (dayoff > max_time / SECSPERDAY)
{
error(_("time too large"));
- return;
+ return -1;
}
t = dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"));
- cp = fields[LP_CORR];
+ t = tadd(t, tod);
+ if (t < 0)
+ error(_("leap second precedes Epoch"));
+ return t;
+}
+
+static void
+inleap(char **fields, int nfields)
+{
+ if (nfields != LEAP_FIELDS)
+ error(_("wrong number of fields on Leap line"));
+ else
{
- bool positive;
- int count;
+ zic_t t = getleapdatetime(fields, nfields, false);
- if (strcmp(cp, "") == 0)
- { /* infile() turns "-" into "" */
- positive = false;
- count = 1;
- }
- else if (strcmp(cp, "+") == 0)
+ if (0 <= t)
{
- positive = true;
- count = 1;
- }
- else
- {
- error(_("illegal CORRECTION field on Leap line"));
- return;
- }
- if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL)
- {
- error(_("illegal Rolling/Stationary field on Leap line"));
- return;
- }
- t = tadd(t, tod);
- if (t < 0)
- {
- error(_("leap second precedes Epoch"));
- return;
+ struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
+
+ if (!lp)
+ error(_("invalid Rolling/Stationary field on Leap line"));
+ else
+ {
+ int correction = 0;
+
+ if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
+ correction = -1;
+ else if (strcmp(fields[LP_CORR], "+") == 0)
+ correction = 1;
+ else
+ error(_("invalid CORRECTION field on Leap line"));
+ if (correction)
+ leapadd(t, correction, lp->l_value);
+ }
}
- leapadd(t, positive, lp->l_value, count);
}
}
+static void
+inexpires(char **fields, int nfields)
+{
+ if (nfields != EXPIRES_FIELDS)
+ error(_("wrong number of fields on Expires line"));
+ else if (0 <= leapexpires)
+ error(_("multiple Expires lines"));
+ else
+ leapexpires = getleapdatetime(fields, nfields, true);
+}
+
static void
inlink(char **fields, int nfields)
{
}
static void
-leapadd(zic_t t, bool positive, int rolling, int count)
+leapadd(zic_t t, int correction, int rolling)
{
- int i,
- j;
+ int i;
- if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS)
+ if (TZ_MAX_LEAPS <= leapcnt)
{
error(_("too many leap seconds"));
exit(EXIT_FAILURE);
for (i = 0; i < leapcnt; ++i)
if (t <= trans[i])
break;
- do
- {
- for (j = leapcnt; j > i; --j)
- {
- trans[j] = trans[j - 1];
- corr[j] = corr[j - 1];
- roll[j] = roll[j - 1];
- }
- trans[i] = t;
- corr[i] = positive ? 1 : -count;
- roll[i] = rolling;
- ++leapcnt;
- } while (positive && --count != 0);
+ memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
+ memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
+ memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
+ trans[i] = t;
+ corr[i] = correction;
+ roll[i] = rolling;
+ ++leapcnt;
}
static void
trans[i] = tadd(trans[i], last);
last = corr[i] += last;
}
+
+ if (leapexpires < 0)
+ {
+ leapexpires = comment_leapexpires;
+ if (0 <= leapexpires)
+ warning(_("\"#expires\" is obsolescent; use \"Expires\""));
+ }
+
+ if (0 <= leapexpires)
+ {
+ leapexpires = oadd(leapexpires, last);
+ if (!(leapcnt == 0 || (trans[leapcnt - 1] < leapexpires)))
+ {
+ error(_("last Leap time does not precede Expires time"));
+ exit(EXIT_FAILURE);
+ }
+ if (leapexpires <= hi_time)
+ hi_time = leapexpires - 1;
+ }
}
static char *