Skip to content

Commit 4be4a60

Browse files
committed
Addresses review comments.
1 parent 5b2ab38 commit 4be4a60

File tree

12 files changed

+315
-62
lines changed

12 files changed

+315
-62
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/include/tzdb/time_zone_private.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace chrono {
2424

2525
class time_zone::__impl {
2626
public:
27-
explicit _LIBCPP_HIDE_FROM_ABI __impl(string&& __name) : __name_(std::move(__name)) {}
27+
explicit _LIBCPP_HIDE_FROM_ABI __impl(string&& __name, const __tz::__rules_storage_type& __rules_db)
28+
: __name_(std::move(__name)), __rules_db_(__rules_db) {}
2829

2930
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view __name() const noexcept { return __name_; }
3031

@@ -33,12 +34,20 @@ class time_zone::__impl {
3334
return __continuations_;
3435
}
3536

37+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __tz::__rules_storage_type& __rules_db() const { return __rules_db_; }
38+
3639
private:
3740
string __name_;
3841
// Note the first line has a name + __continuation, the other lines
3942
// are just __continuations. So there is always at least one item in
4043
// the vector.
4144
vector<__tz::__continuation> __continuations_;
45+
46+
// Continuations often depend on a set of rules. The rules are stored in
47+
// parallel data structurs in tzdb_list. From the time_zone it's not possible
48+
// to find its associated tzdb entry and thus not possible to find its
49+
// associated rules. Therefore a link to the rules in stored in this class.
50+
const __tz::__rules_storage_type& __rules_db_;
4251
};
4352

4453
} // namespace chrono

libcxx/src/include/tzdb/tzdb_list_private.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,6 @@ class tzdb_list::__impl {
8181
return __tzdb_.end();
8282
}
8383

84-
const __tz::__rules_storage_type& __rules() const noexcept {
85-
#ifndef _LIBCPP_HAS_NO_THREADS
86-
shared_lock __lock{__mutex_};
87-
#endif
88-
return __rules_.front();
89-
}
90-
9184
private:
9285
// Loads the tzdbs
9386
// pre: The caller ensures the locking, if needed, is done.

libcxx/src/time_zone.cpp

Lines changed: 44 additions & 39 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

@@ -300,11 +302,10 @@ class __named_rule_until {
300302
return chrono::__from_to_sys_seconds(__stdoff, __rule, __rule.__from);
301303
}
302304

