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
@@ -348,7 +350,7 @@ class __named_rule_until {
348
350
// R HK 1946 o - Ap 21 0 1 S // (3)
349
351
// There (1) is active until Novemer 18th 1945 at 02:00, after this time
350
352
// (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.
352
354
// This entry has an offset.
353
355
// This entry has no save, letters, or dst flag. So in the period
354
356
// after (1) and until (3) no rule entry is associated with the time.
@@ -439,11 +441,11 @@ __next_rule(sys_seconds __time,
439
441
if (__y == __year && __it == __current)
440
442
continue ;
441
443
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);
443
445
if (__t <= __time)
444
446
continue ;
445
447
446
- _LIBCPP_ASSERT (!__candidates.contains (__t ), " duplicated rule" );
448
+ _LIBCPP_ASSERT_INTERNAL (!__candidates.contains (__t ), " duplicated rule" );
447
449
__candidates[__t ] = __it;
448
450
break ;
449
451
}
@@ -495,7 +497,7 @@ __first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) {
495
497
const vector<__tz::__rule>& __rules = __get_rules (__rule_name);
496
498
497
499
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" );
499
501
500
502
// Avoid selecting a time before the start of the continuation
501
503
__time = std::max (__time, __continuation_begin);
@@ -726,7 +728,7 @@ _LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
726
728
time_zone::__get_info (sys_seconds __time) const {
727
729
optional<sys_info> __result;
728
730
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.
730
732
bool __can_merge = false ;
731
733
sys_seconds __continuation_begin = sys_seconds::min ();
732
734
// Iterates over the Zone entry and its continuations. Internally the Zone
@@ -746,9 +748,9 @@ time_zone::__get_info(sys_seconds __time) const {
746
748
// no continuation is applicable it will return the end time as "error". When
747
749
// two continuations are contiguous and contain the "same" information these
748
750
// 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
752
754
// Continuations
753
755
// 0 s WE%sT 1929 (C1)
754
756
// 0 - WET 1967 (C2)
@@ -779,7 +781,7 @@ time_zone::__get_info(sys_seconds __time) const {
779
781
__sys_info_result __sys_info = chrono::__get_sys_info (__time, __continuation_begin, __continuation);
780
782
781
783
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" );
783
785
784
786
// Filters out dummy entries
785
787
// Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
@@ -813,7 +815,8 @@ time_zone::__get_info(sys_seconds __time) const {
813
815
__can_merge = __sys_info->__can_merge ;
814
816
} else if (__can_merge && chrono::__merge_continuation (*__result, __sys_info->__info )) {
815
817
// 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.
817
820
__valid_result = __time >= __result->begin && __time < __result->end ;
818
821
__can_merge = __sys_info->__can_merge ;
819
822
} else {
@@ -843,7 +846,7 @@ time_zone::__get_info(sys_seconds __time) const {
843
846
if (__valid_result) {
844
847
return *__result;
845
848
} 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" );
847
850
const auto & __last = *(__it - 1 );
848
851
if (std::holds_alternative<string>(__last.__rules )) {
849
852
// Europe/Berlin
0 commit comments