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
2             #include
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 1240           bool tzparse (const string_view& content, Timezone* zone) {
106 1240           const char* ptr = content.data();
107 1240           const char* end = ptr + content.length();
108            
109             ftz_head head;
110             int version;
111 1240           int bodyV1_size = tzparse_headerV1(ptr, end, head, &version);
112 1240 50         if (bodyV1_size == -1) return false;
113            
114             bool result;
115 1240 50         if (version >= 2) {
116 1240           ptr += bodyV1_size;
117 1240 50         if (tzparse_headerV2(ptr, end, head, &version) == -1) return false;
118 1240 50         result = tzparse_bodyV2(ptr, end, head, zone);
119             } else {
120 0 0         result = tzparse_bodyV1(ptr, end, head, zone);
121             }
122              
123 1240           return result;
124             }
125              
126 1318           bool tzparse_rule (const string_view& sv, Timezone::Rule* rule) {
127 1318           char buf[sv.length()+1]; // null-terminate
128 1318           std::memcpy(buf, sv.data(), sv.length());
129 1318           buf[sv.length()] = 0;
130 1318           const char* rulestr = buf;
131              
132 1318 100         if (tzparse_rule_abbrev(rulestr, rule->outer.abbrev) != ParseResult::OK) return false;
133 1314 100         if (tzparse_rule_time(rulestr, &rule->outer.gmt_offset) != ParseResult::OK) return false;
134 1309           rule->outer.isdst = 0;
135              
136 1309           rule->hasdst = 0;
137             ParseResult result;
138 1309 100         if ((result = tzparse_rule_abbrev(rulestr, rule->inner.abbrev)) == ParseResult::ERROR) return false;
139 1308 100         if (result == ParseResult::ABSENT) return *rulestr == '\0';
140            
141 483 50         if ((result = tzparse_rule_time(rulestr, &rule->inner.gmt_offset)) == ParseResult::ERROR) return false;
142 483 100         if (result == ParseResult::ABSENT) rule->inner.gmt_offset = rule->outer.gmt_offset + 3600;
143            
144 483 100         if (*rulestr == ',') {
145 481           rulestr++;
146 481           rule->hasdst = 1;
147 481           rule->inner.isdst = 1;
148            
149 481 100         if (tzparse_rule_switch(rulestr, &rule->outer.type, &rule->outer.end) != ParseResult::OK) return false;
150 470 100         if (*rulestr != ',') return false;
151 469           rulestr++;
152 469 100         if (tzparse_rule_switch(rulestr, &rule->inner.type, &rule->inner.end) != ParseResult::OK) return false;
153            
154 466 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 462 100         else if (rule->outer.end.mon > rule->inner.end.mon) {
159 466           std::swap(rule->outer, rule->inner);
160             }
161             }
162            
163 1318           return *rulestr == '\0';
164             }
165              
166 2627           static ParseResult tzparse_rule_abbrev (const char*& str, char* dest) {
167 2627           const char* st = str;
168 2627           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 6311 100         while ((c = *str) && !isdigit(c) && c != ',' && c != '+' && c != '-') str++;
    100          
    100          
    100          
    100          
    100          
178             }
179            
180 2625           size_t len = str - st;
181 2625 100         if (*str == '>') str++;
182              
183 2625 100         if (!len) return ParseResult::ABSENT;
184 1800 100         if (len < ZONE_ABBR_MIN) return ParseResult::ERROR;
185              
186 1797           strncpy(dest, st, len);
187 1797           dest[len] = '\0';
188            
189 1797           return ParseResult::OK;
190             }
191              
192 2138           static ParseResult tzparse_rule_time (const char*& str, int32_t* dest) {
193 2138           const char* st = str;
194 2138           *dest = - (int32_t) strtol(st, (char**)&str, 10) * 3600;
195 2138 100         if (str == st) return ParseResult::ABSENT;
196 1656 100         int sign = (*dest >= 0 ? 1 : -1);
197 1656 100         if (*str == ':') {
198 82           str++; st = str;
199 82           *dest += sign * (int32_t) strtol(st, (char**)&str, 10) * 60;
200 82 100         if (str == st) return ParseResult::ERROR;
201 79 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 1650           return ParseResult::OK;
209             }
210              
211 950           static ParseResult tzparse_rule_switch (const char*& str, Timezone::Rule::Zone::Switch* swtype, datetime* swdate) {
212 950           std::memset(swdate, 0, sizeof(*swdate));
213 950           const char* st = str;
214            
215 950 100         if (*str == 'M') {
216 940           str++; st = str;
217 940           *swtype = Timezone::Rule::Zone::Switch::DATE;
218 940           swdate->mon = (ptime_t) strtol(st, (char**)&str, 10) - 1;
219 940 50         if (st == str || swdate->mon < 0 || swdate->mon > 11 || *str != '.') return ParseResult::ERROR;
    50          
    50          
    50          
220 940           str++; st = str;
221 940           swdate->yday = (int32_t) strtol(st, (char**)&str, 10); // yday holds week number
222 940 50         if (st == str || swdate->yday < 1 || swdate->yday > 5 || *str != '.') return ParseResult::ERROR;
    100          
    100          
    50          
223 936           str++; st = str;
224 936           swdate->wday = (int32_t) strtol(st, (char**)&str, 10);
225 936 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 942 100         if (*str == '/') {
239 341           str++;
240             int32_t when;
241 341 100         if (tzparse_rule_time(str, &when) != ParseResult::OK) return ParseResult::ERROR;
242 335           when = -when; // revert reverse behaviour of parsing rule time
243 335 50         if (when < -604799 || when > 604799) return ParseResult::ERROR;
    50          
244 335 100         int sign = when >= 0 ? 1 : -1;
245 335           when *= sign;
246 335           swdate->hour = when / 3600;
247 335           when %= 3600;
248 335           swdate->min = when / 60;
249 335           swdate->sec = when % 60;
250 335           swdate->hour *= sign;
251 335           swdate->min *= sign;
252 335           swdate->sec *= sign;
253             } else {
254 601           swdate->hour = 2;
255 601           swdate->min = 0;
256 601           swdate->sec = 0;
257             }
258            
259 936           return ParseResult::OK;
260             }
261              
262 72 50         }};
    50