summaryrefslogtreecommitdiff
path: root/src/timezone/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r--src/timezone/localtime.c103
1 files changed, 81 insertions, 22 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index a14215d6bd..a2faa82ac8 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -17,8 +17,9 @@
#include <fcntl.h>
#include "datatype/timestamp.h"
-#include "private.h"
#include "pgtz.h"
+
+#include "private.h"
#include "tzfile.h"
@@ -54,6 +55,13 @@ static const pg_time_t time_t_min = MINVAL(pg_time_t, TYPE_BIT(pg_time_t));
static const pg_time_t time_t_max = MAXVAL(pg_time_t, TYPE_BIT(pg_time_t));
/*
+ * We cache the result of trying to load the TZDEFRULES zone here.
+ * tzdefrules_loaded is 0 if not tried yet, +1 if good, -1 if failed.
+ */
+static struct state tzdefrules_s;
+static int tzdefrules_loaded = 0;
+
+/*
* The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
* We default to US rules as of 1999-08-17.
* POSIX 1003.1 section 8.1.1 says that the default DST rules are
@@ -414,10 +422,10 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
{
/*
* Attempt to reuse existing abbreviations. Without this,
- * America/Anchorage would stop working after 2037 when
- * TZ_MAX_CHARS is 50, as sp->charcnt equals 42 (for LMT CAT CAWT
- * CAPT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for
- * AKST AKDT). Reusing means sp->charcnt can stay 42 in this
+ * America/Anchorage would be right on the edge after 2037 when
+ * TZ_MAX_CHARS is 50, as sp->charcnt equals 40 (for LMT AST AWT
+ * APT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for
+ * AKST AKDT). Reusing means sp->charcnt can stay 40 in this
* example.
*/
int gotabbr = 0;
@@ -451,6 +459,17 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
if (gotabbr == 2)
{
sp->charcnt = charcnt;
+
+ /*
+ * Ignore any trailing, no-op transitions generated by zic as
+ * they don't help here and can run afoul of bugs in zic 2016j
+ * or earlier.
+ */
+ while (1 < sp->timecnt
+ && (sp->types[sp->timecnt - 1]
+ == sp->types[sp->timecnt - 2]))
+ sp->timecnt--;
+
for (i = 0; i < ts->timecnt; i++)
if (sp->ats[sp->timecnt - 1] < ts->ats[i])
break;
@@ -489,7 +508,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
}
/*
- * If type 0 is is unused in transitions, it's the type to use for early
+ * If type 0 is unused in transitions, it's the type to use for early
* times.
*/
for (i = 0; i < sp->timecnt; ++i)
@@ -930,7 +949,21 @@ tzparse(const char *name, struct state * sp, bool lastditch)
charcnt = stdlen + 1;
if (sizeof sp->chars < charcnt)
return false;
- load_ok = tzload(TZDEFRULES, NULL, sp, false) == 0;
+
+ /*
+ * This bit also differs from the IANA code, which doesn't make any
+ * attempt to avoid repetitive loadings of the TZDEFRULES zone.
+ */
+ if (tzdefrules_loaded == 0)
+ {
+ if (tzload(TZDEFRULES, NULL, &tzdefrules_s, false) == 0)
+ tzdefrules_loaded = 1;
+ else
+ tzdefrules_loaded = -1;
+ }
+ load_ok = (tzdefrules_loaded > 0);
+ if (load_ok)
+ memcpy(sp, &tzdefrules_s, sizeof(struct state));
}
if (!load_ok)
sp->leapcnt = 0; /* so, we're off a little */
@@ -974,6 +1007,8 @@ tzparse(const char *name, struct state * sp, bool lastditch)
int yearlim;
int timecnt;
pg_time_t janfirst;
+ int32 janoffset = 0;
+ int yearbeg;
++name;
if ((name = getrule(name, &start)) == NULL)
@@ -994,8 +1029,23 @@ tzparse(const char *name, struct state * sp, bool lastditch)
sp->defaulttype = 0;
timecnt = 0;
janfirst = 0;
- yearlim = EPOCH_YEAR + YEARSPERREPEAT;
- for (year = EPOCH_YEAR; year < yearlim; year++)
+ yearbeg = EPOCH_YEAR;
+
+ do
+ {
+ int32 yearsecs
+ = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
+
+ yearbeg--;
+ if (increment_overflow_time(&janfirst, -yearsecs))
+ {
+ janoffset = -yearsecs;
+ break;
+ }
+ } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
+
+ yearlim = yearbeg + YEARSPERREPEAT + 1;
+ for (year = yearbeg; year < yearlim; year++)
{
int32
starttime = transtime(year, &start, stdoffset),
@@ -1020,24 +1070,34 @@ tzparse(const char *name, struct state * sp, bool lastditch)
{
if (TZ_MAX_TIMES - 2 < timecnt)
break;
- yearlim = year + YEARSPERREPEAT + 1;
sp->ats[timecnt] = janfirst;
- if (increment_overflow_time
- (&sp->ats[timecnt], starttime))
- break;
- sp->types[timecnt++] = reversed;
+ if (!increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + starttime))
+ sp->types[timecnt++] = reversed;
+ else if (janoffset)
+ sp->defaulttype = reversed;
sp->ats[timecnt] = janfirst;
- if (increment_overflow_time
- (&sp->ats[timecnt], endtime))
- break;
- sp->types[timecnt++] = !reversed;
+ if (!increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + endtime))
+ {
+ sp->types[timecnt++] = !reversed;
+ yearlim = year + YEARSPERREPEAT + 1;
+ }
+ else if (janoffset)
+ sp->defaulttype = !reversed;
}
- if (increment_overflow_time(&janfirst, yearsecs))
+ if (increment_overflow_time
+ (&janfirst, janoffset + yearsecs))
break;
+ janoffset = 0;
}
sp->timecnt = timecnt;
if (!timecnt)
sp->typecnt = 1; /* Perpetual DST. */
+ else if (YEARSPERREPEAT < year - yearbeg)
+ sp->goback = sp->goahead = true;
}
else
{
@@ -1280,9 +1340,8 @@ gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
result = timesub(timep, offset, gmtptr, tmp);
/*
- * Could get fancy here and deliver something such as "UT+xxxx" or
- * "UT-xxxx" if offset is non-zero, but this is no time for a treasure
- * hunt.
+ * Could get fancy here and deliver something such as "+xx" or "-xx" if
+ * offset is non-zero, but this is no time for a treasure hunt.
*/
if (offset != 0)
tmp->tm_zone = wildabbr;