17
17
// It would be possible to cache lookups. If a time for a zone is calculated its
18
18
// sys_info could be kept and the next lookup could test whether the time is in
19
19
// 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.
21
21
22
22
// TODO TZDB look at removing quirks
23
23
//
30
30
// which implies there are no sys_info objects with a duration of less than 12h.
31
31
32
32
#include < algorithm>
33
+ #include < cctype>
33
34
#include < chrono>
34
35
#include < expected>
35
36
#include < map>
@@ -111,20 +112,8 @@ __binary_find(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __pro
111
112
// text in the appropriate Rule's LETTER column, and the resulting string
112
113
// should be a time zone abbreviation
113
114
//
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.
128
117
[[nodiscard]] static string
129
118
__format (const __tz::__continuation& __continuation, const string& __letters, seconds __save) {
130
119
bool __shift = false ;
@@ -137,6 +126,11 @@ __format(const __tz::__continuation& __continuation, const string& __letters, se
137
126
break ;
138
127
139
128
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 ());
140
134
chrono::hh_mm_ss __offset{__continuation.__stdoff + __save};
141
135
if (__offset.is_negative ()) {
142
136
__result += ' -' ;
@@ -164,14 +158,22 @@ __format(const __tz::__continuation& __continuation, const string& __letters, se
164
158
165
159
} else if (__c == ' %' ) {
166
160
__shift = true ;
167
- } else {
161
+ } else if (__c == ' + ' || __c == ' - ' || std::isalnum (__c)) {
168
162
__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 ());
169
168
}
170
169
}
171
170
172
171
if (__shift)
173
172
std::__throw_runtime_error (" corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'" );
174
173
174
+ if (__result.empty ())
175
+ std::__throw_runtime_error (" corrupt tzdb FORMAT field: result is empty" );
176
+
175
177
return __result;
176
178
}
177
179
@@ -300,11 +302,10 @@ class __named_rule_until {
300
302
return chrono::__from_to_sys_seconds (__stdoff, __rule, __rule.__from );
301
303
}
302
304
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))
308
309
std::__throw_runtime_error ((" corrupt tzdb: rule '" + __rule_name + " 'does not exist" ).c_str ());
309
310
310
311
return __result->second ;
@@ -348,7 +349,7 @@ class __named_rule_until {
348
349
// R HK 1946 o - Ap 21 0 1 S // (3)
349
350
// There (1) is active until Novemer 18th 1945 at 02:00, after this time
350
351
// (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.
352
353
// This entry has an offset.
353
354
// This entry has no save, letters, or dst flag. So in the period
354
355
// after (1) and until (3) no rule entry is associated with the time.
@@ -439,11 +440,11 @@ __next_rule(sys_seconds __time,
439
440
if (__y == __year && __it == __current)
440
441
continue ;
441
442
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);
443
444
if (__t <= __time)
444
445
continue ;
445
446
446
- _LIBCPP_ASSERT (!__candidates.contains (__t ), " duplicated rule" );
447
+ _LIBCPP_ASSERT_INTERNAL (!__candidates.contains (__t ), " duplicated rule" );
447
448
__candidates[__t ] = __it;
448
449
break ;
449
450
}
@@ -491,11 +492,9 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
491
492
sys_seconds __time,
492
493
sys_seconds __continuation_begin,
493
494
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) {
497
496
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" );
499
498
500
499
// Avoid selecting a time before the start of the continuation
501
500
__time = std::max (__time, __continuation_begin);
@@ -662,12 +661,16 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
662
661
}
663
662
664
663
[[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) {
666
668
return std::visit (
667
669
[&](const auto & __value) {
668
670
using _Tp = decay_t <decltype (__value)>;
669
671
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));
671
674
else if constexpr (same_as<_Tp, monostate>)
672
675
return chrono::__get_sys_info_basic (__time, __continuation_begin, __continuation, chrono::seconds (0 ));
673
676
else if constexpr (same_as<_Tp, __tz::__save>)
@@ -726,7 +729,7 @@ _LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
726
729
time_zone::__get_info (sys_seconds __time) const {
727
730
optional<sys_info> __result;
728
731
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.
730
733
bool __can_merge = false ;
731
734
sys_seconds __continuation_begin = sys_seconds::min ();
732
735
// Iterates over the Zone entry and its continuations. Internally the Zone
@@ -746,9 +749,9 @@ time_zone::__get_info(sys_seconds __time) const {
746
749
// no continuation is applicable it will return the end time as "error". When
747
750
// two continuations are contiguous and contain the "same" information these
748
751
// 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
752
755
// Continuations
753
756
// 0 s WE%sT 1929 (C1)
754
757
// 0 - WET 1967 (C2)
@@ -773,13 +776,14 @@ time_zone::__get_info(sys_seconds __time) const {
773
776
// means the period [1928-10-07 00:00:00, 1967-06-03 12:00:00) should be
774
777
// returned in one sys_info object.
775
778
776
- const auto & __continuations = __impl_->__continuations ();
779
+ const auto & __continuations = __impl_->__continuations ();
780
+ const __tz::__rules_storage_type& __rules_db = __impl_->__rules_db ();
777
781
for (auto __it = __continuations.begin (); __it != __continuations.end (); ++__it) {
778
782
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 );
780
784
781
785
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" );
783
787
784
788
// Filters out dummy entries
785
789
// Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
@@ -813,7 +817,8 @@ time_zone::__get_info(sys_seconds __time) const {
813
817
__can_merge = __sys_info->__can_merge ;
814
818
} else if (__can_merge && chrono::__merge_continuation (*__result, __sys_info->__info )) {
815
819
// 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.
817
822
__valid_result = __time >= __result->begin && __time < __result->end ;
818
823
__can_merge = __sys_info->__can_merge ;
819
824
} else {
@@ -843,7 +848,7 @@ time_zone::__get_info(sys_seconds __time) const {
843
848
if (__valid_result) {
844
849
return *__result;
845
850
} 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" );
847
852
const auto & __last = *(__it - 1 );
848
853
if (std::holds_alternative<string>(__last.__rules )) {
849
854
// Europe/Berlin
0 commit comments