Skip to content

Commit

Permalink
LibTimeZone: Use the last DST rule in the TZDB if a match isn't found
Browse files Browse the repository at this point in the history
Some time zones, like "Asia/Shanghai", use a set of DST rules that end
before present day. In these cases, we should fall back to last possible
RULE entry from the TZDB. The time zone compiler published by IANA (zic)
performs the same fallback starting with version 2 of the time zone file
format.
  • Loading branch information
trflynn89 authored and linusg committed Sep 28, 2022
1 parent a194303 commit 9a1b24d
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ static Array<DaylightSavingsOffset const*, 2> find_dst_offsets(TimeZoneOffset co
DaylightSavingsOffset const* standard_offset = nullptr;
DaylightSavingsOffset const* daylight_offset = nullptr;
DaylightSavingsOffset const* last_offset = nullptr;
auto preferred_rule = [&](auto* current_offset, auto& new_offset) {
if (!current_offset)
Expand All @@ -652,6 +653,12 @@ static Array<DaylightSavingsOffset const*, 2> find_dst_offsets(TimeZoneOffset co
for (size_t index = 0; (index < dst_rules.size()) && (!standard_offset || !daylight_offset); ++index) {
auto const& dst_rule = dst_rules[index];
if (last_offset == nullptr)
last_offset = &dst_rule;
else if (dst_rule.time_in_effect(dst_rule.year_to) > last_offset->time_in_effect(last_offset->year_to))
last_offset = &dst_rule;
if ((time < dst_rule.year_from) || (time >= dst_rule.year_to))
continue;
Expand All @@ -661,11 +668,10 @@ static Array<DaylightSavingsOffset const*, 2> find_dst_offsets(TimeZoneOffset co
daylight_offset = preferred_rule(daylight_offset, dst_rule);
}
// In modern times, there will always be a standard rule in the TZDB, but that isn't true in
// all time zones in or before the early 1900s. For example, the "US" rules begin in 1918.
// If there isn't a standard or daylight rule in effect, fall back to the last rule given in the TZDB.
if (!standard_offset) {
static DaylightSavingsOffset const empty_offset {};
return { &empty_offset, &empty_offset };
VERIFY(last_offset != nullptr);
standard_offset = last_offset;
}
return { standard_offset, daylight_offset ? daylight_offset : standard_offset };
Expand Down
4 changes: 4 additions & 0 deletions Tests/LibTimeZone/TestTimeZone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ TEST_CASE(get_named_time_zone_offsets)
test_named_offsets("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19), offset(+1, 3, 31, 19), "MSK"sv, "MSD"sv); // Wednesday, January 1, 1919 12:00:00 AM
test_named_offsets("Europe/Moscow"sv, -1596412800, offset(+1, 2, 31, 19), offset(+1, 4, 31, 19), "MSK"sv, "MDST"sv); // Sunday, June 1, 1919 12:00:00 AM
test_named_offsets("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), offset(+1, 4, 00, 00), "MSK"sv, "MSD"sv); // Monday, August 25, 1919 12:00:00 AM

// Shanghai's DST rules end in 1991.
test_named_offsets("Asia/Shanghai"sv, 694223999, offset(+1, 8, 00, 00), offset(+1, 9, 00, 00), "CST"sv, "CDT"sv); // Tuesday, December 31, 1991 11:59:59 PM
test_named_offsets("Asia/Shanghai"sv, 694224000, offset(+1, 8, 00, 00), offset(+1, 8, 00, 00), "CST"sv, "CST"sv); // Wednesday, January 1, 1992 12:00:00 AM
}

#else
Expand Down

0 comments on commit 9a1b24d

Please sign in to comment.