Zone lines ending with a plain number as the time for the DST transition
(e.g. "2026 Mar 16 2") were incorrectly parsed as changing at midnight.
The problem was that eofbit got set after extracting the "2" from the
stream using `in >> i`, then the `in >> at.indicator` expression
failed and set failbit, so that the `if (in >> at.indicator)` condition
was always false and so the assignment to at.time guarded by that
condition was never performed. So the at.time member was always left
equal to zero, i.e. midnight. Suffixed times such as "2s" or "2u" were
parsed correctly.
This commit fixes the operator>> overload for the Indicator enum to not
cause failbit to be set if eofbit is already set. This means that the
`in >> at.indicator` expression yields true when converted to bool, and
the assignment to at.time happens. Not trying to extract an Indicator
when eofbit is already set is correct, because it's valid for there to
be no indicator suffix on an AT time (that just means the time should be
interpreted as wall time).
There was also a bug in the handling of a "-" value for the time, which
is supposed to mean midnight but was not being parsed due to failing to
skip leading whitespace. That did no harm in most cases, because the "-"
would not be extracted from the stream but would then cause a failure to
parse an integer number of hours, and no time would be set, causing it
to default to midnight, which is what "-" means anyway. However, a value
of "-u" is supposed to mean midnight UTC and "-s" is supposed to mean
midnight standard time, and the indicators for UTC or standard were not
being set in those cases. This fix uses std::ws to skip initial
whitespace, and then correctly handles "-" followed by EOF.
libstdc++-v3/ChangeLog:
PR libstdc++/124513
* src/c++20/tzdb.cc (operator>>(istream&, at_time::Indicator&)):
Do not peek at the next character if eofbit is already set.
(istream& operator>>(istream&, at_time&)): Skip whitespace
before the first character. Handle EOF when parsing "-" as time.
Do not peek for ":" or "." if eofbit already set.
* testsuite/std/time/time_zone/124513.cc: New test.
---
Tested x86_64-linux.
libstdc++-v3/src/c++20/tzdb.cc | 20 ++--
.../testsuite/std/time/time_zone/124513.cc | 111 ++++++++++++++++++
2 files changed, 123 insertions(+), 8 deletions(-)
create mode 100644 libstdc++-v3/testsuite/std/time/time_zone/124513.cc
diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
index 82e4790b0d7f..e42ba70dee24 100644
--- a/libstdc++-v3/src/c++20/tzdb.cc
+++ b/libstdc++-v3/src/c++20/tzdb.cc
@@ -272,11 +272,14 @@ namespace std::chrono
// If not, indic is unchanged. Callers should set a default first.
friend istream& operator>>(istream& in, Indicator& indic)
{
- auto [val, yes] = at_time::is_indicator(in.peek());
- if (yes)
+ if (!in.eof())
{
- in.ignore(1);
- indic = val;
+ auto [val, yes] = at_time::is_indicator(in.peek());
+ if (yes)
+ {
+ in.ignore(1);
+ indic = val;
+ }
}
return in;
}
@@ -2186,10 +2189,11 @@ namespace std::chrono
istream& operator>>(istream& in, at_time& at)
{
int sign = 1;
- if (in.peek() == '-')
+ if (ws(in).peek() == '-')
{
in.ignore(1);
- if (auto [val, yes] = at_time::is_indicator(in.peek()); yes)
+ if (auto [val, yes] = at_time::is_indicator(in.peek());
+ in.eof() || yes)
{
in.ignore(1);
at.time = 0s;
@@ -2208,11 +2212,11 @@ namespace std::chrono
in.ignore(1); // discard the colon.
in >> i;
m = minutes{i};
- if (in.peek() == ':')
+ if (!in.eof() && in.peek() == ':')
{
in.ignore(1); // discard the colon.
in >> i;
- if (in.peek() == '.')
+ if (!in.eof() && in.peek() == '.')
{
double frac;
in >> frac;
diff --git a/libstdc++-v3/testsuite/std/time/time_zone/124513.cc
b/libstdc++-v3/testsuite/std/time/time_zone/124513.cc
new file mode 100644
index 000000000000..49d7ba8bb016
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/time_zone/124513.cc
@@ -0,0 +1,111 @@
+// { dg-do run { target c++20 } }
+// { dg-require-effective-target tzdb }
+// { dg-require-effective-target cxx11_abi }
+// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } }
+
+#include <chrono>
+#include <fstream>
+#include <testsuite_hooks.h>
+
+static bool override_used = false;
+
+namespace __gnu_cxx
+{
+ const char* zoneinfo_dir_override() {
+ override_used = true;
+ return "./";
+ }
+}
+
+void
+test_mawson()
+{
+ using namespace std::chrono;
+
+ std::ofstream("tzdata.zi") << R"(# version test1
+Z Antarctica/Mawson 0 - -00 1954 F 13
+ 6 - %z 2009 O 18 2
+ 5 - %z
+)";
+
+ const auto& db = reload_tzdb();
+ VERIFY( override_used ); // If this fails then XFAIL for the target.
+ VERIFY( db.version == "test1" );
+
+ auto zone = locate_zone("Antarctica/Mawson");
+ auto info = zone->get_info(sys_days(2009y/October/19));
+ VERIFY( info.begin == sys_days(2009y/October/17) + 20h);
+}
+
+void
+test_custom()
+{
+ using namespace std::chrono;
+
+ std::ofstream("tzdata.zi") << R"(# version test2
+Z Test/Zomg 3:00:00 - LMT 1990 Mar 1 2
+ 4 - DST 1990 Oct 1 2
+ 3 - STD 1991 Mar 1 3:30
+ 3 1 DST 1991 Oct 1 3:45:01
+ 3 - STD 1992 Mar 1 -
+ 3 1 DST 1992 Oct 1 -u
+ 3 - STD 1993 Mar 1 -s
+ 3 2 DST 1993 Oct 1 -s
+ 3 - STD
+
+Z Test/Zoinks 0:00 - LMT 1990 Mar 1 2
+ 0:00 1:00 DST 1990 Oct 1 3
+ 0:00 - STD 1991 Mar 1 1:30
+ 0:00 1:00 DST 1991 Oct 1 3:30
+ 0:00 - STD
+)";
+
+ const auto& db = reload_tzdb();
+ VERIFY( override_used ); // If this fails then XFAIL for the target.
+ VERIFY( db.version == "test2" );
+
+ const auto offset = 3h;
+
+ const time_zone* zone;
+ sys_info info;
+
+ zone = locate_zone("Test/Zomg");
+ info = zone->get_info(sys_days(1990y/June/1));
+ VERIFY( info.begin == sys_days(1990y/March/1) + 2h - 3h);
+ VERIFY( info.end == sys_days(1990y/October/1) + 2h - 4h);
+
+ info = zone->get_info(sys_days(1991y/June/1));
+ VERIFY( info.begin == sys_days(1991y/March/1) + 3h + 30min - 3h);
+ VERIFY( info.end == sys_days(1991y/October/1) + 3h + 45min + 1s - 4h);
+
+ info = zone->get_info(sys_days(1992y/June/1));
+ VERIFY( info.begin == sys_days(1992y/March/1) - 3h);
+ VERIFY( info.end == sys_days(1992y/October/1)); // -u means midnight UTC
+
+ info = zone->get_info(sys_days(1992y/November/1));
+ VERIFY( info.begin == sys_days(1992y/October/1)); // -u means midnight UTC
+ VERIFY( info.end == sys_days(1993y/March/1) - 3h); // -s means midnight STD
+
+ info = zone->get_info(sys_days(1993y/June/1));
+ VERIFY( info.begin == sys_days(1993y/March/1) -3h); // -s means midnight STD
+ VERIFY( info.end == sys_days(1993y/October/1) - 3h);
+
+
+ zone = locate_zone("Test/Zoinks");
+ info = zone->get_info(sys_days(1990y/June/1));
+ VERIFY( info.begin == sys_days(1990y/March/1) + 2h);
+ VERIFY( info.end == sys_days(1990y/October/1) + 3h - 1h);
+
+ info = zone->get_info(sys_days(1991y/June/1));
+ VERIFY( info.begin == sys_days(1991y/March/1) + 1h + 30min);
+ VERIFY( info.end == sys_days(1991y/October/1) + 3h + 30min - 1h);
+
+ info = zone->get_info(sys_days(1992y/June/1));
+ VERIFY( info.begin == sys_days(1991y/October/1) + 3h + 30min - 1h);
+}
+
+int main()
+{
+ test_custom();
+ test_mawson();
+}
--
2.53.0