summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2022-09-27 13:50:41 -0400
committerLinus Groh <mail@linusgroh.de>2022-09-28 23:52:51 +0100
commit9a1b24d5bef6e706cebe7e7ef7ca18007b4860c8 (patch)
tree64811ab4af0257948c05363cf4f63669e931bd35
parenta1943039489b1e9c4742498ee8424e10379de5a4 (diff)
downloadserenity-9a1b24d5bef6e706cebe7e7ef7ca18007b4860c8.zip
LibTimeZone: Use the last DST rule in the TZDB if a match isn't found
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.
-rw-r--r--Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp14
-rw-r--r--Tests/LibTimeZone/TestTimeZone.cpp4
2 files changed, 14 insertions, 4 deletions
diff --git a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp
index 4762c63a05..adac6c3387 100644
--- a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp
+++ b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp
@@ -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)
@@ -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;
@@ -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 };
diff --git a/Tests/LibTimeZone/TestTimeZone.cpp b/Tests/LibTimeZone/TestTimeZone.cpp
index 1e44cffba2..7cf8a6fc2e 100644
--- a/Tests/LibTimeZone/TestTimeZone.cpp
+++ b/Tests/LibTimeZone/TestTimeZone.cpp
@@ -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