Remove support for timezone "posixrules" file.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Jun 2020 22:55:01 +0000 (18:55 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Jun 2020 22:55:01 +0000 (18:55 -0400)
The IANA tzcode library has a feature to read a time zone file named
"posixrules" and apply the daylight-savings transition dates and times
therein, when it is given a POSIX-style time zone specification that
lacks an explicit transition rule.  However, there's a problem with
that code: it doesn't work for dates past the Y2038 time_t rollover.
(Effectively, all times beyond that point are treated as standard
time.)  The IANA crew regard this feature as legacy, so their plan is
to remove it not fix it.  The time frame in which that will happen
is unclear, but presumably it'll happen well before 2038.

Moreover, effective with the next IANA data update (probably this
fall), the recommended default will be to not install a "posixrules"
file in the first place.  The time frame in which tzdata packagers
might adopt that suggestion is likewise unclear, but at least some
platforms will probably do it in the next year or so.  While we could
ignore that recommendation so far as PG-supplied tzdata trees are
concerned, builds using --with-system-tzdata will be subject to
whatever the platform's tzdata packager decides to do.

Thus, whether or not we do anything, some increasing fraction of
Postgres users will be exposed to the behavior observed when there
is no "posixrules" file; and if we do nothing, we'll have essentially
no control over the timing of that change.

The best thing to do to ameliorate the uncertainty seems to be to
proactively remove the posixrules-reading feature.  If we do that in
a scheduled release then at least we can release-note the behavioral
change, rather than having users be surprised by it after a routine
tzdata update.

The change in question is fairly minor anyway: to be affected,
you have to be using a POSIX-style timezone spec, it has to not
have an explicit rule, and it has to not be one of the four traditional
continental-USA zone names (EST5EDT, CST6CDT, MST7MDT, or PST8PDT),
as those are special-cased.  Since the default "posixrules" file
provides USA DST rules, the number of people who are likely to find
such a zone spec useful is probably quite small.  Moreover, the
fallback behavior with no explicit rule and no "posixrules" file is to
apply current USA rules, so the only thing that really breaks is the
DST transitions in years before 2007 (and you get the countervailing
fix that transitions after 2038 will be applied).

Now, some installations might have replaced the "posixrules" file,
allowing e.g. EU rules to be applied to a POSIX-style timezone spec.
That won't work anymore.  But it's not exactly clear why this solution
would be preferable to using a regular named zone.  In any case, given
the Y2038 issue, we need to be pushing users to stop depending on this.

Back-patch into v13; it hasn't been released yet, so it seems OK to
change its behavior.  (Personally I think we ought to back-patch
further, but I've been outvoted.)

Discussion: https://postgr.es/m/1390.1562258309@sss.pgh.pa.us
Discussion: https://postgr.es/m/20200621211855.6211-1-eggert@cs.ucla.edu

doc/src/sgml/datetime.sgml
src/timezone/Makefile
src/timezone/README
src/timezone/localtime.c
src/tools/msvc/Install.pm

index 71fbf842cca9722123b4470a9248570ec88b3b2c..bbf50b76f8c3d89ecc664d430fb6e0f7bea7de3f 100644 (file)
   <para>
    If a daylight-savings abbreviation is given but the
    transition <replaceable>rule</replaceable> field is omitted,
-   <productname>PostgreSQL</productname> attempts to determine the
-   transition times by consulting the <filename>posixrules</filename> file
-   in the IANA time zone database.  This file has the same format as a
-   full time zone entry, but only its transition timing rules are used,
-   not its UTC offsets.  Typically, this file has the same contents as the
-   <literal>US/Eastern</literal> file, so that POSIX-style time zone
-   specifications follow USA daylight-savings rules.  If needed, you can
-   adjust this behavior by replacing the <filename>posixrules</filename>
-   file.
-  </para>
-
-  <note>
-   <para>
-    The facility to consult a <filename>posixrules</filename> file has
-    been deprecated by IANA, and it is likely to go away in the future.
-    One bug in this feature, which is unlikely to be fixed before it
-    disappears, is that it fails to apply DST rules to dates after 2038.
-   </para>
-  </note>
-
-  <para>
-   If the <filename>posixrules</filename> file is not present,
    the fallback behavior is to use the
    rule <literal>M3.2.0,M11.1.0</literal>, which corresponds to USA
    practice as of 2020 (that is, spring forward on the second Sunday of
    March, fall back on the first Sunday of November, both transitions
-   occurring at 2AM prevailing time).
+   occurring at 2AM prevailing time).  Note that this rule does not
+   give correct USA transition dates for years before 2007.
   </para>
 
   <para>
    because (for historical reasons) there are files by those names in the
    IANA time zone database.  The practical implication of this is that
    these zone names will produce valid historical USA daylight-savings
-   transitions, even when a plain POSIX specification would not due to
-   lack of a suitable <filename>posixrules</filename> file.
+   transitions, even when a plain POSIX specification would not.
   </para>
 
   <para>
index bf23ac9da9741d0a0bb73a1e3c9de40974ef1c77..715b63cee0cdbf93c0fcbabfb49a8e347066ea9b 100644 (file)
@@ -29,10 +29,6 @@ ZICOBJS = \
 # we now distribute the timezone data as a single file
 TZDATAFILES = $(srcdir)/data/tzdata.zi
 
-# which zone should determine the DST rules (not the specific UTC offset!)
-# for POSIX-style timezone specs
-POSIXRULES = US/Eastern
-
 # any custom options you might want to pass to zic while installing data files
 ZIC_OPTIONS =
 
@@ -60,13 +56,13 @@ zic: $(ZICOBJS) | submake-libpgport
 
 install: all installdirs
 ifeq (,$(with_system_tzdata))
-   $(ZIC) -d '$(DESTDIR)$(datadir)/timezone' -p '$(POSIXRULES)' -b slim $(ZIC_OPTIONS) $(TZDATAFILES)
+   $(ZIC) -d '$(DESTDIR)$(datadir)/timezone' -b slim $(ZIC_OPTIONS) $(TZDATAFILES)
 endif
    $(MAKE) -C tznames $@
 
 abbrevs.txt: zic $(TZDATAFILES)
    mkdir junkdir
-   $(ZIC) -P -d junkdir -p '$(POSIXRULES)' $(TZDATAFILES) | LANG=C sort | uniq >abbrevs.txt
+   $(ZIC) -P -d junkdir $(TZDATAFILES) | LANG=C sort | uniq >abbrevs.txt
    rm -rf junkdir
 
 installdirs:
index 9939aa6dd7ea483e18dd7ddbe98b120beee05195..8af44449329a9b4894ffe1e07aaf366963a90cb3 100644 (file)
@@ -93,10 +93,7 @@ in some other files where we have variables named that.
 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 avoid loading the TZDEFRULES zone unless
-really necessary, and to ignore any leap-second data it may supply.
-We also cache the result of loading the TZDEFRULES zone, so that
-that's not repeated more than once per process.
+* tzparse() is adjusted to never try to load the TZDEFRULES zone.
 
 * There's a fair amount of code we don't need and have removed,
 including all the nonstandard optional APIs.  We have also added
index 0f65f3c648e59280457e6720a0346894a1934db8..fa3c059038c40778627ac018f738f52ee020b6af 100644 (file)
@@ -53,14 +53,7 @@ static const char wildabbr[] = WILDABBR;
 static const char gmt[] = "GMT";
 
 /*
- * PG: 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 = NULL;
-static int tzdefrules_loaded = 0;
-
-/*
- * The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+ * The DST rules to use if a POSIX TZ string has no rules.
  * Default to US rules as of 2017-05-07.
  * POSIX does not specify the default DST rules;
  * for historical reasons, US rules are a common default.
@@ -986,14 +979,15 @@ tzparse(const char *name, struct state *sp, bool lastditch)
        return false;
 
    /*
-    * The IANA code always tries tzload(TZDEFRULES) here.  We do not want to
-    * do that; it would be bad news in the lastditch case, where we can't
-    * assume pg_open_tzfile() is sane yet.  Moreover, the only reason to do
-    * it unconditionally is to absorb the TZDEFRULES zone's leap second info,
-    * which we don't want to do anyway.  Without that, we only need to load
-    * TZDEFRULES if the zone name specifies DST but doesn't incorporate a
-    * POSIX-style transition date rule, which is not a common case.
+    * The IANA code always tries to tzload(TZDEFRULES) here.  We do not want
+    * to do that; it would be bad news in the lastditch case, where we can't
+    * assume pg_open_tzfile() is sane yet.  Moreover, if we did load it and
+    * it contains leap-second-dependent info, that would cause problems too.
+    * Finally, IANA has deprecated the TZDEFRULES feature, so it presumably
+    * will die at some point.  Desupporting it now seems like good
+    * future-proofing.
     */
+   load_ok = false;
    sp->goback = sp->goahead = false;   /* simulate failed tzload() */
    sp->leapcnt = 0;            /* intentionally assume no leap seconds */
 
@@ -1027,38 +1021,8 @@ tzparse(const char *name, struct state *sp, bool lastditch)
        }
        else
            dstoffset = stdoffset - SECSPERHOUR;