303-
[[nodiscard]] static const vector<__tz::__rule>& __get_rules(const string& __rule_name) {
304-
const __tz::__rules_storage_type& __rules = get_tzdb_list().__implementation().__rules();
305-
306-
auto __result = chrono::__binary_find(__rules, __rule_name, {}, [](const auto& __p) { return __p.first; });
307-
if (__result == std::end(__rules))
305+
[[nodiscard]] static const vector<__tz::__rule>&
306+
__get_rules(const __tz::__rules_storage_type& __rules_db, const string& __rule_name) {
307+
auto __result = chrono::__binary_find(__rules_db, __rule_name, {}, [](const auto& __p) { return __p.first; });
308+
if (__result == std::end(__rules_db))
308309
std::__throw_runtime_error(("corrupt tzdb: rule '" + __rule_name + " 'does not exist").c_str());
309310

310311
return __result->second;
@@ -348,7 +349,7 @@ class __named_rule_until {
348349
// R HK 1946 o - Ap 21 0 1 S // (3)
349350
// There (1) is active until Novemer 18th 1945 at 02:00, after this time
350351
// (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.
352+
// from April 21st 1945 at 01:00. In the period between (2) is active.
352353
// This entry has an offset.
353354
// This entry has no save, letters, or dst flag. So in the period
354355
// after (1) and until (3) no rule entry is associated with the time.
@@ -439,11 +440,11 @@ __next_rule(sys_seconds __time,
439440
if (__y == __year && __it == __current)
440441
continue;
441442

442-
sys_seconds __t = __rule_to_sys_seconds(__stdoff, __save, *__it, __y);
443+
sys_seconds __t = chrono::__rule_to_sys_seconds(__stdoff, __save, *__it, __y);
443444
if (__t <= __time)
444445
continue;
445446

446-
_LIBCPP_ASSERT(!__candidates.contains(__t), "duplicated rule");
447+
_LIBCPP_ASSERT_INTERNAL(!__candidates.contains(__t), "duplicated rule");
447448
__candidates[__t] = __it;
448449
break;
449450
}
@@ -491,11 +492,9 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
491492
sys_seconds __time,
492493
sys_seconds __continuation_begin,
493494
const __tz::__continuation& __continuation,
494-
const string& __rule_name) {
495-
const vector<__tz::__rule>& __rules = __get_rules(__rule_name);
496-
495+
const vector<__tz::__rule>& __rules) {
497496
auto __rule = chrono::__first_rule(__continuation.__stdoff, __rules);
498-
_LIBCPP_ASSERT(__rule != __rules.end(), "the set of rules has no first rule");
497+
_LIBCPP_ASSERT_INTERNAL(__rule != __rules.end(), "the set of rules has no first rule");
499498

500499
// Avoid selecting a time before the start of the continuation
501500
__time = std::max(__time, __continuation_begin);
@@ -662,12 +661,16 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
662661
}
663662

664663
[[nodiscard]] static __sys_info_result
665-
__get_sys_info(sys_seconds __time, sys_seconds __continuation_begin, const __tz::__continuation& __continuation) {
664+
__get_sys_info(sys_seconds __time,
665+
sys_seconds __continuation_begin,
666+
const __tz::__continuation& __continuation,
667+
const __tz::__rules_storage_type& __rules_db) {
666668
return std::visit(
667669
[&](const auto& __value) {
668670
using _Tp = decay_t<decltype(__value)>;
669671
if constexpr (same_as<_Tp, std::string>)
670-
return chrono::__get_sys_info_rule(__time, __continuation_begin, __continuation, __value);
672+
return chrono::__get_sys_info_rule(
673+
__time, __continuation_begin, __continuation, __get_rules(__rules_db, __value));
671674
else if constexpr (same_as<_Tp, monostate>)
672675
return chrono::__get_sys_info_basic(__time, __continuation_begin, __continuation, chrono::seconds(0));
673676
else if constexpr (same_as<_Tp, __tz::__save>)
@@ -726,7 +729,7 @@ _LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
726729
time_zone::__get_info(sys_seconds __time) const {
727730
optional<sys_info> __result;
728731
bool __valid_result = false; // true iff __result.has_value() is true and
729-
// result.begin <= __time < __result.end is true.
732+
// __result.begin <= __time < __result.end is true.
730733
bool __can_merge = false;
731734
sys_seconds __continuation_begin = sys_seconds::min();
732735
// Iterates over the Zone entry and its continuations. Internally the Zone
@@ -746,9 +749,9 @@ time_zone::__get_info(sys_seconds __time) const {
746749
// no continuation is applicable it will return the end time as "error". When
747750
// two continuations are contiguous and contain the "same" information these
748751
// 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
752+
// The merging requires keeping any result that occurs before __time,
753+
// likewise when a valid result is found the algorithm needs to test the next
754+
// continuation to see whether it can be merged. For example, Africa/Ceuta
752755
// Continuations
753756
// 0 s WE%sT 1929 (C1)
754757
// 0 - WET 1967 (C2)
@@ -773,13 +776,14 @@ time_zone::__get_info(sys_seconds __time) const {
773776
// means the period [1928-10-07 00:00:00, 1967-06-03 12:00:00) should be
774777
// returned in one sys_info object.
775778

776-
const auto& __continuations = __impl_->__continuations();
779+
const auto& __continuations = __impl_->__continuations();
780+
const __tz::__rules_storage_type& __rules_db = __impl_->__rules_db();
777781
for (auto __it = __continuations.begin(); __it != __continuations.end(); ++__it) {
778782
const auto& __continuation = *__it;
779-
__sys_info_result __sys_info = chrono::__get_sys_info(__time, __continuation_begin, __continuation);
783+
__sys_info_result __sys_info = chrono::__get_sys_info(__time, __continuation_begin, __continuation, __rules_db);
780784

781785
if (__sys_info) {
782-
_LIBCPP_ASSERT(__sys_info->__info.begin < __sys_info->__info.end, "invalid sys_info range");
786+
_LIBCPP_ASSERT_INTERNAL(__sys_info->__info.begin < __sys_info->__info.end, "invalid sys_info range");
783787

784788
// Filters out dummy entries
785789
// Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
@@ -813,7 +817,8 @@ time_zone::__get_info(sys_seconds __time) const {
813817
__can_merge = __sys_info->__can_merge;
814818
} else if (__can_merge && chrono::__merge_continuation(*__result, __sys_info->__info)) {
815819
// The results are merged, update the result state. This may
816-
// "overwrite" valid with valid.
820+
// "overwrite" a valid sys_info object with another valid sys_info
821+
// object.
817822
__valid_result = __time >= __result->begin && __time < __result->end;
818823
__can_merge = __sys_info->__can_merge;
819824
} else {
@@ -843,7 +848,7 @@ time_zone::__get_info(sys_seconds __time) const {
843848
if (__valid_result) {
844849
return *__result;
845850
} else {
846-
_LIBCPP_ASSERT(__it != __continuations.begin(), "the first rule should always seed the result");
851+
_LIBCPP_ASSERT_INTERNAL(__it != __continuations.begin(), "the first rule should always seed the result");
847852
const auto& __last = *(__it - 1);
848853
if (std::holds_alternative<string>(__last.__rules)) {
849854
// Europe/Berlin

libcxx/src/tzdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istr
561561

562562
static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
563563
chrono::__skip_mandatory_whitespace(__input);
564-
auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input));
564+
auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input), __rules);
565565
vector<__tz::__continuation>& __continuations = __p->__continuations();
566566
chrono::__skip_mandatory_whitespace(__input);
567567

0 commit comments

Comments
 (0)