libstdc++: Add workaround for old tzdata.zi files
authorJonathan Wakely <jwakely@redhat.com>
Thu, 26 Jan 2023 11:35:00 +0000 (11:35 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 26 Jan 2023 13:38:22 +0000 (13:38 +0000)
The tzdata.zi file in the RHEL 6 tzdata-2018e-3.el6 package (with
version "unknown") does not conform to the current rules described in
the zic(8) man page. Specifically, a Rule name must not start with the
character '+' in the current rules, but the older tzdata.zi file
used "+" as the name of rules for the "Europe/Sofia" zone.

Add a special case to the logic that detects whether a RULES field
refers to a named rule or is an offset from standard time. For a string
matching exactly "+" treat it as a named Rule, but for any other string
starting with '+' treat it as an offset.

libstdc++-v3/ChangeLog:

* src/c++20/tzdb.cc (operator>>(istream&, ZoneInfo&)): Allow
rules named "+" for compatibility with older tzdata.zi files.

libstdc++-v3/src/c++20/tzdb.cc

index c945f00..c956e86 100644 (file)
@@ -1967,6 +1967,22 @@ namespace std::chrono
       return in;
     }
 
+    // Test whether the RULES field of a Zone line is a valid Rule name.
+    inline bool
+    is_rule_name(string_view rules) noexcept
+    {
+      // The NAME field of a Rule line must start with a character that is
+      // neither an ASCII digit nor '-' nor '+'.
+      if (('0' <= rules[0] && rules[0] <= '9') || rules[0] == '-')
+       return false;
+      // However, some older tzdata.zi files (e.g. in tzdata-2018e-3.el6 RPM)
+      // used "+" as a Rule name, so we need to handle that special case.
+      if (rules[0] == '+')
+       return rules.size() == 1; // "+" is a rule name, "+1" is not.
+      // Everything else is the name of a Rule.
+      return true;
+    }
+
     istream& operator>>(istream& in, ZoneInfo& inf)
     {
       // STDOFF  RULES  FORMAT  [UNTIL]
@@ -1976,25 +1992,28 @@ namespace std::chrono
 
       in >> off >> quoted{rules} >> fmt;
       inf.m_offset = off.time;
-      if (rules == "-")
-       {
-         // Standard time always applies, no DST.
-         inf.set_abbrev(fmt);
-       }
-      else if (string_view("0123456789-+").find(rules[0]) != string_view::npos)
+      if (is_rule_name(rules))
        {
-         // rules specifies the difference from standard time.
-         at_time rules_time;
-         istringstream in2(std::move(rules));
-         in2 >> rules_time;
-         inf.m_save = duration_cast<minutes>(rules_time.time);
-         select_std_or_dst_abbrev(fmt, inf.m_save);
-         inf.set_abbrev(fmt);
+         // `rules` refers to a named Rule which describes transitions.
+         inf.set_rules_and_format(rules, fmt);
        }
       else
        {
-         // rules refers to a named Rule which describes transitions.
-         inf.set_rules_and_format(rules, fmt);
+         if (rules == "-")
+           {
+             // Standard time always applies, no DST.
+           }
+         else
+           {
+             // `rules` specifies the difference from standard time,
+             // e.g., "-2:30"
+             at_time rules_time;
+             istringstream in2(std::move(rules));
+             in2 >> rules_time;
+             inf.m_save = duration_cast<minutes>(rules_time.time);
+             select_std_or_dst_abbrev(fmt, inf.m_save);
+           }
+         inf.set_abbrev(fmt);
        }
 
       // YEAR [MONTH [DAY [TIME]]]