Skip to content

Commit 30e7076

Browse files
committed
Addresses review comments.
1 parent 5b2ab38 commit 30e7076

File tree

8 files changed

+105
-39
lines changed

8 files changed

+105
-39
lines changed

libcxx/include/__chrono/sys_info.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828

2929
_LIBCPP_BEGIN_NAMESPACE_STD
3030

31-
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
32-
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
31+
# if _LIBCPP_STD_VER >= 20
3332

3433
namespace chrono {
3534

@@ -43,8 +42,7 @@ struct sys_info {
4342

4443
} // namespace chrono
4544

46-
# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
47-
// && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
45+
# endif //_LIBCPP_STD_VER >= 20
4846

4947
_LIBCPP_END_NAMESPACE_STD
5048

libcxx/include/chrono

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ constexpr chrono::year operator ""y(unsigned lo
893893
#include <__chrono/month_weekday.h>
894894
#include <__chrono/monthday.h>
895895
#include <__chrono/steady_clock.h>
896+
#include <__chrono/sys_info.h>
896897
#include <__chrono/system_clock.h>
897898
#include <__chrono/time_point.h>
898899
#include <__chrono/weekday.h>
@@ -918,7 +919,6 @@ constexpr chrono::year operator ""y(unsigned lo
918919
#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
919920
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
920921
# include <__chrono/leap_second.h>
921-
# include <__chrono/sys_info.h>
922922
# include <__chrono/time_zone.h>
923923
# include <__chrono/time_zone_link.h>
924924
# include <__chrono/tzdb.h>

libcxx/src/time_zone.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
// It would be possible to cache lookups. If a time for a zone is calculated its
1818
// sys_info could be kept and the next lookup could test whether the time is in
1919
// a "known" sys_info. The wording in the Standard hints at this slowness by
20-
// "suggesting" this could be implemented at the user's side.
20+
// "suggesting" this could be implemented on the user's side.
2121

2222
// TODO TZDB look at removing quirks
2323
//
@@ -30,6 +30,7 @@
3030
// which implies there are no sys_info objects with a duration of less than 12h.
3131

3232
#include <algorithm>
33+
#include <cctype>
3334
#include <chrono>
3435
#include <expected>
3536
#include <map>
@@ -111,20 +112,8 @@ __binary_find(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __pro
111112
// text in the appropriate Rule's LETTER column, and the resulting string
112113
// should be a time zone abbreviation
113114
//
114-
// Accepting invalid formats that can be processed in a sensible way would better
115-
// serve the user than throwing an exception. So some of these rules are not
116-
// strictly validated.
117-
// 1 This is not validated. Some examples that will be accepted are, "+04:30",
118-
// "Q", "42".
119-
// 2 How this format is formatted is not specified. In the current tzdata.zi
120-
// this value is not used. This value is accepted in a part of the format. So
121-
// "a%s%zb" will be considered valid.
122-
// 3 This is not validated, the output might be incorrect.
123-
// Proper validation would make the algorithm more complex. Then the first
124-
// element of the pair is used the parsing of FORMAT can stop. To do proper
125-
// validation the tail should be validated.
126-
// 4 This value is accepted in a part of the format. So "a%s%zb" will be
127-
// considered valid.
115+
// Rule 1 is not strictly validated since America/Barbados uses a two letter
116+
// abbreviation AT.
128117
[[nodiscard]] static string
129118
__format(const __tz::__continuation& __continuation, const string& __letters, seconds __save) {
130119
bool __shift = false;
@@ -137,6 +126,11 @@ __format(const __tz::__continuation& __continuation, const string& __letters, se
137126
break;
138127

139128
case 'z': {
129+
if (__continuation.__format.size() != 2)
130+
std::__throw_runtime_error(
131+
std::format("corrupt tzdb FORMAT field: %z should be the entire contents, instead contains '{}'",
132+
__continuation.__format)
133+
.c_str());
140134
chrono::hh_mm_ss __offset{__continuation.__stdoff + __save};
141135
if (__offset.is_negative()) {
142136
__result += '-';
@@ -164,14 +158,22 @@ __format(const __tz::__continuation& __continuation, const string& __letters, se
164158

165159
} else if (__c == '%') {
166160
__shift = true;
167-
} else {
161+
} else if (__c == '+' || __c == '-' || std::isalnum(__c)) {
168162
__result.push_back(__c);
163+
} else {
164+
std::__throw_runtime_error(
165+
std::format(
166+
"corrupt tzdb FORMAT field: invalid character '{}' found, expected +, -, or an alphanumeric value", __c)
167+
.c_str());
169168
}
170169
}
171170

172171
if (__shift)
173172
std::__throw_runtime_error("corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'");
174173

174+
if (__result.empty())
175+
std::__throw_runtime_error("corrupt tzdb FORMAT field: result is empty");
176+
175177
return __result;
176178
}
177179

@@ -348,7 +350,7 @@ class __named_rule_until {
348350
// R HK 1946 o - Ap 21 0 1 S // (3)
349351
// There (1) is active until Novemer 18th 1945 at 02:00, after this time
350352
// (2) becomes active. The first rule entry for HK (3) becomes active
351-
// from pril 21st 1945 at 01:00. In the period between (2) is active.
353+
// from April 21st 1945 at 01:00. In the period between (2) is active.
352354
// This entry has an offset.
353355
// This entry has no save, letters, or dst flag. So in the period
354356
// after (1) and until (3) no rule entry is associated with the time.
@@ -439,11 +441,11 @@ __next_rule(sys_seconds __time,
439441
if (__y == __year && __it == __current)
440442
continue;
441443

442-
sys_seconds __t = __rule_to_sys_seconds(__stdoff, __save, *__it, __y);
444+
sys_seconds __t = chrono::__rule_to_sys_seconds(__stdoff, __save, *__it, __y);
443445
if (__t <= __time)
444446
continue;
445447

446-
_LIBCPP_ASSERT(!__candidates.contains(__t), "duplicated rule");
448+
_LIBCPP_ASSERT_INTERNAL(!__candidates.contains(__t), "duplicated rule");
447449
__candidates[__t] = __it;
448450
break;
449451
}
@@ -495,7 +497,7 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
495497
const vector<__tz::__rule>& __rules = __get_rules(__rule_name);
496498

497499
auto __rule = chrono::__first_rule(__continuation.__stdoff, __rules);
498-
_LIBCPP_ASSERT(__rule != __rules.end(), "the set of rules has no first rule");
500+
_LIBCPP_ASSERT_INTERNAL(__rule != __rules.end(), "the set of rules has no first rule");
499501

500502
// Avoid selecting a time before the start of the continuation
501503
__time = std::max(__time, __continuation_begin);
@@ -726,7 +728,7 @@ _LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
726728
time_zone::__get_info(sys_seconds __time) const {
727729
optional<sys_info> __result;
728730
bool __valid_result = false; // true iff __result.has_value() is true and
729-
// result.begin <= __time < __result.end is true.
731+
// __result.begin <= __time < __result.end is true.
730732
bool __can_merge = false;
731733
sys_seconds __continuation_begin = sys_seconds::min();
732734
// Iterates over the Zone entry and its continuations. Internally the Zone
@@ -746,9 +748,9 @@ time_zone::__get_info(sys_seconds __time) const {
746748
// no continuation is applicable it will return the end time as "error". When
747749
// two continuations are contiguous and contain the "same" information these
748750
// ranges are merged as one range.
749-
// The merging requires to keep results occur before __time, likewise when a
750-
// valid result is found the algorithm needs test the next continuation to see
751-
// when it can be merged. For example, Africa/Ceuta
751+
// The merging requires keeping any result that occurs before __time,
752+
// likewise when a valid result is found the algorithm needs to test the next
753+
// continuation to see whether it can be merged. For example, Africa/Ceuta
752754
// Continuations
753755
// 0 s WE%sT 1929 (C1)
754756
// 0 - WET 1967 (C2)
@@ -779,7 +781,7 @@ time_zone::__get_info(sys_seconds __time) const {
779781
__sys_info_result __sys_info = chrono::__get_sys_info(__time, __continuation_begin, __continuation);
780782

781783
if (__sys_info) {
782-
_LIBCPP_ASSERT(__sys_info->__info.begin < __sys_info->__info.end, "invalid sys_info range");
784+
_LIBCPP_ASSERT_INTERNAL(__sys_info->__info.begin < __sys_info->__info.end, "invalid sys_info range");
783785

784786
// Filters out dummy entries
785787
// Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
@@ -813,7 +815,8 @@ time_zone::__get_info(sys_seconds __time) const {
813815
__can_merge = __sys_info->__can_merge;
814816
} else if (__can_merge && chrono::__merge_continuation(*__result, __sys_info->__info)) {
815817
// The results are merged, update the result state. This may
816-
// "overwrite" valid with valid.
818+
// "overwrite" a valid sys_info object with another valid sys_info
819+
// object.
817820
__valid_result = __time >= __result->begin && __time < __result->end;
818821
__can_merge = __sys_info->__can_merge;
819822
} else {
@@ -843,7 +846,7 @@ time_zone::__get_info(sys_seconds __time) const {
843846
if (__valid_result) {
844847
return *__result;
845848
} else {
846-
_LIBCPP_ASSERT(__it != __continuations.begin(), "the first rule should always seed the result");
849+
_LIBCPP_ASSERT_INTERNAL(__it != __continuations.begin(), "the first rule should always seed the result");
847850
const auto& __last = *(__it - 1);
848851
if (std::holds_alternative<string>(__last.__rules)) {
849852
// Europe/Berlin

libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/get_info.sys_time.pass.cpp

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
// sys_info get_info(const sys_time<_Duration>& time) const;
2323

2424
// tests the parts not validated in the public test
25+
// - Validates a zone with an UNTIL in its last continuation is corrupt
26+
// - The formatting of the FORMAT field's constrains
27+
// - Formatting of "%z", this is valid but not present in the actual database
2528

2629
#include <algorithm>
2730
#include <cassert>
@@ -66,7 +69,7 @@ static const std::chrono::tzdb& parse(std::string_view input) {
6669
}
6770

6871
static void test_exception([[maybe_unused]] std::string_view input, [[maybe_unused]] std::string_view what) {
69-
#ifndef TEST_NOEXCEPT
72+
#ifndef TEST_HAS_NO_EXCEPTIONS
7073
const std::chrono::tzdb& tzdb = parse(input);
7174
const std::chrono::time_zone* tz = tzdb.locate_zone("Format");
7275
TEST_VALIDATE_EXCEPTION(
@@ -77,21 +80,76 @@ static void test_exception([[maybe_unused]] std::string_view input, [[maybe_unus
7780
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
7881
},
7982
TEST_IGNORE_NODISCARD tz->get_info(to_sys_seconds(2000)));
80-
#endif
83+
#endif // TEST_HAS_NO_EXCEPTIONS
84+
}
85+
86+
static void zone_without_until_entry() {
87+
#ifndef TEST_HAS_NO_EXCEPTIONS
88+
const std::chrono::tzdb& tzdb = parse(
89+
R"(
90+
Z America/Paramaribo -3:40:40 - LMT 1911
91+
-3:40:52 - PMT 1935
92+
-3:40:36 - PMT 1945 O
93+
-3:30 - -0330 1984 O
94+
# -3 - -03 Commented out so the last entry has an UNTIL field.
95+
)");
96+
const std::chrono::time_zone* tz = tzdb.locate_zone("America/Paramaribo");
97+
98+
TEST_IGNORE_NODISCARD tz->get_info(to_sys_seconds(1984));
99+
TEST_VALIDATE_EXCEPTION(
100+
std::runtime_error,
101+
[&]([[maybe_unused]] const std::runtime_error& e) {
102+
std::string what = "tzdb: corrupt db";
103+
TEST_LIBCPP_REQUIRE(
104+
e.what() == what,
105+
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
106+
},
107+
TEST_IGNORE_NODISCARD tz->get_info(to_sys_seconds(1985)));
108+
#endif // TEST_HAS_NO_EXCEPTIONS
81109
}
82110

83111
static void invalid_format() {
84112
test_exception(
85113
R"(
86114
R F 2000 max - Jan 5 0 0 foo
115+
Z Format 0 F %zandfoo)",
116+
"corrupt tzdb FORMAT field: %z should be the entire contents, instead contains '%zandfoo'");
117+
118+
test_exception(
119+
R"(
120+
R F 2000 max - Jan 5 0 0 foo
87121
Z Format 0 F %q)",
88122
"corrupt tzdb FORMAT field: invalid sequence '%q' found, expected %s or %z");
89123

124+
test_exception(
125+
R"(
126+
R F 2000 max - Jan 5 0 0 foo
127+
Z Format 0 F !)",
128+
"corrupt tzdb FORMAT field: invalid character '!' found, expected +, -, or an alphanumeric value");
129+
130+
test_exception(
131+
R"(
132+
R F 2000 max - Jan 5 0 0 foo
133+
Z Format 0 F @)",
134+
"corrupt tzdb FORMAT field: invalid character '@' found, expected +, -, or an alphanumeric value");
135+
136+
test_exception(
137+
R"(
138+
R F 2000 max - Jan 5 0 0 foo
139+
Z Format 0 F $)",
140+
"corrupt tzdb FORMAT field: invalid character '$' found, expected +, -, or an alphanumeric value");
141+
90142
test_exception(
91143
R"(
92144
R F 1970 max - Jan 5 0 0 foo
93145
Z Format 0 F %)",
94146
"corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'");
147+
148+
test_exception(
149+
R"(
150+
R F 2000 max - Jan 5 0 0 -
151+
Z Format 0 F %s)",
152+
"corrupt tzdb FORMAT field: result is empty");
95153
}
96154

97155
static void test_abbrev(std::string_view input, std::string_view expected) {
@@ -135,6 +193,7 @@ Z Format 0:45 F %z)",
135193
}
136194

137195
int main(int, const char**) {
196+
zone_without_until_entry();
138197
invalid_format();
139198
percentage_z_format();
140199

libcxx/test/libcxx/transitive_includes/cxx23.csv

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ chrono cstring
8080
chrono ctime
8181
chrono cwchar
8282
chrono forward_list
83-
chrono initializer_list
8483
chrono limits
8584
chrono new
8685
chrono optional

libcxx/test/libcxx/transitive_includes/cxx26.csv

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ chrono cstring
8080
chrono ctime
8181
chrono cwchar
8282
chrono forward_list
83-
chrono initializer_list
8483
chrono limits
8584
chrono new
8685
chrono optional

libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.sys/sys_info.members.compile.pass.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
//===----------------------------------------------------------------------===//
88

99
// UNSUPPORTED: c++03, c++11, c++14, c++17
10-
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
1110

1211
// XFAIL: libcpp-has-no-incomplete-tzdb
1312

@@ -21,13 +20,19 @@
2120
// string abbrev;
2221
// };
2322

23+
// Validates whether:
24+
// - The members are present as non-const members.
25+
// - The struct is an aggregate.
26+
2427
#include <chrono>
2528
#include <string>
29+
#include <type_traits>
2630

2731
std::chrono::sys_info sys_info;
28-
2932
[[maybe_unused]] std::chrono::sys_seconds& begin = sys_info.begin;
3033
[[maybe_unused]] std::chrono::sys_seconds& end = sys_info.end;
3134
[[maybe_unused]] std::chrono::seconds& offset = sys_info.offset;
3235
[[maybe_unused]] std::chrono::minutes& save = sys_info.save;
3336
[[maybe_unused]] std::string& abbrev = sys_info.abbrev;
37+
38+
static_assert(std::is_aggregate_v<std::chrono::sys_info>);

libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/sys_info.zdump.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <fstream>
2626
#include <cassert>
2727

28+
#include "filesystem_test_helper.h"
2829
#include "assert_macros.h"
2930
#include "concat_macros.h"
3031

@@ -106,8 +107,10 @@ void process(std::ostream& stream, const std::chrono::time_zone& zone) {
106107
}
107108

108109
int main(int, const char**) {
110+
scoped_test_env env;
111+
const std::string file = env.create_file("zdump.txt");
112+
109113
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
110-
std::string file = std::tmpnam(nullptr);
111114
for (const auto& zone : tzdb.zones) {
112115
std::stringstream libcxx;
113116
process(libcxx, zone);

0 commit comments

Comments
 (0)