[PATCH] libstdc++: Make chrono::tzdb parser case-insensive [PR123510]
Tomasz Kaminski
tkaminsk@redhat.com
Thu Aug 27 10:05:57 GMT 2026
On Thu, Aug 27, 2026 at 11:30 AM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Thu, 27 Aug 2026 at 08:32, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
> >
> > Expand switch and if-check to check both lowercase and uppercase
> > letter.
> >
> > For testing, we use test_combinations, that checks minimum prefix
> > (few characters) in all combination of letter cases, and whole
> > world and midpoint length, by combining above with all lowercase
> > or uppercase suffix.
> >
> > libstdc++-v3/ChangeLog:
> >
> > PR libstdc++/123510
> > * src/c++20/tzdb.cc (at_time::is_indicator): Add case labels
> > for uppercase letters.
> > (operator>>(istream&, years_from_to): Check for 'M' and 'O',
> > (chrono::reload_tzdb): Add case labels for lowercase letters,
> > and normalize first letter of type.
> > (operator>>(istream&, abbrev_month&): Match all cases in switch
> > and days_chars. Replace if with switch for matching 'a' in
> > March/May for consistency.
> > (operator>>(istream&, on_month_day&)): Check for 'L'.
> > * testsuite/std/time/tzdb/name_matching.cc: New test.
> > ---
> > Knowing the file format by hearth (now), made writting the test checking
> > if each name (and prefix) is interpreted correctly (instead of checking
> > only if it is not causing errors) much faster than I expected.
> >
> > Testing on x86_64-linux. new test already passed in all standard modes.
> > OK for trunk? This is non invasive change, so I would backport it to
> > GCC-16 (when C++20 is stable).
> >
> > libstdc++-v3/src/c++20/tzdb.cc | 56 ++-
> > .../testsuite/std/time/tzdb/name_matching.cc | 345 ++++++++++++++++++
> > 2 files changed, 395 insertions(+), 6 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/std/time/tzdb/name_matching.cc
> >
> > diff --git a/libstdc++-v3/src/c++20/tzdb.cc
> b/libstdc++-v3/src/c++20/tzdb.cc
> > index 0d004a8a6fe..1421cd85b3a 100644
> > --- a/libstdc++-v3/src/c++20/tzdb.cc
> > +++ b/libstdc++-v3/src/c++20/tzdb.cc
> > @@ -272,14 +272,20 @@ namespace std::chrono
> > switch (c)
> > {
> > case 's':
> > + case 'S':
> > return {Standard, true};
> > case 'u':
> > + case 'U':
> > case 'g':
> > + case 'G':
> > case 'z':
> > + case 'Z':
> > return {Universal, true};
> > case 'w':
> > + case 'W':
> > return {Wall, true};
> > case 'd':
> > + case 'D':
> > return {Daylight, true};
> > default:
> > return {Wall, false};
> > @@ -399,7 +405,7 @@ namespace std::chrono
> > {
> > string s;
> > auto c = ws(in).peek();
> > - if (c == 'm') [[unlikely]] // keyword "minimum"
> > + if (c == 'm' || c == 'M') [[unlikely]] // keyword "minimum"
> > {
> > in >> s; // extract the rest of the word
> > yy.from = year(1900);
> > @@ -408,12 +414,12 @@ namespace std::chrono
> > yy.from = year{num};
> >
> > c = ws(in).peek();
> > - if (c == 'm') // keyword "maximum"
> > + if (c == 'm' || c == 'M') // keyword "maximum"
> > {
> > in >> s; // extract the rest of the word
> > yy.to = year::max();
> > }
> > - else if (c == 'o') // keyword "only"
> > + else if (c == 'o' || c == 'O') // keyword "only"
> > {
> > in >> s; // extract the rest of the word
> > yy.to = yy.from;
> > @@ -1969,28 +1975,37 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > {
> > case '#':
> > break;
> > + case 'r':
> > case 'R':
> > {
> > // Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
> > is >> type; // extract the "Rule" or "R" marker
> > + type[0] = 'R';
>
> Is this necessary?
> We never inspect the type variable, it's just used to extract a word.
>
Not for the rule and link at this point, but it is necessary for Zone, as
default branch
validates that the type is zone, and I think it is better to write to the
memory location
we just overwrote than do comparison against two letters on each line.
And I just made it applied consistently, as I do not think that this will
have measureable
impact.
> > +
> > Rule rule;
> > is >> rule;
> > node->rules.push_back(std::move(rule));
> > break;
> > }
> > + case 'l':
> > case 'L':
> > {
> > // Link TARGET LINK-NAME
> > is >> type; // extract the "Link" or "L" marker
> > + type[0] = 'L';
> > +
> > time_zone_link link(nullptr);
> > is >> quoted(link._M_target) >> quoted(link._M_name);
> > node->db.links.push_back(std::move(link));
> > break;
> > }
> > + case 'z':
> > case 'Z':
> > {
> > // Zone NAME STDOFF RULES FORMAT [UNTIL]
> > is >> type; // extract the "Zone" or "Z" marker
> > + type[0] = 'Z';
> > +
> > time_zone tz(std::make_unique<time_zone::_Impl>(node));
> > is >> quoted(tz._M_name);
> > node->db.zones.push_back(time_zone(std::move(tz)));
> > @@ -2459,60 +2474,80 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > in >> s;
> > switch (s[0])
> > {
> > + case 'j':
> > case 'J':
> > switch (s[1])
> > {
> > case 'a':
> > + case 'A':
> > am.m = January;
> > return in;
> > case 'u':
> > + case 'U':
> > switch (s[2])
> > {
> > case 'n':
> > + case 'N':
> > am.m = June;
> > return in;
> > case 'l':
> > + case 'L':
> > am.m = July;
> > return in;
> > }
> > break;
> > }
> > break;
> > + case 'f':
> > case 'F':
> > am.m = February;
> > return in;
> > + case 'm':
> > case 'M':
> > - if (s[1] == 'a') [[likely]]
> > + switch (s[1])
> > + {
> > + case 'a':
> > + case 'A':
>
> I don't really like changing from 'if' to a 'switch' with only one
> case (well, two, but they have the same statements following the case
> labels).
>
> I would find `if (s[1] == 'a' || s[1] == 'A') easier to parse.
>
OK, will change that.
>
> > switch (s[2])
> > {
> > case 'r':
> > + case 'R':
> > am.m = March;
> > return in;
> > case 'y':
> > + case 'Y':
> > am.m = May;
> > return in;
> > }
> > + }
> > break;
> > + case 'a':
> > case 'A':
> > switch (s[1])
> > {
> > case 'p':
> > + case 'P':
> > am.m = April;
> > return in;
> > case 'u':
> > + case 'U':
> > am.m = August;
> > return in;
> > }
> > break;
> > + case 's':
> > case 'S':
> > am.m = September;
> > return in;
> > + case 'o':
> > case 'O':
> > am.m = October;
> > return in;
> > + case 'n':
> > case 'N':
> > am.m = November;
> > return in;
> > + case 'd':
> > case 'D':
> > am.m = December;
> > return in;
> > @@ -2536,37 +2571,46 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > // Just peek at one char at a time.
> > switch (in.peek())
> > {
> > + case 'm':
> > case 'M':
> > aw.wd = Monday;
> > break;
> > + case 't':
> > case 'T':
> > in.ignore(1); // Discard the 'T'
> > switch (in.peek())
> > {
> > case 'u':
> > + case 'U':
> > aw.wd = Tuesday;
> > break;
> > case 'h':
> > + case 'H':
> > aw.wd = Thursday;
> > break;
> > default:
> > in.setstate(ios::failbit);
> > }
> > break;
> > + case 'w':
> > case 'W':
> > aw.wd = Wednesday;
> > break;
> > + case 'f':
> > case 'F':
> > aw.wd = Friday;
> > break;
> > + case 's':
> > case 'S':
> > in.ignore(1); // Discard the 'S'
> > switch (in.peek())
> > {
> > case 'a':
> > + case 'A':
> > aw.wd = Saturday;
> > break;
> > case 'u':
> > + case 'U':
> > aw.wd = Sunday;
> > break;
> > default:
> > @@ -2579,7 +2623,7 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > in.ignore(1); // Discard whichever char we just looked at.
> >
> > // Discard any remaining chars from weekday, e.g. "onday".
> > - string_view day_chars = "ondayesritu";
> > + string_view day_chars = "oOnNdDaAyYeEsSrRiItTuU";
>
> Since we expect the characters to be lower case the find(c) call below
> should be faster if we use:
>
> day_chars = "ondayesrituONDAYESRITU";
>
> because we won't have to search as far to find the most likely characters.
>
> We could take that to the extreme and sort them by frequency, so maybe
> something like "daysneroitu" ... but on the other hand, this code will
> probably never run at all for real tzdata.zi files. There are no
> redundant chars in the names of weekdays, so after the switch matches
> an unambiguous prefix, we will always have c == eof for the real
> files.
>
> I think putting the uppercase ones last is worth it though.
>
>
> > auto is_day_char = [&day_chars](int c) {
> > return c != char_traits<char>::eof()
> > && day_chars.find((char)c) != day_chars.npos;
> > @@ -2613,7 +2657,7 @@ constinit tzdb_list::_Node::NumLeapSeconds
> tzdb_list::_Node::num_leap_seconds;
> > return in;
> > }
> > }
> > - else if (c == 'l') // lastSunday, lastWed, ...
> > + else if (c == 'l' || c == 'L') // lastSunday, lastWed, ...
> > {
> > in.ignore(4);
> > if (abbrev_weekday w{}; in >> w) [[likely]]
> > diff --git a/libstdc++-v3/testsuite/std/time/tzdb/name_matching.cc
> b/libstdc++-v3/testsuite/std/time/tzdb/name_matching.cc
> > new file mode 100644
> > index 00000000000..ccd6f6b400e
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/std/time/tzdb/name_matching.cc
> > @@ -0,0 +1,345 @@
> > +// { 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* } }
>
> This new test takes over 6s on my laptop, it will be much slower on
> some low-power targets.
>
> I still think exhaustively testing every combination is overkill
> because testing "monday" and "MONDAY" exercises every code path in the
> current implementation, there's no need to test "mOnDaY" and "MONday"
> etc.
>
It does not check if we properly handle seeing just 'm' or "M". Also the
loop
It does not check all letter combinations, only the prefix, so we get
m
M
mONDAY
MONDAY
monday
mONDAY
mON
MON
mon
mON
I can reduce above to check only for, minimum prefix and the whole word,
so something like:
m
M
monday
MONDAY
> If we ever change the structure of the parsing implementation, we
> could add more exhaustive tests then.
>
> > +
> > +#include <chrono>
> > +#include <fstream>
> > +#include <format>
> > +#include <testsuite_hooks.h>
> > +#include <iostream>
> > +
> > +static bool override_used = false;
> > +
> > +namespace __gnu_cxx
> > +{
> > + const char* zoneinfo_dir_override() {
> > + override_used = true;
> > + return "./";
> > + }
> > +}
> > +
> > +using namespace std::chrono;
> > +
> > +template<typename Functor, typename... Args>
> > + void
> > + test_combinations(Functor&& func, std::string_view txt, size_t min,
> Args&&... args)
> > + {
> > + const size_t mid = (min + txt.size()) / 2;
> > + const unsigned mask = (1u << min) - 1;
> > +
> > + std::string lower(txt);
> > + std::string upper(txt);
> > + for (char& c : upper)
> > + c = std::toupper(c);
> > +
> > + unsigned pattern = 0;
> > + do
> > + {
> > + for (size_t i = 0; i < min; ++i)
> > + lower[i] = upper[i]
> > + = (pattern & (1u << i)) ? std::toupper(txt[i]) : txt[i];
> > +
> > + func(lower, args...);
> > + if (min < txt.size())
> > + {
> > + func(lower.substr(0, min), args...);
> > + func(upper, args...);
> > + }
> > + if (mid != min)
> > + {
> > + func(lower.substr(0, mid), args...);
> > + func(upper.substr(0, mid), args...);
> > + }
> > + ++pattern;
> > + }
> > + while (pattern & mask);
>
> Maybe this loop could break after just two iterations for simulator
> targets (using a macro like in
> testsuite/25_algorithms/partial_sort/random_test.cc and other slow
> tests).
>
I will reduce it only to 4 calls, which should help here.
>
> > + }
> > +
> > +void
> > +test_day(std::string_view name, weekday wd)
> > +{
> > + constexpr const char* templ = R"(# version test_day_{0}
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + Zone Test 0 - Z1 2000 May last{0}
> > + 0 - Z2 2010 May {0}>=10
> > + 0 - Z3
> > + )";
> > +
> > + std::string input = std::format(templ, name);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > +
> > + std::string_view ver = db.version;
> > + VERIFY( ver.starts_with("test_day_") );
> > + ver.remove_prefix(9);
> > + VERIFY( ver == name );
> > +
> > + sys_info info =
> locate_zone("Test")->get_info(sys_days(2005y/January/1));
> > + VERIFY( info.begin == sys_days(2000y/May/wd[last]) );
> > + year_month_day ymd(ceil<days>(info.end));
> > + VERIFY( ymd.year() == 2010y );
> > + VERIFY( ymd.month() == May );
> > + VERIFY( ymd.day() >= day(10) );
> > + VERIFY( weekday(ymd) == wd );
> > +}
> > +
> > +void
> > +test_last(std::string_view name)
> > +{
> > + constexpr const char* templ = R"(# version test_{0}
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + Zone Test 0 - Z1 2000 July {0}Mon
> > + 0 - Z3
> > + )";
> > +
> > + std::string input = std::format(templ, name);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > +
> > + std::string_view ver = db.version;
> > + VERIFY( ver.starts_with("test_") );
> > + ver.remove_prefix(5);
> > + VERIFY( ver == name );
> > +
> > + sys_info info =
> locate_zone("Test")->get_info(sys_days(2005y/January/1));
> > + VERIFY( info.begin == sys_days(2000y/July/Monday[last]) );
> > +}
> > +
> > +void
> > +test_days()
> > +{
> > + test_combinations(test_day, "monday", 1, Monday);
> > + test_combinations(test_day, "tuesday", 2, Tuesday);
> > + test_combinations(test_day, "wednesday", 1, Wednesday);
> > + test_combinations(test_day, "thursday", 2, Thursday);
> > + test_combinations(test_day, "friday", 1, Friday);
> > + test_combinations(test_day, "saturday", 2, Saturday);
> > + test_combinations(test_day, "sunday", 2, Sunday);
> > +
> > + test_last("last");
> > + test_last("LAST");
> > + test_last("Last");
> > +}
> > +
> > +void
> > +test_month(std::string_view name, month m)
> > +{
> > + constexpr const char* templ = R"(# version test_month_{0}
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + Zone Test 0 - Z1 2000 {0} 13
> > + 0 - Z2 2010 {0}
> > + 0 - Z3
> > + )";
> > +
> > + std::string input = std::format(templ, name);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > +
> > + std::string_view ver = db.version;
> > + VERIFY( ver.starts_with("test_month_") );
> > + ver.remove_prefix(11);
> > + VERIFY( ver == name );
> > +
> > + sys_info info =
> locate_zone("Test")->get_info(sys_days(2005y/January/1));
> > + VERIFY( info.begin == sys_days(2000y/m/13) );
> > + VERIFY( info.end == sys_days(2010y/m/1) );
> > +}
> > +
> > +void
> > +test_months()
> > +{
> > + test_combinations(test_month, "january", 2, January);
> > + test_combinations(test_month, "february", 1, February);
> > + test_combinations(test_month, "march", 3, March);
> > + test_combinations(test_month, "april", 2, April);
> > + test_combinations(test_month, "may", 3, May);
> > + test_combinations(test_month, "june", 3, June);
> > + test_combinations(test_month, "july", 3, July);
> > + test_combinations(test_month, "august", 3, August);
> > + test_combinations(test_month, "september", 1, September);
> > + test_combinations(test_month, "october", 1, October);
> > + test_combinations(test_month, "november", 1, November);
> > + test_combinations(test_month, "december", 1, December);
> > +}
> > +
> > +void
> > +test_year(std::string_view min, std::string_view max, std::string_view
> only)
> > +{
> > + constexpr const char* templ = R"(# version {0}
> > + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S
> > + Rule Rule {1} 2020 - Jan 10 0u 0 S
> > + Rule Rule 2000 {2} - Nov 12 0u 1 D
> > + Rule Rule 2010 {3} - Aug 11 0u 2 O
> > + Rule Rule 2020 {2} - Jan 13 0u 0 S
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + Zone Test 0 - Z1 1800 Jan 1 0u
> > + 0 Rule Z%s 3001 Jan 1 0u
> > + 0 - Zl
> > + )";
> > +
> > + const std::string ver = std::format("test_{}_{}_{}", min, max, only);
> > + std::string input = std::format(templ, ver, min, max, only);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > + VERIFY( db.version == ver );
> > +
> > + const time_zone* zone = locate_zone("Test");
> > +
> > + // min applies since 1900, check first transition
> > + sys_info info = zone->get_info(sys_days(1900y/January/1));
> > + VERIFY( info.begin == sys_days(1800y/January/1) );
> > + VERIFY( info.end == sys_days(1900y/January/10) );
> > +
> > + // Check 2010 only rule does not apply in 2009
> > + info = zone->get_info(sys_days(2009y/February/1));
> > + VERIFY( info.begin == sys_days(2009y/January/10) );
> > + VERIFY( info.end == sys_days(2009y/November/12) );
> > +
> > + // Check 2010 only rule in 2010
> > + info = zone->get_info(sys_days(2010y/February/1));
> > + VERIFY( info.begin == sys_days(2010y/January/10) );
> > + VERIFY( info.end == sys_days(2010y/August/11) );
> > +
> > + info = zone->get_info(sys_days(2010y/October/1));
> > + VERIFY( info.begin == sys_days(2010y/August/11) );
> > + VERIFY( info.end == sys_days(2010y/November/12) );
> > +
> > + // Check 2010 only rule does not apply in 2011
> > + info = zone->get_info(sys_days(2011y/February/1));
> > + VERIFY( info.begin == sys_days(2011y/January/10) );
> > + VERIFY( info.end == sys_days(2011y/November/12) );
> > +
> > + // max rule applies foreve
> > + info = zone->get_info(sys_days(2500y/February/1));
> > + VERIFY( info.begin == sys_days(2500y/January/13) );
> > + VERIFY( info.end == sys_days(2500y/November/12) );
> > +
> > + info = zone->get_info(sys_days(3000y/December/1));
> > + VERIFY( info.begin == sys_days(3000y/November/12) );
> > + VERIFY( info.end == sys_days(3001y/January/1) );
> > +}
> > +
> > +void
> > +test_years()
> > +{
> > + auto test_min = [](std::string_view name) { test_year(name, "max",
> "only"); };
> > + auto test_max = [](std::string_view name) { test_year("MIN", name,
> "ONLY"); };
> > + auto test_only = [](std::string_view name) { test_year("Min", "Max",
> name); };
> > +
> > + test_combinations(test_min, "min", 1);
> > + test_combinations(test_max, "max", 1);
> > + test_combinations(test_only, "only", 1);
> > +}
> > +
> > +void
> > +test_time(char s, char u, char w, char d)
> > +{
> > + constexpr const char* templ = R"(# version test_time_{0}{1}{2}{3}
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + Zone Test 1 0 Zs 2000 Jan 11 10{0}
> > + 3 1 Zu 2005 Feb 12 11{1}
> > + 5 1 Zw 2010 Mar 13 12{2}
> > + 7 1 Zd 2015 Apr 14 13{3}
> > + 9 0 Su 2020 May 15 14{1}
> > + 10 0 Sw 2025 Jun 16 15{2}
> > + 11 - Zl
> > + )";
> > +
> > + std::string input = std::format(templ, s, u, w, d);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > +
> > + std::string_view ver = db.version;
> > + VERIFY( ver.starts_with("test_time_") );
> > + VERIFY( ver[10] == s );
> > + VERIFY( ver[11] == u );
> > + VERIFY( ver[12] == w );
> > + VERIFY( ver[13] == d );
> > +
> > + const time_zone* zone = locate_zone("Test");
> > + sys_info info = zone->get_info(sys_days(2002y/January/1));
> > + VERIFY( info.begin == sys_days(2000y/January/11) + 10h - 1h );
> > + VERIFY( info.end == sys_days(2005y/February/12) + 11h );
> > +
> > + info = zone->get_info(sys_days(2012y/January/1));
> > + VERIFY( info.begin == sys_days(2010y/March/13) + 12h - 6h );
> > + VERIFY( info.end == sys_days(2015y/April/14) + 13h - 8h );
> > +
> > + info = zone->get_info(sys_days(2022y/January/1));
> > + VERIFY( info.begin == sys_days(2020y/May/15) + 14h );
> > + VERIFY( info.end == sys_days(2025y/June/16) + 15h - 10h );
> > +}
> > +
> > +void
> > +test_times()
> > +{
> > + test_time('s', 'u', 'w', 'd');
> > + test_time('S', 'U', 'W', 'D');
> > + // Alternate spelling for Universal
> > + test_time('s', 'g', 'w', 'd');
> > + test_time('s', 'G', 'w', 'd');
> > + test_time('s', 'z', 'w', 'd');
> > + test_time('s', 'Z', 'w', 'd');
> > +}
> > +
> > +void
> > +test_line(std::string_view rule, std::string_view zone,
> std::string_view link)
> > +{
> > + constexpr const char* templ = R"(# version {0}
> > + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S
> > + {1} Rule min max - Jan 13 0u 0 D
> > + {1} Rule min max - Oct 12 0u 1 S
> > + # Zone NAME STDOFF RULES FORMAT [UNTIL]
> > + {2} Test 0 - Z1 2000 Jan 13
> > + 0 Rule Z%s
> > + # Link TARGET LINK-NAME
> > + {3} Test Link
> > + )";
> > +
> > + const std::string ver = std::format("test_{}_{}_{}", rule, zone,
> link);
> > + std::string input = std::format(templ, ver, rule, zone, link);
> > + std::ofstream("tzdata.zi") << input;
> > +
> > + const auto& db = reload_tzdb();
> > + VERIFY( override_used ); // If this fails then XFAIL for the target.
> > + VERIFY( db.version == ver );
> > +
> > + sys_info info =
> locate_zone("Test")->get_info(sys_days(2005y/February/1));
> > + VERIFY( info.begin == sys_days(2005y/January/13) );
> > + VERIFY( info.end == sys_days(2005y/October/12) );
> > +
> > + const time_zone* target = locate_zone("Link");
> > + VERIFY( target->name() == "Test" );
> > +}
> > +
> > +void
> > +test_lines()
> > +{
> > + auto test_rule = [](std::string_view name) { test_line(name, "Zone",
> "Link"); };
> > + auto test_zone = [](std::string_view name) { test_line("rule", name,
> "link"); };
> > + auto test_link = [](std::string_view name) { test_line("RULE",
> "ZONE", name); };
> > +
> > + test_combinations(test_rule, "rule", 1);
> > + test_combinations(test_zone, "zone", 1);
> > + test_combinations(test_link, "link", 1);
> > +}
> > +
> > +int main()
> > +{
> > + test_days();
> > + test_months();
> > + test_years();
> > + test_times();
> > + test_lines();
> > +}
> > --
> > 2.55.0
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260827/3ea15fb9/attachment-0001.htm>
More information about the Libstdc++
mailing list