1 | // |
---|
2 | // DateTimeParser.cpp |
---|
3 | // |
---|
4 | // $Id: //poco/1.3/Foundation/src/DateTimeParser.cpp#7 $ |
---|
5 | // |
---|
6 | // Library: Foundation |
---|
7 | // Package: DateTime |
---|
8 | // Module: DateTimeParser |
---|
9 | // |
---|
10 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
---|
11 | // and Contributors. |
---|
12 | // |
---|
13 | // Permission is hereby granted, free of charge, to any person or organization |
---|
14 | // obtaining a copy of the software and accompanying documentation covered by |
---|
15 | // this license (the "Software") to use, reproduce, display, distribute, |
---|
16 | // execute, and transmit the Software, and to prepare derivative works of the |
---|
17 | // Software, and to permit third-parties to whom the Software is furnished to |
---|
18 | // do so, all subject to the following: |
---|
19 | // |
---|
20 | // The copyright notices in the Software and this entire statement, including |
---|
21 | // the above license grant, this restriction and the following disclaimer, |
---|
22 | // must be included in all copies of the Software, in whole or in part, and |
---|
23 | // all derivative works of the Software, unless such copies or derivative |
---|
24 | // works are solely in the form of machine-executable object code generated by |
---|
25 | // a source language processor. |
---|
26 | // |
---|
27 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
28 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
29 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
---|
30 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
---|
31 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
---|
32 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
33 | // DEALINGS IN THE SOFTWARE. |
---|
34 | // |
---|
35 | |
---|
36 | |
---|
37 | #include <Poco/DateTimeParser.h> |
---|
38 | #include <Poco/DateTimeFormat.h> |
---|
39 | #include <Poco/DateTime.h> |
---|
40 | #include <Poco/Exception.h> |
---|
41 | #include <cctype> |
---|
42 | |
---|
43 | |
---|
44 | namespace Poco { |
---|
45 | |
---|
46 | |
---|
47 | #define SKIP_JUNK() \ |
---|
48 | while (it != end && !std::isdigit(*it)) ++it |
---|
49 | |
---|
50 | |
---|
51 | #define PARSE_NUMBER(var) \ |
---|
52 | while (it != end && std::isdigit(*it)) var = var*10 + ((*it++) - '0') |
---|
53 | |
---|
54 | |
---|
55 | #define PARSE_NUMBER_N(var, n) \ |
---|
56 | { int i = 0; while (i++ < n && it != end && std::isdigit(*it)) var = var*10 + ((*it++) - '0'); } |
---|
57 | |
---|
58 | |
---|
59 | void DateTimeParser::parse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential) |
---|
60 | { |
---|
61 | int year = 0; |
---|
62 | int month = 0; |
---|
63 | int day = 0; |
---|
64 | int hour = 0; |
---|
65 | int minute = 0; |
---|
66 | int second = 0; |
---|
67 | int millis = 0; |
---|
68 | int micros = 0; |
---|
69 | int tzd = 0; |
---|
70 | |
---|
71 | std::string::const_iterator it = str.begin(); |
---|
72 | std::string::const_iterator end = str.end(); |
---|
73 | std::string::const_iterator itf = fmt.begin(); |
---|
74 | std::string::const_iterator endf = fmt.end(); |
---|
75 | |
---|
76 | while (itf != endf && it != end) |
---|
77 | { |
---|
78 | if (*itf == '%') |
---|
79 | { |
---|
80 | if (++itf != endf) |
---|
81 | { |
---|
82 | switch (*itf) |
---|
83 | { |
---|
84 | case 'w': |
---|
85 | case 'W': |
---|
86 | while (it != end && std::isspace(*it)) ++it; |
---|
87 | while (it != end && std::isalpha(*it)) ++it; |
---|
88 | break; |
---|
89 | case 'b': |
---|
90 | case 'B': |
---|
91 | month = parseMonth(it, end); |
---|
92 | break; |
---|
93 | case 'd': |
---|
94 | case 'e': |
---|
95 | case 'f': |
---|
96 | SKIP_JUNK(); |
---|
97 | PARSE_NUMBER_N(day, 2); |
---|
98 | break; |
---|
99 | case 'm': |
---|
100 | case 'n': |
---|
101 | case 'o': |
---|
102 | SKIP_JUNK(); |
---|
103 | PARSE_NUMBER_N(month, 2); |
---|
104 | break; |
---|
105 | case 'y': |
---|
106 | SKIP_JUNK(); |
---|
107 | PARSE_NUMBER_N(year, 2); |
---|
108 | if (year >= 69) |
---|
109 | year += 1900; |
---|
110 | else |
---|
111 | year += 2000; |
---|
112 | break; |
---|
113 | case 'Y': |
---|
114 | SKIP_JUNK(); |
---|
115 | PARSE_NUMBER_N(year, 4); |
---|
116 | break; |
---|
117 | case 'r': |
---|
118 | SKIP_JUNK(); |
---|
119 | PARSE_NUMBER(year); |
---|
120 | if (year < 1000) |
---|
121 | { |
---|
122 | if (year >= 69) |
---|
123 | year += 1900; |
---|
124 | else |
---|
125 | year += 2000; |
---|
126 | } |
---|
127 | break; |
---|
128 | case 'H': |
---|
129 | case 'h': |
---|
130 | SKIP_JUNK(); |
---|
131 | PARSE_NUMBER_N(hour, 2); |
---|
132 | break; |
---|
133 | case 'a': |
---|
134 | case 'A': |
---|
135 | hour = parseAMPM(it, end, hour); |
---|
136 | break; |
---|
137 | case 'M': |
---|
138 | SKIP_JUNK(); |
---|
139 | PARSE_NUMBER_N(minute, 2); |
---|
140 | break; |
---|
141 | case 'S': |
---|
142 | SKIP_JUNK(); |
---|
143 | PARSE_NUMBER_N(second, 2); |
---|
144 | break; |
---|
145 | case 'i': |
---|
146 | SKIP_JUNK(); |
---|
147 | PARSE_NUMBER_N(millis, 3); |
---|
148 | break; |
---|
149 | case 'c': |
---|
150 | SKIP_JUNK(); |
---|
151 | PARSE_NUMBER_N(millis, 1); |
---|
152 | millis *= 100; |
---|
153 | break; |
---|
154 | case 'F': |
---|
155 | SKIP_JUNK(); |
---|
156 | PARSE_NUMBER_N(millis, 3); |
---|
157 | PARSE_NUMBER_N(micros, 3); |
---|
158 | break; |
---|
159 | case 'z': |
---|
160 | case 'Z': |
---|
161 | tzd = parseTZD(it, end); |
---|
162 | break; |
---|
163 | } |
---|
164 | ++itf; |
---|
165 | } |
---|
166 | } |
---|
167 | else ++itf; |
---|
168 | } |
---|
169 | if (month == 0) month = 1; |
---|
170 | if (day == 0) day = 1; |
---|
171 | if (DateTime::isValid(year, month, day, hour, minute, second, millis, micros)) |
---|
172 | dateTime.assign(year, month, day, hour, minute, second, millis, micros); |
---|
173 | else |
---|
174 | throw SyntaxException("date/time component out of range"); |
---|
175 | timeZoneDifferential = tzd; |
---|
176 | } |
---|
177 | |
---|
178 | |
---|
179 | DateTime DateTimeParser::parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential) |
---|
180 | { |
---|
181 | DateTime result; |
---|
182 | parse(fmt, str, result, timeZoneDifferential); |
---|
183 | return result; |
---|
184 | } |
---|
185 | |
---|
186 | |
---|
187 | bool DateTimeParser::tryParse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential) |
---|
188 | { |
---|
189 | try |
---|
190 | { |
---|
191 | parse(fmt, str, dateTime, timeZoneDifferential); |
---|
192 | } |
---|
193 | catch (Exception&) |
---|
194 | { |
---|
195 | return false; |
---|
196 | } |
---|
197 | return true; |
---|
198 | } |
---|
199 | |
---|
200 | |
---|
201 | void DateTimeParser::parse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential) |
---|
202 | { |
---|
203 | if (!tryParse(str, dateTime, timeZoneDifferential)) |
---|
204 | throw SyntaxException("Unsupported or invalid date/time format"); |
---|
205 | } |
---|
206 | |
---|
207 | |
---|
208 | DateTime DateTimeParser::parse(const std::string& str, int& timeZoneDifferential) |
---|
209 | { |
---|
210 | DateTime result; |
---|
211 | if (tryParse(str, result, timeZoneDifferential)) |
---|
212 | return result; |
---|
213 | else |
---|
214 | throw SyntaxException("Unsupported or invalid date/time format"); |
---|
215 | } |
---|
216 | |
---|
217 | |
---|
218 | bool DateTimeParser::tryParse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential) |
---|
219 | { |
---|
220 | if (str.length() < 4) return false; |
---|
221 | |
---|
222 | if (str[3] == ',') |
---|
223 | return tryParse("%w, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential); |
---|
224 | else if (str[3] == ' ') |
---|
225 | return tryParse(DateTimeFormat::ASCTIME_FORMAT, str, dateTime, timeZoneDifferential); |
---|
226 | else if (str.find(',') != std::string::npos) |
---|
227 | return tryParse("%W, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential); |
---|
228 | else if (std::isdigit(str[0])) |
---|
229 | { |
---|
230 | if (str.find(' ') != std::string::npos || str.length() == 10) |
---|
231 | return tryParse(DateTimeFormat::SORTABLE_FORMAT, str, dateTime, timeZoneDifferential); |
---|
232 | else |
---|
233 | return tryParse(DateTimeFormat::ISO8601_FORMAT, str, dateTime, timeZoneDifferential); |
---|
234 | } |
---|
235 | else return false; |
---|
236 | } |
---|
237 | |
---|
238 | |
---|
239 | int DateTimeParser::parseTZD(std::string::const_iterator& it, const std::string::const_iterator& end) |
---|
240 | { |
---|
241 | struct Zone |
---|
242 | { |
---|
243 | const char* designator; |
---|
244 | int timeZoneDifferential; |
---|
245 | }; |
---|
246 | |
---|
247 | static Zone zones[] = |
---|
248 | { |
---|
249 | {"Z", 0}, |
---|
250 | {"UT", 0}, |
---|
251 | {"GMT", 0}, |
---|
252 | {"BST", 1*3600}, |
---|
253 | {"IST", 1*3600}, |
---|
254 | {"WET", 0}, |
---|
255 | {"WEST", 1*3600}, |
---|
256 | {"CET", 1*3600}, |
---|
257 | {"CEST", 2*3600}, |
---|
258 | {"EET", 2*3600}, |
---|
259 | {"EEST", 3*3600}, |
---|
260 | {"MSK", 3*3600}, |
---|
261 | {"MSD", 4*3600}, |
---|
262 | {"NST", -3*3600-1800}, |
---|
263 | {"NDT", -2*3600-1800}, |
---|
264 | {"AST", -4*3600}, |
---|
265 | {"ADT", -3*3600}, |
---|
266 | {"EST", -5*3600}, |
---|
267 | {"EDT", -4*3600}, |
---|
268 | {"CST", -6*3600}, |
---|
269 | {"CDT", -5*3600}, |
---|
270 | {"MST", -7*3600}, |
---|
271 | {"MDT", -6*3600}, |
---|
272 | {"PST", -8*3600}, |
---|
273 | {"PDT", -7*3600}, |
---|
274 | {"AKST", -9*3600}, |
---|
275 | {"AKDT", -8*3600}, |
---|
276 | {"HST", -10*3600}, |
---|
277 | {"AEST", 10*3600}, |
---|
278 | {"AEDT", 11*3600}, |
---|
279 | {"ACST", 9*3600+1800}, |
---|
280 | {"ACDT", 10*3600+1800}, |
---|
281 | {"AWST", 8*3600}, |
---|
282 | {"AWDT", 9*3600} |
---|
283 | }; |
---|
284 | |
---|
285 | while (it != end && std::isspace(*it)) ++it; |
---|
286 | if (it != end) |
---|
287 | { |
---|
288 | if (std::isalpha(*it)) |
---|
289 | { |
---|
290 | std::string designator; |
---|
291 | designator += *it++; |
---|
292 | if (it != end && std::isalpha(*it)) designator += *it++; |
---|
293 | if (it != end && std::isalpha(*it)) designator += *it++; |
---|
294 | if (it != end && std::isalpha(*it)) designator += *it++; |
---|
295 | for (unsigned i = 0; i < sizeof(zones)/sizeof(Zone); ++i) |
---|
296 | { |
---|
297 | if (designator == zones[i].designator) |
---|
298 | return zones[i].timeZoneDifferential; |
---|
299 | } |
---|
300 | } |
---|
301 | else if (*it == '+' || *it == '-') |
---|
302 | { |
---|
303 | int sign = *it == '+' ? 1 : -1; |
---|
304 | ++it; |
---|
305 | int hours = 0; |
---|
306 | PARSE_NUMBER_N(hours, 2); |
---|
307 | if (it != end && *it == ':') ++it; |
---|
308 | int minutes = 0; |
---|
309 | PARSE_NUMBER_N(minutes, 2); |
---|
310 | return sign*(hours*3600 + minutes*60); |
---|
311 | } |
---|
312 | } |
---|
313 | return 0; |
---|
314 | } |
---|
315 | |
---|
316 | |
---|
317 | int DateTimeParser::parseMonth(std::string::const_iterator& it, const std::string::const_iterator& end) |
---|
318 | { |
---|
319 | std::string month; |
---|
320 | while ((it != end && std::isspace(*it)) || std::ispunct(*it)) ++it; |
---|
321 | bool isFirst = true; |
---|
322 | while (it != end && std::isalpha(*it)) |
---|
323 | { |
---|
324 | char ch = (*it++); |
---|
325 | if (isFirst) { month += std::toupper(ch); isFirst = false; } |
---|
326 | else month += std::tolower(ch); |
---|
327 | } |
---|
328 | if (month.length() < 3) throw SyntaxException("Month name must be at least three characters long", month); |
---|
329 | for (int i = 0; i < 12; ++i) |
---|
330 | { |
---|
331 | if (DateTimeFormat::MONTH_NAMES[i].find(month) == 0) |
---|
332 | return i + 1; |
---|
333 | } |
---|
334 | throw SyntaxException("Not a valid month name", month); |
---|
335 | } |
---|
336 | |
---|
337 | |
---|
338 | int DateTimeParser::parseDayOfWeek(std::string::const_iterator& it, const std::string::const_iterator& end) |
---|
339 | { |
---|
340 | std::string dow; |
---|
341 | while ((it != end && std::isspace(*it)) || std::ispunct(*it)) ++it; |
---|
342 | bool isFirst = true; |
---|
343 | while (it != end && std::isalpha(*it)) |
---|
344 | { |
---|
345 | char ch = (*it++); |
---|
346 | if (isFirst) { dow += std::toupper(ch); isFirst = false; } |
---|
347 | else dow += std::tolower(ch); |
---|
348 | } |
---|
349 | if (dow.length() < 3) throw SyntaxException("Weekday name must be at least three characters long", dow); |
---|
350 | for (int i = 0; i < 7; ++i) |
---|
351 | { |
---|
352 | if (DateTimeFormat::WEEKDAY_NAMES[i].find(dow) == 0) |
---|
353 | return i; |
---|
354 | } |
---|
355 | throw SyntaxException("Not a valid weekday name", dow); |
---|
356 | } |
---|
357 | |
---|
358 | |
---|
359 | int DateTimeParser::parseAMPM(std::string::const_iterator& it, const std::string::const_iterator& end, int hour) |
---|
360 | { |
---|
361 | std::string ampm; |
---|
362 | while ((it != end && std::isspace(*it)) || std::ispunct(*it)) ++it; |
---|
363 | while (it != end && std::isalpha(*it)) |
---|
364 | { |
---|
365 | char ch = (*it++); |
---|
366 | ampm += std::toupper(ch); |
---|
367 | } |
---|
368 | if (ampm == "AM") |
---|
369 | { |
---|
370 | if (hour == 12) |
---|
371 | return 0; |
---|
372 | else |
---|
373 | return hour; |
---|
374 | } |
---|
375 | else if (ampm == "PM") |
---|
376 | { |
---|
377 | if (hour < 12) |
---|
378 | return hour + 12; |
---|
379 | else |
---|
380 | return hour; |
---|
381 | } |
---|
382 | else throw SyntaxException("Not a valid AM/PM designator", ampm); |
---|
383 | } |
---|
384 | |
---|
385 | |
---|
386 | } // namespace Poco |
---|