Skip to content

Commit 65eea3e

Browse files
mordanteldionne
andauthored
[libc++][TZDB] Fixes parsing interleaved rules. (#84808)
Typically the rules in the database are contiguous, but that is not a requirement. This fixes the case when they are not. --------- Co-authored-by: Louis Dionne <[email protected]>
1 parent fa1d135 commit 65eea3e

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

libcxx/src/tzdb.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,14 +511,33 @@ static string __parse_version(istream& __input) {
511511
return chrono::__parse_string(__input);
512512
}
513513

514+
[[nodiscard]]
515+
static __tz::__rule& __create_entry(__tz::__rules_storage_type& __rules, const string& __name) {
516+
auto __result = [&]() -> __tz::__rule& {
517+
auto& __rule = __rules.emplace_back(__name, vector<__tz::__rule>{});
518+
return __rule.second.emplace_back();
519+
};
520+
521+
if (__rules.empty())
522+
return __result();
523+
524+
// Typically rules are in contiguous order in the database.
525+
// But there are exceptions, some rules are interleaved.
526+
if (__rules.back().first == __name)
527+
return __rules.back().second.emplace_back();
528+
529+
if (auto __it = ranges::find(__rules, __name, [](const auto& __r) { return __r.first; });
530+
__it != ranges::end(__rules))
531+
return __it->second.emplace_back();
532+
533+
return __result();
534+
}
535+
514536
static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
515537
chrono::__skip_mandatory_whitespace(__input);
516538
string __name = chrono::__parse_string(__input);
517539

518-
if (__rules.empty() || __rules.back().first != __name)
519-
__rules.emplace_back(__name, vector<__tz::__rule>{});
520-
521-
__tz::__rule& __rule = __rules.back().second.emplace_back();
540+
__tz::__rule& __rule = __create_entry(__rules, __name);
522541

523542
chrono::__skip_mandatory_whitespace(__input);
524543
__rule.__from = chrono::__parse_year(__input);

libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,29 @@ R a 0 1 - Ja Su>=31 1w 2s abc
550550
assert(result.rules[0].second[2].__letters == "abc");
551551
}
552552

553+
static void test_mixed_order() {
554+
// This is a part of the real database. The interesting part is that the
555+
// rules NZ and Chatham are interleaved. Make sure the parse algorithm
556+
// handles this correctly.
557+
parse_result result{
558+
R"(
559+
# Since 1957 Chatham has been 45 minutes ahead of NZ, but until 2018a
560+
# there was no documented single notation for the date and time of this
561+
# transition. Duplicate the Rule lines for now, to give the 2018a change
562+
# time to percolate out.
563+
Rule NZ 1974 only - Nov Sun>=1 2:00s 1:00 D
564+
Rule Chatham 1974 only - Nov Sun>=1 2:45s 1:00 -
565+
Rule NZ 1975 only - Feb lastSun 2:00s 0 S
566+
Rule Chatham 1975 only - Feb lastSun 2:45s 0 -
567+
Rule NZ 1975 1988 - Oct lastSun 2:00s 1:00 D
568+
Rule Chatham 1975 1988 - Oct lastSun 2:45s 1:00 -
569+
)"};
570+
571+
assert(result.rules.size() == 2);
572+
assert(result.rules[0].second.size() == 3);
573+
assert(result.rules[1].second.size() == 3);
574+
}
575+
553576
int main(int, const char**) {
554577
test_invalid();
555578
test_name();
@@ -560,6 +583,7 @@ int main(int, const char**) {
560583
test_at();
561584
test_save();
562585
test_letter();
586+
test_mixed_order();
563587

564588
return 0;
565589
}

0 commit comments

Comments
 (0)