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