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
|
|
|
|
|
|