File Coverage

src/panda/time/tzparse.cc
Criterion Covered Total %
statement 107 108 99.0
branch 95 124 76.6
condition n/a
subroutine n/a
pod n/a
total 202 232 87.0


line stmt bran cond sub pod time code
1             #include "tzparse.h"
2             #include "util.h"
3             #include
4             #include
5             #include
6             #include
7             #include
8              
9             namespace panda { namespace time {
10              
11             static const char FTZ_MAGIC[] = "TZif";
12             static const size_t FTZ_MAX_TIMES = 1200;
13             static const size_t FTZ_MAX_TYPES = 256;
14             static const size_t FTZ_MAX_CHARS = 50; /* Maximum number of abbreviation characters */
15             static const size_t FTZ_MAX_LEAPS = 50; /* Maximum number of leap second corrections */
16              
17             #pragma pack(push,1)
18              
19             struct ftz_head {
20             char tzh_magic[4]; /* TZ_MAGIC */
21             char tzh_version[1]; /* '\0' or '2' as of 2005 */
22             char tzh_reserved[15]; /* reserved--must be zero */
23             uint32_t tzh_ttisgmtcnt; /* coded number of trans. time flags */
24             uint32_t tzh_ttisstdcnt; /* coded number of trans. time flags */
25             uint32_t tzh_leapcnt; /* coded number of leap seconds */
26             uint32_t tzh_timecnt; /* coded number of transition times */
27             uint32_t tzh_typecnt; /* coded number of local time types */
28             uint32_t tzh_charcnt; /* coded number of abbr. chars */
29             };
30              
31             typedef int32_t ftz_transtimeV1;
32             typedef int64_t ftz_transtimeV2;
33             typedef uint8_t ftz_ilocaltype;
34             typedef uint8_t ftz_abbrev_offset;
35             typedef uint8_t ftz_isstd;
36             typedef uint8_t ftz_isgmt;
37              
38             struct ftz_localtype {
39             int32_t offset;
40             uint8_t isdst;
41             ftz_abbrev_offset abbrev_offset;
42             };
43              
44             struct ftz_leapsecV1 {
45             ftz_transtimeV1 time;
46             uint32_t correction;
47             };
48              
49             struct ftz_leapsecV2 {
50             ftz_transtimeV2 time;
51             uint32_t correction;
52             };
53              
54             #pragma pack(pop)
55              
56             /*
57             ** . . .followed by. . .
58             **
59             ** tzh_timecnt (char [4])s coded transition times a la time(2)
60             ** tzh_timecnt (unsigned char)s types of local time starting at above
61             ** tzh_typecnt repetitions of
62             ** one (char [4]) coded UTC offset in seconds
63             ** one (unsigned char) used to set tm_isdst
64             ** one (unsigned char) that's an abbreviation list index
65             ** tzh_charcnt (char)s '\0'-terminated zone abbreviations
66             ** tzh_leapcnt repetitions of
67             ** one (char [4]) coded leap second transition times
68             ** one (char [4]) total correction after above
69             ** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
70             ** time is standard time, if FALSE,
71             ** transition time is wall clock time
72             ** if absent, transition times are
73             ** assumed to be wall clock time
74             ** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
75             ** time is UTC, if FALSE,
76             ** transition time is local time
77             ** if absent, transition times are
78             ** assumed to be local time
79             */
80              
81             /*
82             ** If tzh_version is '2' or greater, the above is followed by a second instance
83             ** of tzhead and a second instance of the data in which each coded transition
84             ** time uses 8 rather than 4 chars,
85             ** then a POSIX-TZ-environment-variable-style string for use in handling
86             ** instants after the last transition time stored in the file
87             ** (with nothing between the newlines if there is no POSIX representation for
88             ** such instants).
89             */
90              
91             #undef PTIME_TZPARSE_V1
92             #undef PTIME_TZPARSE_V2
93             #define PTIME_TZPARSE_V1
94             #include
95             #undef PTIME_TZPARSE_V1
96             #define PTIME_TZPARSE_V2
97             #include
98              
99             enum class ParseResult { OK, ABSENT, ERROR };
100              
101             static ParseResult tzparse_rule_abbrev (const char* &str, char* dest);
102             static ParseResult tzparse_rule_time (const char* &str, int32_t* dest);
103             static ParseResult tzparse_rule_switch (const char* &str, Timezone::Rule::Zone::Switch* swtype, datetime* swdate);
104              
105 1251           bool tzparse (const string_view& content, Timezone* zone) {
106 1251           const char* ptr = content.data();
107 1251           const char* end = ptr + content.length();
108            
109             ftz_head head;
110             int version;
111 1251           int bodyV1_size = tzparse_headerV1(ptr, end, head, &version);
112 1251 50         if (bodyV1_size == -1) return false;
113            
114             bool result;
115 1251 50         if (version >= 2) {
116 1251           ptr += bodyV1_size;
117 1251 50         if (tzparse_headerV2(ptr, end, head, &version) == -1) return false;
118 1251 50         result = tzparse_bodyV2(ptr, end, head, zone);
119             } else {
120 0 0         result = tzparse_bodyV1(ptr, end, head, zone);
121             }
122              
123 1251           return result;
124             }
125              
126 1327           bool tzparse_rule (const string_view& sv, Timezone::Rule* rule) {
127 1327           char buf[sv.length()+1]; // null-terminate
128 1327           std::memcpy(buf, sv.data(), sv.length());
129 1327           buf[sv.length()] = 0;
130 1327           const char* rulestr = buf;
131              
132 1327 100         if (tzparse_rule_abbrev(rulestr, rule->outer.abbrev) != ParseResult::OK) return false;
133 1323 100         if (tzparse_rule_time(rulestr, &rule->outer.gmt_offset) != ParseResult::OK) return false;
134 1317           rule->outer.isdst = 0;
135              
136 1317           rule->hasdst = 0;
137             ParseResult result;
138 1317 100         if ((result = tzparse_rule_abbrev(rulestr, rule->inner.abbrev)) == ParseResult::ERROR) return false;
139 1316 100         if (result == ParseResult::ABSENT) return *rulestr == '\0';
140            
141 480 50         if ((result = tzparse_rule_time(rulestr, &rule->inner.gmt_offset)) == ParseResult::ERROR) return false;
142 480 100         if (result == ParseResult::ABSENT) rule->inner.gmt_offset = rule->outer.gmt_offset + 3600;
143            
144 480 100         if (*rulestr == ',') {
145 478           rulestr++;
146 478           rule->hasdst = 1;
147 478           rule->inner.isdst = 1;
148            
149 478 100         if (tzparse_rule_switch(rulestr, &rule->outer.type, &rule->outer.end) != ParseResult::OK) return false;
150 467 100         if (*rulestr != ',') return false;
151 466           rulestr++;
152 466 100         if (tzparse_rule_switch(rulestr, &rule->inner.type, &rule->inner.end) != ParseResult::OK) return false;
153            
154 463 100         if (rule->outer.type != Timezone::Rule::Zone::Switch::DATE || rule->inner.type != Timezone::Rule::Zone::Switch::DATE) {
    50          
155             //fprintf(stderr, "ptime: tz switch rules other than Mm.w.d (i.e. 'n' or 'Jn') are not supported (will consider no DST in this zone)\n");
156 4           rule->hasdst = 0;
157             }
158 459 100         else if (rule->outer.end.mon > rule->inner.end.mon) {
159 463           std::swap(rule->outer, rule->inner);
160             }
161             }
162            
163 1327           return *rulestr == '\0';
164             }
165              
166 2644           static ParseResult tzparse_rule_abbrev (const char*& str, char* dest) {
167 2644           const char* st = str;
168 2644           switch (*str) {
169 2           case ':': return ParseResult::ERROR;
170             case '<':
171 501           str++; st = str;
172 2082 50         while (*str && *str != '>') str++;
    100          
173 501 50         if (*str != '>') return ParseResult::ERROR;
174 501           break;
175             default:
176             char c;
177 6343 100         while ((c = *str) && !isdigit(c) && c != ',' && c != '+' && c != '-') str++;
    100          
    100          
    100          
    100          
    100          
178             }
179            
180 2642           size_t len = str - st;
181 2642 100         if (*str == '>') str++;
182              
183 2642 100         if (!len) return ParseResult::ABSENT;
184 1806 100         if (len < ZONE_ABBR_MIN) return ParseResult::ERROR;
185              
186 1803           strncpy(dest, st, len);
187 1803           dest[len] = '\0';
188            
189 1803           return ParseResult::OK;
190             }
191              
192 2142           static ParseResult tzparse_rule_time (const char*& str, int32_t* dest) {
193 2142           const char* st = str;
194 2142           *dest = - (int32_t) strtol(st, (char**)&str, 10) * 3600;
195 2142 100         if (str == st) return ParseResult::ABSENT;
196 1666 100         int sign = (*dest >= 0 ? 1 : -1);
197 1666 100         if (*str == ':') {
198 80           str++; st = str;
199 80           *dest += sign * (int32_t) strtol(st, (char**)&str, 10) * 60;
200 80 100         if (str == st) return ParseResult::ERROR;
201 77 100         if (*str == ':') {
202 16           str++; st = str;
203 16           *dest += sign * (int32_t) strtol(st, (char**)&str, 10);
204 16 100         if (str == st) return ParseResult::ERROR;
205             }
206             }
207              
208 1660           return ParseResult::OK;
209             }
210              
211 944           static ParseResult tzparse_rule_switch (const char*& str, Timezone::Rule::Zone::Switch* swtype, datetime* swdate) {
212 944           std::memset(swdate, 0, sizeof(*swdate));
213 944           const char* st = str;
214            
215 944 100         if (*str == 'M') {
216 934           str++; st = str;
217 934           *swtype = Timezone::Rule::Zone::Switch::DATE;
218 934           swdate->mon = (ptime_t) strtol(st, (char**)&str, 10) - 1;
219 934 50         if (st == str || swdate->mon < 0 || swdate->mon > 11 || *str != '.') return ParseResult::ERROR;
    50          
    50          
    50          
220 934           str++; st = str;
221 934           swdate->yday = (int32_t) strtol(st, (char**)&str, 10); // yday holds week number
222 934 50         if (st == str || swdate->yday < 1 || swdate->yday > 5 || *str != '.') return ParseResult::ERROR;
    100          
    100          
    50          
223 930           str++; st = str;
224 930           swdate->wday = (int32_t) strtol(st, (char**)&str, 10);
225 930 50         if (st == str || swdate->wday < 0 || swdate->wday > 6) return ParseResult::ERROR;
    100          
    100          
226             }
227 10 100         else if (*str == 'J') {
228 8           *swtype = Timezone::Rule::Zone::Switch::JDAY;
229 8           str++; st = str;
230 8           swdate->yday = (int32_t) strtol(st, (char**)&str, 10);
231 8 50         if (st == str || swdate->yday < 1 || swdate->yday > 365) return ParseResult::ERROR;
    50          
    50          
232             } else {
233 2           *swtype = Timezone::Rule::Zone::Switch::DAY;
234 2           swdate->yday = (int32_t) strtol(st, (char**)&str, 10);
235 2 50         if (st == str || swdate->yday < 0 || swdate->yday > 365) return ParseResult::ERROR;
    0          
    0          
236             }
237            
238 936 100         if (*str == '/') {
239 339           str++;
240             int32_t when;
241 339 100         if (tzparse_rule_time(str, &when) != ParseResult::OK) return ParseResult::ERROR;
242 333           when = -when; // revert reverse behaviour of parsing rule time
243 333 50         if (when < -604799 || when > 604799) return ParseResult::ERROR;
    50          
244 333 100         int sign = when >= 0 ? 1 : -1;
245 333           when *= sign;
246 333           swdate->hour = when / 3600;
247 333           when %= 3600;
248 333           swdate->min = when / 60;
249 333           swdate->sec = when % 60;
250 333           swdate->hour *= sign;
251 333           swdate->min *= sign;
252 333           swdate->sec *= sign;
253             } else {
254 597           swdate->hour = 2;
255 597           swdate->min = 0;
256 597           swdate->sec = 0;
257             }
258            
259 930           return ParseResult::OK;
260             }
261              
262 104 50         }};
    50