summaryrefslogtreecommitdiff
path: root/src/timezone
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone')
-rw-r--r--src/timezone/README3
-rw-r--r--src/timezone/localtime.c23
-rw-r--r--src/timezone/pgtz.c42
3 files changed, 60 insertions, 8 deletions
diff --git a/src/timezone/README b/src/timezone/README
index 2544230c4c..912e0c1b39 100644
--- a/src/timezone/README
+++ b/src/timezone/README
@@ -79,6 +79,9 @@ other exposed names.
slightly modified the API of the former, in part because it now relies
on our own pg_open_tzfile() rather than opening files for itself.
+* tzparse() is adjusted to cache the result of loading the TZDEFRULES
+zone, so that that's not repeated more than once per process.
+
* There's a fair amount of code we don't need and have removed,
including all the nonstandard optional APIs. We have also added
a few functions of our own at the bottom of localtime.c.
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 6e0fa10066..04105b2c96 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -55,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
@@ -942,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 */
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index 982fc1fe71..9356f2592d 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -80,12 +80,37 @@ pg_open_tzfile(const char *name, char *canonname)
int fullnamelen;
int orignamelen;
+ /* Initialize fullname with base name of tzdata directory */
+ strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
+ orignamelen = fullnamelen = strlen(fullname);
+
+ if (fullnamelen + 1 + strlen(name) >= MAXPGPATH)
+ return -1; /* not gonna fit */
+
+ /*
+ * If the caller doesn't need the canonical spelling, first just try to
+ * open the name as-is. This can be expected to succeed if the given name
+ * is already case-correct, or if the filesystem is case-insensitive; and
+ * we don't need to distinguish those situations if we aren't tasked with
+ * reporting the canonical spelling.
+ */
+ if (canonname == NULL)
+ {
+ int result;
+
+ fullname[fullnamelen] = '/';
+ /* test above ensured this will fit: */
+ strcpy(fullname + fullnamelen + 1, name);
+ result = open(fullname, O_RDONLY | PG_BINARY, 0);
+ if (result >= 0)
+ return result;
+ /* If that didn't work, fall through to do it the hard way */
+ }
+
/*
* Loop to split the given name into directory levels; for each level,
* search using scan_directory_ci().
*/
- strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
- orignamelen = fullnamelen = strlen(fullname);
fname = name;
for (;;)
{
@@ -97,8 +122,6 @@ pg_open_tzfile(const char *name, char *canonname)
fnamelen = slashptr - fname;
else
fnamelen = strlen(fname);
- if (fullnamelen + 1 + fnamelen >= MAXPGPATH)
- return -1; /* not gonna fit */
if (!scan_directory_ci(fullname, fname, fnamelen,
fullname + fullnamelen + 1,
MAXPGPATH - fullnamelen - 1))
@@ -458,10 +481,11 @@ pg_tzenumerate_next(pg_tzenum *dir)
/*
* Load this timezone using tzload() not pg_tzset(), so we don't fill
- * the cache
+ * the cache. Also, don't ask for the canonical spelling: we already
+ * know it, and pg_open_tzfile's way of finding it out is pretty
+ * inefficient.
*/
- if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
- true) != 0)
+ if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0)
{
/* Zone could not be loaded, ignore it */
continue;
@@ -473,6 +497,10 @@ pg_tzenumerate_next(pg_tzenum *dir)
continue;
}
+ /* OK, return the canonical zone name spelling. */
+ strlcpy(dir->tz.TZname, fullname + dir->baselen,
+ sizeof(dir->tz.TZname));
+
/* Timezone loaded OK. */
return &dir->tz;
}