-       if (*name == '\0')
-       {
-           /*
-            * The POSIX zone name does not provide a transition-date rule.
-            * Here we must load the TZDEFRULES zone, if possible, to serve as
-            * source data for the transition dates.  Unlike the IANA code, we
-            * try to cache the data so it's only loaded once.
-            */
-           if (tzdefrules_loaded == 0)
-           {
-               /* Allocate on first use */
-               if (tzdefrules_s == NULL)
-                   tzdefrules_s = (struct state *) malloc(sizeof(struct state));
-               if (tzdefrules_s != NULL)
-               {
-                   if (tzload(TZDEFRULES, NULL, tzdefrules_s, false) == 0)
-                       tzdefrules_loaded = 1;
-                   else
-                       tzdefrules_loaded = -1;
-                   /* In any case, we ignore leap-second data from the file */
-                   tzdefrules_s->leapcnt = 0;
-               }
-           }
-           load_ok = (tzdefrules_loaded > 0);
-           if (load_ok)
-               memcpy(sp, tzdefrules_s, sizeof(struct state));
-           else
-           {
-               /* If we can't load TZDEFRULES, fall back to hard-wired rule */
-               name = TZDEFRULESTRING;
-           }
-       }
+       if (*name == '\0' && !load_ok)
+           name = TZDEFRULESTRING;
        if (*name == ',' || *name == ';')
        {
            struct rule start;
index 9bf111c41ef86ab4acf001917bf8b965a9285e99..b6d0cfd39b492580ce9babc55d236a0e34651200 100644 (file)
@@ -366,16 +366,10 @@ sub GenerateTimezoneFiles
      || die "Could not find TZDATAFILES line in timezone makefile\n";
    my @tzfiles = split /\s+/, $1;
 
-   $mf =~ /^POSIXRULES\s*:?=\s*(.*)$/m
-     || die "Could not find POSIXRULES line in timezone makefile\n";
-   my $posixrules = $1;
-   $posixrules =~ s/\s+//g;
-
    print "Generating timezone files...";
 
    my @args = (
-       "$conf/zic/zic", '-d', "$target/share/timezone", '-p',
-       "$posixrules",   '-b', 'slim');
+       "$conf/zic/zic", '-d', "$target/share/timezone", '-b', 'slim');
    foreach (@tzfiles)
    {
        my $tzfile = $_;