https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127815

liu zhenyu <lzy20000419 at outlook dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |lzy20000419 at outlook dot com

--- Comment #6 from liu zhenyu <lzy20000419 at outlook dot com> ---
r17-5617 fixes the lower bound, but the upper bound has the mirror problem and
it is unchanged on trunk. For an instant at or after the last period's until()
(sys_days(year::max()/December/31)), _M_get_sys_info clamps with `tp =
(--i)->until();` (libstdc++-v3/src/c++20/tzdb.cc:1082 on trunk 20261105), which
is the exclusive end of that period. If the last period follows a rule set, the
rule expansion stops at info.end == tp, no generated sys_info contains tp, and
get_info throws. This testcase does not depend on the system tzdata:

```c++
#include <chrono>
#include <cstdio>
#include <filesystem>
#include <fstream>
namespace __gnu_cxx { const char* zoneinfo_dir_override() { return
"/tmp/tzmax-repro"; } }
int main(int argc, char**)
{
  std::filesystem::create_directories("/tmp/tzmax-repro");
  std::ofstream("/tmp/tzmax-repro/tzdata.zi")
    << "# version test\n"
       "R X 2000 ma - Mar lastSu 2 1 D\n"
       "R X 2000 ma - O lastSu 2 0 S\n"
       "Z Test -6 X C%sT\n"
       "Z Plain -6 - CST\n";
  using namespace std::chrono;
  const char* name = argc > 1 ? "Plain" : "Test";   // one zone per run
(get_info caches expansions)
  const sys_seconds U{sys_days{year::max() / December / 31}};
  int bad = 0;
  for (sys_seconds t : {U, sys_seconds::max(), U - 1s})
    try { sys_info i = locate_zone(name)->get_info(t); std::printf("%s: ok,
abbrev=%s\n", name, i.abbrev.c_str()); }
    catch (const std::exception& e) { std::printf("%s: threw %s\n", name,
e.what()); bad = 1; }
  return bad;
}
```

```
$ g++ -std=c++20 t.cc && ./a.out
Test: threw time_zone::get_info
Test: threw time_zone::get_info
Test: ok, abbrev=CST
$ ./a.out plain
Plain: ok, abbrev=CST
Plain: ok, abbrev=CST
Plain: ok, abbrev=CST
```

Expected: all three calls return a sys_info (as U - 1s and the rule-less zone
do). With the system tzdata,
`locate_zone("America/New_York")->get_info(sys_seconds::max())` throws as well.
The result also depends on cache history: after a query in the last year, the
same call returns a sys_info that does not contain tp instead of throwing.

Tested with 14.2.0. I did not build trunk libstdc++, but the clamp at
tzdb.cc:1082 is the same on trunk after r17-5617. Clamping to `(--i)->until() -
1s` (like the new lower-bound clamp) should avoid it, since U - 1s already
works.

Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.

Reply via email to