Skip to content

[libc++][chrono] Fixes leap seconds. #90070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
"`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|"
"`3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\ should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0"
"`3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\ can throw","Prague","|Complete|","17.0"
"`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","Prague","|Complete|","19.0","|chrono|"
"`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","Prague","|In Progress|","","|chrono|"
"`3360 <https://wg21.link/LWG3360>`__","``three_way_comparable_with``\ is inconsistent with similar concepts","Prague","|Nothing To Do|","","|spaceship|"
"`3362 <https://wg21.link/LWG3362>`__","Strike ``stop_source``\ 's ``operator!=``\ ","Prague","",""
"`3363 <https://wg21.link/LWG3363>`__","``drop_while_view``\ should opt-out of ``sized_range``\ ","Prague","|Nothing To Do|","","|ranges|"
Expand Down
64 changes: 40 additions & 24 deletions libcxx/src/tzdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,29 +626,49 @@ static void __parse_leap_seconds(vector<leap_second>& __leap_seconds, istream&&
// seconds since 1 January 1970.
constexpr auto __offset = sys_days{1970y / January / 1} - sys_days{1900y / January / 1};

while (true) {
switch (__input.peek()) {
case istream::traits_type::eof():
return;

case ' ':
case '\t':
case '\n':
__input.get();
continue;
struct __entry {
sys_seconds __timestamp;
seconds __value;
};
vector<__entry> __entries;
[&] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of wrapping the loop in an IILE?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the while loop there is a switch that should terminate the processing when EOF is found. Since test is in a switch I can't use break. Using a IILE allows to use return. Refactoring this lambda to a function doesn't improve code readability, so I prefer an IILE.

while (true) {
switch (__input.peek()) {
case istream::traits_type::eof():
return;

case ' ':
case '\t':
case '\n':
__input.get();
continue;

case '#':
chrono::__skip_line(__input);
continue;
}

case '#':
sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset;
chrono::__skip_mandatory_whitespace(__input);
seconds __value{chrono::__parse_integral(__input, false)};
chrono::__skip_line(__input);
continue;
}

sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset;
chrono::__skip_mandatory_whitespace(__input);
seconds __value{chrono::__parse_integral(__input, false)};
chrono::__skip_line(__input);

__leap_seconds.emplace_back(std::__private_constructor_tag{}, __date, __value);
}
__entries.emplace_back(__date, __value);
}
}();
// The Standard requires the leap seconds to be sorted. The file
// leap-seconds.list usually provides them in sorted order, but that is not
// guaranteed so we ensure it here.
ranges::sort(__entries, {}, &__entry::__timestamp);

// The database should contain the number of seconds inserted by a leap
// second (1 or -1). So the difference between the two elements is stored.
// std::ranges::views::adjacent has not been implemented yet.
(void)ranges::adjacent_find(__entries, [&](const __entry& __first, const __entry& __second) {
__leap_seconds.emplace_back(
std::__private_constructor_tag{}, __second.__timestamp, __second.__value - __first.__value);
return false;
});
}

void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
Expand All @@ -667,10 +687,6 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
// The latter is much easier to parse, it seems Howard shares that
// opinion.
chrono::__parse_leap_seconds(__tzdb.leap_seconds, ifstream{__root / "leap-seconds.list"});
// The Standard requires the leap seconds to be sorted. The file
// leap-seconds.list usually provides them in sorted order, but that is not
// guaranteed so we ensure it here.
std::ranges::sort(__tzdb.leap_seconds);
}

#ifdef _WIN32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,32 +83,39 @@ static void test_leap_seconds() {
2303683200 12 # 1 Jan 1973
2287785600 11 # 1 Jul 1972
2272060800 10 # 1 Jan 1972
86400 1 # 2 Jan 1900 Dummy entry to test before 1970
86400 9 # 2 Jan 1900 Dummy entry to test before 1970
1 8 # 2 Jan 1900 Dummy entry to test before 1970

# Fictional negative leap second
2303769600 11 # 2 Jan 1973

# largest accepted value by the parser
5764607523034234879 2
5764607523034234879 12
)");

assert(result.leap_seconds.size() == 5);
assert(result.leap_seconds.size() == 6);

assert(result.leap_seconds[0].date() == sys_seconds{sys_days{1900y / January / 2}});
assert(result.leap_seconds[0].value() == 1s);

assert(result.leap_seconds[1].date() == sys_seconds{sys_days{1972y / January / 1}});
assert(result.leap_seconds[1].value() == 10s);
assert(result.leap_seconds[1].value() == 1s);

assert(result.leap_seconds[2].date() == sys_seconds{sys_days{1972y / July / 1}});
assert(result.leap_seconds[2].value() == 11s);
assert(result.leap_seconds[2].value() == 1s);

assert(result.leap_seconds[3].date() == sys_seconds{sys_days{1973y / January / 1}});
assert(result.leap_seconds[3].value() == 12s);
assert(result.leap_seconds[3].value() == 1s);

assert(result.leap_seconds[4].date() == sys_seconds{sys_days{1973y / January / 2}});
assert(result.leap_seconds[4].value() == -1s);

assert(result.leap_seconds[4].date() ==
assert(result.leap_seconds[5].date() ==
sys_seconds{5764607523034234879s
// The database uses 1900-01-01 as epoch.
- std::chrono::duration_cast<std::chrono::seconds>(
sys_days{1970y / January / 1} - sys_days{1900y / January / 1})});
assert(result.leap_seconds[4].value() == 2s);
assert(result.leap_seconds[5].value() == 1s);
}

int main(int, const char**) {
Expand Down
55 changes: 27 additions & 28 deletions libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,33 @@ using namespace std::literals::chrono_literals;
// At the moment of writing that list is the actual list in the IANA database.
// If in the future more leap seconds can be added.
static const std::array leap_seconds = {
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::January / 1}}, 10s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::July / 1}}, 11s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1973y / std::chrono::January / 1}}, 12s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1974y / std::chrono::January / 1}}, 13s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1975y / std::chrono::January / 1}}, 14s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1976y / std::chrono::January / 1}}, 15s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1977y / std::chrono::January / 1}}, 16s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1978y / std::chrono::January / 1}}, 17s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1979y / std::chrono::January / 1}}, 18s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1980y / std::chrono::January / 1}}, 19s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1981y / std::chrono::July / 1}}, 20s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1982y / std::chrono::July / 1}}, 21s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1983y / std::chrono::July / 1}}, 22s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1985y / std::chrono::July / 1}}, 23s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1988y / std::chrono::January / 1}}, 24s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1990y / std::chrono::January / 1}}, 25s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1991y / std::chrono::January / 1}}, 26s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1992y / std::chrono::July / 1}}, 27s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1993y / std::chrono::July / 1}}, 28s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1994y / std::chrono::July / 1}}, 29s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1996y / std::chrono::January / 1}}, 30s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1997y / std::chrono::July / 1}}, 31s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1999y / std::chrono::January / 1}}, 32s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2006y / std::chrono::January / 1}}, 33s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2009y / std::chrono::January / 1}}, 34s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2012y / std::chrono::July / 1}}, 35s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2015y / std::chrono::July / 1}}, 36s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2017y / std::chrono::January / 1}}, 37s)};
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1973y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1974y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1975y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1976y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1977y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1978y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1979y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1980y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1981y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1982y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1983y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1985y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1988y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1990y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1991y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1992y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1993y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1994y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1996y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1997y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1999y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2006y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2009y / std::chrono::January / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2012y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2015y / std::chrono::July / 1}}, 1s),
std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2017y / std::chrono::January / 1}}, 1s)};

int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
Expand Down
Loading