* in which Daylight Saving Time is never observed.
* 4. They might reference tzname[0] after setting to a time zone
* in which Standard Time is never observed.
- * 5. They might reference tm.TM_ZONE after calling offtime.
+ * 5. They might reference tm.tm_zone after calling offtime.
* What's best to do in the above cases is open to debate;
* 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
static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
static bool increment_overflow(int *, int);
static bool increment_overflow_time(pg_time_t *, int32);
+static int64 leapcorr(struct state const *, pg_time_t);
static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *,
- struct pg_tm *);
+ struct pg_tm *);
static bool typesequiv(struct state const *, int, int);
* Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval.
*/
- result -= !TWOS_COMPLEMENT(int32) &&result != 0;
+ result -= !TWOS_COMPLEMENT(int32) && result != 0;
result += minval;
}
return result;
int64 one = 1;
int64 halfmaxval = one << (64 - 2);
int64 maxval = halfmaxval - 1 + halfmaxval;
- int64 minval = -TWOS_COMPLEMENT(int64) -maxval;
+ int64 minval = -TWOS_COMPLEMENT(int64) - maxval;
result = codep[0] & 0x7f;
for (i = 1; i < 8; ++i)
* Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval.
*/
- result -= !TWOS_COMPLEMENT(int64) &&result != 0;
+ result -= !TWOS_COMPLEMENT(int64) && result != 0;
result += minval;
}
return result;
static bool
differ_by_repeat(const pg_time_t t1, const pg_time_t t0)
{
- if (TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS)
+ if (TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_time_t) < SECSPERREPEAT_BITS)
return 0;
return t1 - t0 == SECSPERREPEAT;
}
for (i = 0; i < ts->timecnt; i++)
if (sp->timecnt == 0
- || sp->ats[sp->timecnt - 1] < ts->ats[i])
+ || (sp->ats[sp->timecnt - 1]
+ < ts->ats[i] + leapcorr(sp, ts->ats[i])))
break;
while (i < ts->timecnt
&& sp->timecnt < TZ_MAX_TIMES)
{
- sp->ats[sp->timecnt] = ts->ats[i];
+ sp->ats[sp->timecnt]
+ = ts->ats[i] + leapcorr(sp, ts->ats[i]);
sp->types[sp->timecnt] = (sp->typecnt
+ ts->types[i]);
sp->timecnt++;
int leapdays;
tdelta = tdays / DAYSPERLYEAR;
- if (!((!TYPE_SIGNED(pg_time_t) ||INT_MIN <= tdelta)
+ if (!((!TYPE_SIGNED(pg_time_t) || INT_MIN <= tdelta)
&& tdelta <= INT_MAX))
goto out_of_range;
idelta = tdelta;
return false;
}
+static int64
+leapcorr(struct state const *sp, pg_time_t t)
+{
+ struct lsinfo const *lp;
+ int i;
+
+ i = sp->leapcnt;
+ while (--i >= 0)
+ {
+ lp = &sp->lsis[i];
+ if (t >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
/*
* Find the next DST transition time in the given zone after the given time
*
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);
static void outzone(const struct zone *zp, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule *rp, zic_t wantedy);
static void rulesub(struct rule *rp,
- const char *loyearp, const char *hiyearp,
- const char *typep, const char *monthp,
- const char *dayp, const char *timep);
+ const char *loyearp, const char *hiyearp,
+ const char *typep, const char *monthp,
+ const char *dayp, const char *timep);
static zic_t tadd(zic_t t1, zic_t t2);
static bool yearistype(zic_t year, const char *type);
#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 *byword(const char *string,
- const struct lookup *lp);
+ const struct lookup *lp);
static struct lookup const zi_line_codes[] = {
{"Rule", LC_RULE},
};
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
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif
progname = argv[0];
- if (TYPE_BIT(zic_t) <64)
+ if (TYPE_BIT(zic_t) < 64)
{
fprintf(stderr, "%s: %s\n", progname,
_("wild compilation-time specification of zic_t"));
}
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 *
if (type == NULL || *type == '\0')
return true;
buf = emalloc(1 + 4 * strlen(yitcommand) + 2
- + INT_STRLEN_MAXIMUM(zic_t) +2 + 4 * strlen(type) + 2);
+ + INT_STRLEN_MAXIMUM(zic_t) + 2 + 4 * strlen(type) + 2);
b = shellquote(buf, yitcommand);
*b++ = ' ';
b += sprintf(b, INT64_FORMAT, year);