1 | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #if !defined(HAS_STRPTIME) |
16 | # if !defined(_MSC_VER) |
17 | # define HAS_STRPTIME 1 // assume everyone has strptime() except windows |
18 | # endif |
19 | #endif |
20 | |
21 | #include "cctz/time_zone.h" |
22 | |
23 | #include <cctype> |
24 | #include <chrono> |
25 | #include <cstddef> |
26 | #include <cstdint> |
27 | #include <cstring> |
28 | #include <ctime> |
29 | #include <limits> |
30 | #include <string> |
31 | #include <vector> |
32 | #if !HAS_STRPTIME |
33 | #include <iomanip> |
34 | #include <sstream> |
35 | #endif |
36 | |
37 | #include "cctz/civil_time.h" |
38 | #include "time_zone_if.h" |
39 | |
40 | namespace cctz { |
41 | namespace detail { |
42 | |
43 | namespace { |
44 | |
45 | #if !HAS_STRPTIME |
46 | // Build a strptime() using C++11's std::get_time(). |
47 | char* strptime(const char* s, const char* fmt, std::tm* tm) { |
48 | std::istringstream input(s); |
49 | input >> std::get_time(tm, fmt); |
50 | if (input.fail()) return nullptr; |
51 | return const_cast<char*>(s) + |
52 | (input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg())); |
53 | } |
54 | #endif |
55 | |
56 | std::tm ToTM(const time_zone::absolute_lookup& al) { |
57 | std::tm tm{}; |
58 | tm.tm_sec = al.cs.second(); |
59 | tm.tm_min = al.cs.minute(); |
60 | tm.tm_hour = al.cs.hour(); |
61 | tm.tm_mday = al.cs.day(); |
62 | tm.tm_mon = al.cs.month() - 1; |
63 | |
64 | // Saturate tm.tm_year is cases of over/underflow. |
65 | if (al.cs.year() < std::numeric_limits<int>::min() + 1900) { |
66 | tm.tm_year = std::numeric_limits<int>::min(); |
67 | } else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) { |
68 | tm.tm_year = std::numeric_limits<int>::max(); |
69 | } else { |
70 | tm.tm_year = static_cast<int>(al.cs.year() - 1900); |
71 | } |
72 | |
73 | switch (get_weekday(civil_day(al.cs))) { |
74 | case weekday::sunday: |
75 | tm.tm_wday = 0; |
76 | break; |
77 | case weekday::monday: |
78 | tm.tm_wday = 1; |
79 | break; |
80 | case weekday::tuesday: |
81 | tm.tm_wday = 2; |
82 | break; |
83 | case weekday::wednesday: |
84 | tm.tm_wday = 3; |
85 | break; |
86 | case weekday::thursday: |
87 | tm.tm_wday = 4; |
88 | break; |
89 | case weekday::friday: |
90 | tm.tm_wday = 5; |
91 | break; |
92 | case weekday::saturday: |
93 | tm.tm_wday = 6; |
94 | break; |
95 | } |
96 | tm.tm_yday = get_yearday(civil_day(al.cs)) - 1; |
97 | tm.tm_isdst = al.is_dst ? 1 : 0; |
98 | return tm; |
99 | } |
100 | |
101 | const char kDigits[] = "0123456789" ; |
102 | |
103 | // Formats a 64-bit integer in the given field width. Note that it is up |
104 | // to the caller of Format64() [and Format02d()/FormatOffset()] to ensure |
105 | // that there is sufficient space before ep to hold the conversion. |
106 | char* Format64(char* ep, int width, std::int_fast64_t v) { |
107 | bool neg = false; |
108 | if (v < 0) { |
109 | --width; |
110 | neg = true; |
111 | if (v == std::numeric_limits<std::int_fast64_t>::min()) { |
112 | // Avoid negating minimum value. |
113 | std::int_fast64_t last_digit = -(v % 10); |
114 | v /= 10; |
115 | if (last_digit < 0) { |
116 | ++v; |
117 | last_digit += 10; |
118 | } |
119 | --width; |
120 | *--ep = kDigits[last_digit]; |
121 | } |
122 | v = -v; |
123 | } |
124 | do { |
125 | --width; |
126 | *--ep = kDigits[v % 10]; |
127 | } while (v /= 10); |
128 | while (--width >= 0) *--ep = '0'; // zero pad |
129 | if (neg) *--ep = '-'; |
130 | return ep; |
131 | } |
132 | |
133 | // Formats [0 .. 99] as %02d. |
134 | char* Format02d(char* ep, int v) { |
135 | *--ep = kDigits[v % 10]; |
136 | *--ep = kDigits[(v / 10) % 10]; |
137 | return ep; |
138 | } |
139 | |
140 | // Formats a UTC offset, like +00:00. |
141 | char* FormatOffset(char* ep, int minutes, char sep) { |
142 | char sign = '+'; |
143 | if (minutes < 0) { |
144 | minutes = -minutes; |
145 | sign = '-'; |
146 | } |
147 | ep = Format02d(ep, minutes % 60); |
148 | if (sep != '\0') *--ep = sep; |
149 | ep = Format02d(ep, minutes / 60); |
150 | *--ep = sign; |
151 | return ep; |
152 | } |
153 | |
154 | // Formats a std::tm using strftime(3). |
155 | void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) { |
156 | // strftime(3) returns the number of characters placed in the output |
157 | // array (which may be 0 characters). It also returns 0 to indicate |
158 | // an error, like the array wasn't large enough. To accomodate this, |
159 | // the following code grows the buffer size from 2x the format string |
160 | // length up to 32x. |
161 | for (std::size_t i = 2; i != 32; i *= 2) { |
162 | std::size_t buf_size = fmt.size() * i; |
163 | std::vector<char> buf(buf_size); |
164 | if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) { |
165 | out->append(&buf[0], len); |
166 | return; |
167 | } |
168 | } |
169 | } |
170 | |
171 | // Used for %E#S/%E#f specifiers and for data values in parse(). |
172 | template <typename T> |
173 | const char* ParseInt(const char* dp, int width, T min, T max, T* vp) { |
174 | if (dp != nullptr) { |
175 | const T kmin = std::numeric_limits<T>::min(); |
176 | bool erange = false; |
177 | bool neg = false; |
178 | T value = 0; |
179 | if (*dp == '-') { |
180 | neg = true; |
181 | if (width <= 0 || --width != 0) { |
182 | ++dp; |
183 | } else { |
184 | dp = nullptr; // width was 1 |
185 | } |
186 | } |
187 | if (const char* const bp = dp) { |
188 | while (const char* cp = strchr(kDigits, *dp)) { |
189 | int d = static_cast<int>(cp - kDigits); |
190 | if (d >= 10) break; |
191 | if (value < kmin / 10) { |
192 | erange = true; |
193 | break; |
194 | } |
195 | value *= 10; |
196 | if (value < kmin + d) { |
197 | erange = true; |
198 | break; |
199 | } |
200 | value -= d; |
201 | dp += 1; |
202 | if (width > 0 && --width == 0) break; |
203 | } |
204 | if (dp != bp && !erange && (neg || value != kmin)) { |
205 | if (!neg || value != 0) { |
206 | if (!neg) value = -value; // make positive |
207 | if (min <= value && value <= max) { |
208 | *vp = value; |
209 | } else { |
210 | dp = nullptr; |
211 | } |
212 | } else { |
213 | dp = nullptr; |
214 | } |
215 | } else { |
216 | dp = nullptr; |
217 | } |
218 | } |
219 | } |
220 | return dp; |
221 | } |
222 | |
223 | // The number of base-10 digits that can be represented by a signed 64-bit |
224 | // integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1). |
225 | const int kDigits10_64 = 18; |
226 | |
227 | // 10^n for everything that can be represented by a signed 64-bit integer. |
228 | const std::int_fast64_t kExp10[kDigits10_64 + 1] = { |
229 | 1, |
230 | 10, |
231 | 100, |
232 | 1000, |
233 | 10000, |
234 | 100000, |
235 | 1000000, |
236 | 10000000, |
237 | 100000000, |
238 | 1000000000, |
239 | 10000000000, |
240 | 100000000000, |
241 | 1000000000000, |
242 | 10000000000000, |
243 | 100000000000000, |
244 | 1000000000000000, |
245 | 10000000000000000, |
246 | 100000000000000000, |
247 | 1000000000000000000, |
248 | }; |
249 | |
250 | } // namespace |
251 | |
252 | // Uses strftime(3) to format the given Time. The following extended format |
253 | // specifiers are also supported: |
254 | // |
255 | // - %Ez - RFC3339-compatible numeric timezone (+hh:mm or -hh:mm) |
256 | // - %E#S - Seconds with # digits of fractional precision |
257 | // - %E*S - Seconds with full fractional precision (a literal '*') |
258 | // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) |
259 | // |
260 | // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are |
261 | // handled internally for performance reasons. strftime(3) is slow due to |
262 | // a POSIX requirement to respect changes to ${TZ}. |
263 | // |
264 | // The TZ/GNU %s extension is handled internally because strftime() has |
265 | // to use mktime() to generate it, and that assumes the local time zone. |
266 | // |
267 | // We also handle the %z and %Z specifiers to accommodate platforms that do |
268 | // not support the tm_gmtoff and tm_zone extensions to std::tm. |
269 | // |
270 | // Requires that zero() <= fs < seconds(1). |
271 | std::string format(const std::string& format, const time_point<sys_seconds>& tp, |
272 | const detail::femtoseconds& fs, const time_zone& tz) { |
273 | std::string result; |
274 | result.reserve(format.size()); // A reasonable guess for the result size. |
275 | const time_zone::absolute_lookup al = tz.lookup(tp); |
276 | const std::tm tm = ToTM(al); |
277 | |
278 | // Scratch buffer for internal conversions. |
279 | char buf[3 + kDigits10_64]; // enough for longest conversion |
280 | char* const ep = buf + sizeof(buf); |
281 | char* bp; // works back from ep |
282 | |
283 | // Maintain three, disjoint subsequences that span format. |
284 | // [format.begin() ... pending) : already formatted into result |
285 | // [pending ... cur) : formatting pending, but no special cases |
286 | // [cur ... format.end()) : unexamined |
287 | // Initially, everything is in the unexamined part. |
288 | const char* pending = format.c_str(); // NUL terminated |
289 | const char* cur = pending; |
290 | const char* end = pending + format.length(); |
291 | |
292 | while (cur != end) { // while something is unexamined |
293 | // Moves cur to the next percent sign. |
294 | const char* start = cur; |
295 | while (cur != end && *cur != '%') ++cur; |
296 | |
297 | // If the new pending text is all ordinary, copy it out. |
298 | if (cur != start && pending == start) { |
299 | result.append(pending, static_cast<std::size_t>(cur - pending)); |
300 | pending = start = cur; |
301 | } |
302 | |
303 | // Span the sequential percent signs. |
304 | const char* percent = cur; |
305 | while (cur != end && *cur == '%') ++cur; |
306 | |
307 | // If the new pending text is all percents, copy out one |
308 | // percent for every matched pair, then skip those pairs. |
309 | if (cur != start && pending == start) { |
310 | std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2; |
311 | result.append(pending, escaped); |
312 | pending += escaped * 2; |
313 | // Also copy out a single trailing percent. |
314 | if (pending != cur && cur == end) { |
315 | result.push_back(*pending++); |
316 | } |
317 | } |
318 | |
319 | // Loop unless we have an unescaped percent. |
320 | if (cur == end || (cur - percent) % 2 == 0) continue; |
321 | |
322 | // Simple specifiers that we handle ourselves. |
323 | if (strchr("YmdeHMSzZs%" , *cur)) { |
324 | if (cur - 1 != pending) { |
325 | FormatTM(&result, std::string(pending, cur - 1), tm); |
326 | } |
327 | switch (*cur) { |
328 | case 'Y': |
329 | // This avoids the tm.tm_year overflow problem for %Y, however |
330 | // tm.tm_year will still be used by other specifiers like %D. |
331 | bp = Format64(ep, 0, al.cs.year()); |
332 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
333 | break; |
334 | case 'm': |
335 | bp = Format02d(ep, al.cs.month()); |
336 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
337 | break; |
338 | case 'd': |
339 | case 'e': |
340 | bp = Format02d(ep, al.cs.day()); |
341 | if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows |
342 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
343 | break; |
344 | case 'H': |
345 | bp = Format02d(ep, al.cs.hour()); |
346 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
347 | break; |
348 | case 'M': |
349 | bp = Format02d(ep, al.cs.minute()); |
350 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
351 | break; |
352 | case 'S': |
353 | bp = Format02d(ep, al.cs.second()); |
354 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
355 | break; |
356 | case 'z': |
357 | bp = FormatOffset(ep, al.offset / 60, '\0'); |
358 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
359 | break; |
360 | case 'Z': |
361 | result.append(al.abbr); |
362 | break; |
363 | case 's': |
364 | bp = Format64(ep, 0, ToUnixSeconds(tp)); |
365 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
366 | break; |
367 | case '%': |
368 | result.push_back('%'); |
369 | break; |
370 | } |
371 | pending = ++cur; |
372 | continue; |
373 | } |
374 | |
375 | // Loop if there is no E modifier. |
376 | if (*cur != 'E' || ++cur == end) continue; |
377 | |
378 | // Format our extensions. |
379 | if (*cur == 'z') { |
380 | // Formats %Ez. |
381 | if (cur - 2 != pending) { |
382 | FormatTM(&result, std::string(pending, cur - 2), tm); |
383 | } |
384 | bp = FormatOffset(ep, al.offset / 60, ':'); |
385 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
386 | pending = ++cur; |
387 | } else if (*cur == '*' && cur + 1 != end && |
388 | (*(cur + 1) == 'S' || *(cur + 1) == 'f')) { |
389 | // Formats %E*S or %E*F. |
390 | if (cur - 2 != pending) { |
391 | FormatTM(&result, std::string(pending, cur - 2), tm); |
392 | } |
393 | char* cp = ep; |
394 | bp = Format64(cp, 15, fs.count()); |
395 | while (cp != bp && cp[-1] == '0') --cp; |
396 | switch (*(cur + 1)) { |
397 | case 'S': |
398 | if (cp != bp) *--bp = '.'; |
399 | bp = Format02d(bp, al.cs.second()); |
400 | break; |
401 | case 'f': |
402 | if (cp == bp) *--bp = '0'; |
403 | break; |
404 | } |
405 | result.append(bp, static_cast<std::size_t>(cp - bp)); |
406 | pending = cur += 2; |
407 | } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') { |
408 | // Formats %E4Y. |
409 | if (cur - 2 != pending) { |
410 | FormatTM(&result, std::string(pending, cur - 2), tm); |
411 | } |
412 | bp = Format64(ep, 4, al.cs.year()); |
413 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
414 | pending = cur += 2; |
415 | } else if (std::isdigit(*cur)) { |
416 | // Possibly found %E#S or %E#f. |
417 | int n = 0; |
418 | if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) { |
419 | if (*np == 'S' || *np == 'f') { |
420 | // Formats %E#S or %E#f. |
421 | if (cur - 2 != pending) { |
422 | FormatTM(&result, std::string(pending, cur - 2), tm); |
423 | } |
424 | bp = ep; |
425 | if (n > 0) { |
426 | if (n > kDigits10_64) n = kDigits10_64; |
427 | bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15] |
428 | : fs.count() / kExp10[15 - n]); |
429 | if (*np == 'S') *--bp = '.'; |
430 | } |
431 | if (*np == 'S') bp = Format02d(bp, al.cs.second()); |
432 | result.append(bp, static_cast<std::size_t>(ep - bp)); |
433 | pending = cur = ++np; |
434 | } |
435 | } |
436 | } |
437 | } |
438 | |
439 | // Formats any remaining data. |
440 | if (end != pending) { |
441 | FormatTM(&result, std::string(pending, end), tm); |
442 | } |
443 | |
444 | return result; |
445 | } |
446 | |
447 | namespace { |
448 | |
449 | const char* ParseOffset(const char* dp, char sep, int* offset) { |
450 | if (dp != nullptr) { |
451 | const char sign = *dp++; |
452 | if (sign == '+' || sign == '-') { |
453 | int hours = 0; |
454 | const char* ap = ParseInt(dp, 2, 0, 23, &hours); |
455 | if (ap != nullptr && ap - dp == 2) { |
456 | dp = ap; |
457 | if (sep != '\0' && *ap == sep) ++ap; |
458 | int minutes = 0; |
459 | const char* bp = ParseInt(ap, 2, 0, 59, &minutes); |
460 | if (bp != nullptr && bp - ap == 2) dp = bp; |
461 | *offset = (hours * 60 + minutes) * 60; |
462 | if (sign == '-') *offset = -*offset; |
463 | } else { |
464 | dp = nullptr; |
465 | } |
466 | } else { |
467 | dp = nullptr; |
468 | } |
469 | } |
470 | return dp; |
471 | } |
472 | |
473 | const char* ParseZone(const char* dp, std::string* zone) { |
474 | zone->clear(); |
475 | if (dp != nullptr) { |
476 | while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++); |
477 | if (zone->empty()) dp = nullptr; |
478 | } |
479 | return dp; |
480 | } |
481 | |
482 | const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) { |
483 | if (dp != nullptr) { |
484 | std::int_fast64_t v = 0; |
485 | std::int_fast64_t exp = 0; |
486 | const char* const bp = dp; |
487 | while (const char* cp = strchr(kDigits, *dp)) { |
488 | int d = static_cast<int>(cp - kDigits); |
489 | if (d >= 10) break; |
490 | if (exp < 15) { |
491 | exp += 1; |
492 | v *= 10; |
493 | v += d; |
494 | } |
495 | ++dp; |
496 | } |
497 | if (dp != bp) { |
498 | v *= kExp10[15 - exp]; |
499 | *subseconds = detail::femtoseconds(v); |
500 | } else { |
501 | dp = nullptr; |
502 | } |
503 | } |
504 | return dp; |
505 | } |
506 | |
507 | // Parses a string into a std::tm using strptime(3). |
508 | const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { |
509 | if (dp != nullptr) { |
510 | dp = strptime(dp, fmt, tm); |
511 | } |
512 | return dp; |
513 | } |
514 | |
515 | } // namespace |
516 | |
517 | // Uses strptime(3) to parse the given input. Supports the same extended |
518 | // format specifiers as format(), although %E#S and %E*S are treated |
519 | // identically (and similarly for %E#f and %E*f). |
520 | // |
521 | // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are |
522 | // handled internally so that we can normally avoid strptime() altogether |
523 | // (which is particularly helpful when the native implementation is broken). |
524 | // |
525 | // The TZ/GNU %s extension is handled internally because strptime() has to |
526 | // use localtime_r() to generate it, and that assumes the local time zone. |
527 | // |
528 | // We also handle the %z specifier to accommodate platforms that do not |
529 | // support the tm_gmtoff extension to std::tm. %Z is parsed but ignored. |
530 | bool parse(const std::string& format, const std::string& input, |
531 | const time_zone& tz, time_point<sys_seconds>* sec, |
532 | detail::femtoseconds* fs, std::string* err) { |
533 | // The unparsed input. |
534 | const char* data = input.c_str(); // NUL terminated |
535 | |
536 | // Skips leading whitespace. |
537 | while (std::isspace(*data)) ++data; |
538 | |
539 | const year_t kyearmax = std::numeric_limits<year_t>::max(); |
540 | const year_t kyearmin = std::numeric_limits<year_t>::min(); |
541 | |
542 | // Sets default values for unspecified fields. |
543 | bool saw_year = false; |
544 | year_t year = 1970; |
545 | std::tm tm{}; |
546 | tm.tm_year = 1970 - 1900; |
547 | tm.tm_mon = 1 - 1; // Jan |
548 | tm.tm_mday = 1; |
549 | tm.tm_hour = 0; |
550 | tm.tm_min = 0; |
551 | tm.tm_sec = 0; |
552 | tm.tm_wday = 4; // Thu |
553 | tm.tm_yday = 0; |
554 | tm.tm_isdst = 0; |
555 | auto subseconds = detail::femtoseconds::zero(); |
556 | bool saw_offset = false; |
557 | int offset = 0; // No offset from passed tz. |
558 | std::string zone = "UTC" ; |
559 | |
560 | const char* fmt = format.c_str(); // NUL terminated |
561 | bool twelve_hour = false; |
562 | bool afternoon = false; |
563 | |
564 | bool saw_percent_s = false; |
565 | std::int_fast64_t percent_s = 0; |
566 | |
567 | // Steps through format, one specifier at a time. |
568 | while (data != nullptr && *fmt != '\0') { |
569 | if (std::isspace(*fmt)) { |
570 | while (std::isspace(*data)) ++data; |
571 | while (std::isspace(*++fmt)) continue; |
572 | continue; |
573 | } |
574 | |
575 | if (*fmt != '%') { |
576 | if (*data == *fmt) { |
577 | ++data; |
578 | ++fmt; |
579 | } else { |
580 | data = nullptr; |
581 | } |
582 | continue; |
583 | } |
584 | |
585 | const char* percent = fmt; |
586 | if (*++fmt == '\0') { |
587 | data = nullptr; |
588 | continue; |
589 | } |
590 | switch (*fmt++) { |
591 | case 'Y': |
592 | // Symmetrically with FormatTime(), directly handing %Y avoids the |
593 | // tm.tm_year overflow problem. However, tm.tm_year will still be |
594 | // used by other specifiers like %D. |
595 | data = ParseInt(data, 0, kyearmin, kyearmax, &year); |
596 | if (data != nullptr) saw_year = true; |
597 | continue; |
598 | case 'm': |
599 | data = ParseInt(data, 2, 1, 12, &tm.tm_mon); |
600 | if (data != nullptr) tm.tm_mon -= 1; |
601 | continue; |
602 | case 'd': |
603 | case 'e': |
604 | data = ParseInt(data, 2, 1, 31, &tm.tm_mday); |
605 | continue; |
606 | case 'H': |
607 | data = ParseInt(data, 2, 0, 23, &tm.tm_hour); |
608 | twelve_hour = false; |
609 | continue; |
610 | case 'M': |
611 | data = ParseInt(data, 2, 0, 59, &tm.tm_min); |
612 | continue; |
613 | case 'S': |
614 | data = ParseInt(data, 2, 0, 60, &tm.tm_sec); |
615 | continue; |
616 | case 'I': |
617 | case 'l': |
618 | case 'r': // probably uses %I |
619 | twelve_hour = true; |
620 | break; |
621 | case 'R': // uses %H |
622 | case 'T': // uses %H |
623 | case 'c': // probably uses %H |
624 | case 'X': // probably uses %H |
625 | twelve_hour = false; |
626 | break; |
627 | case 'z': |
628 | data = ParseOffset(data, '\0', &offset); |
629 | if (data != nullptr) saw_offset = true; |
630 | continue; |
631 | case 'Z': // ignored; zone abbreviations are ambiguous |
632 | data = ParseZone(data, &zone); |
633 | continue; |
634 | case 's': |
635 | data = ParseInt(data, 0, |
636 | std::numeric_limits<std::int_fast64_t>::min(), |
637 | std::numeric_limits<std::int_fast64_t>::max(), |
638 | &percent_s); |
639 | if (data != nullptr) saw_percent_s = true; |
640 | continue; |
641 | case '%': |
642 | data = (*data == '%' ? data + 1 : nullptr); |
643 | continue; |
644 | case 'E': |
645 | if (*fmt == 'z') { |
646 | if (data != nullptr && *data == 'Z') { // Zulu |
647 | offset = 0; |
648 | data += 1; |
649 | } else { |
650 | data = ParseOffset(data, ':', &offset); |
651 | } |
652 | if (data != nullptr) saw_offset = true; |
653 | fmt += 1; |
654 | continue; |
655 | } |
656 | if (*fmt == '*' && *(fmt + 1) == 'S') { |
657 | data = ParseInt(data, 2, 0, 60, &tm.tm_sec); |
658 | if (data != nullptr && *data == '.') { |
659 | data = ParseSubSeconds(data + 1, &subseconds); |
660 | } |
661 | fmt += 2; |
662 | continue; |
663 | } |
664 | if (*fmt == '*' && *(fmt + 1) == 'f') { |
665 | if (data != nullptr && std::isdigit(*data)) { |
666 | data = ParseSubSeconds(data, &subseconds); |
667 | } |
668 | fmt += 2; |
669 | continue; |
670 | } |
671 | if (*fmt == '4' && *(fmt + 1) == 'Y') { |
672 | const char* bp = data; |
673 | data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year); |
674 | if (data != nullptr) { |
675 | if (data - bp == 4) { |
676 | saw_year = true; |
677 | } else { |
678 | data = nullptr; // stopped too soon |
679 | } |
680 | } |
681 | fmt += 2; |
682 | continue; |
683 | } |
684 | if (std::isdigit(*fmt)) { |
685 | int n = 0; // value ignored |
686 | if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) { |
687 | if (*np == 'S') { |
688 | data = ParseInt(data, 2, 0, 60, &tm.tm_sec); |
689 | if (data != nullptr && *data == '.') { |
690 | data = ParseSubSeconds(data + 1, &subseconds); |
691 | } |
692 | fmt = ++np; |
693 | continue; |
694 | } |
695 | if (*np == 'f') { |
696 | if (data != nullptr && std::isdigit(*data)) { |
697 | data = ParseSubSeconds(data, &subseconds); |
698 | } |
699 | fmt = ++np; |
700 | continue; |
701 | } |
702 | } |
703 | } |
704 | if (*fmt == 'c') twelve_hour = false; // probably uses %H |
705 | if (*fmt == 'X') twelve_hour = false; // probably uses %H |
706 | if (*fmt != '\0') ++fmt; |
707 | break; |
708 | case 'O': |
709 | if (*fmt == 'H') twelve_hour = false; |
710 | if (*fmt == 'I') twelve_hour = true; |
711 | if (*fmt != '\0') ++fmt; |
712 | break; |
713 | } |
714 | |
715 | // Parses the current specifier. |
716 | const char* orig_data = data; |
717 | std::string spec(percent, static_cast<std::size_t>(fmt - percent)); |
718 | data = ParseTM(data, spec.c_str(), &tm); |
719 | |
720 | // If we successfully parsed %p we need to remember whether the result |
721 | // was AM or PM so that we can adjust tm_hour before ConvertDateTime(). |
722 | // So reparse the input with a known AM hour, and check if it is shifted |
723 | // to a PM hour. |
724 | if (spec == "%p" && data != nullptr) { |
725 | std::string test_input = "1" ; |
726 | test_input.append(orig_data, static_cast<std::size_t>(data - orig_data)); |
727 | const char* test_data = test_input.c_str(); |
728 | std::tm tmp{}; |
729 | ParseTM(test_data, "%I%p" , &tmp); |
730 | afternoon = (tmp.tm_hour == 13); |
731 | } |
732 | } |
733 | |
734 | // Adjust a 12-hour tm_hour value if it should be in the afternoon. |
735 | if (twelve_hour && afternoon && tm.tm_hour < 12) { |
736 | tm.tm_hour += 12; |
737 | } |
738 | |
739 | if (data == nullptr) { |
740 | if (err != nullptr) *err = "Failed to parse input" ; |
741 | return false; |
742 | } |
743 | |
744 | // Skip any remaining whitespace. |
745 | while (std::isspace(*data)) ++data; |
746 | |
747 | // parse() must consume the entire input string. |
748 | if (*data != '\0') { |
749 | if (err != nullptr) *err = "Illegal trailing data in input string" ; |
750 | return false; |
751 | } |
752 | |
753 | // If we saw %s then we ignore anything else and return that time. |
754 | if (saw_percent_s) { |
755 | *sec = FromUnixSeconds(percent_s); |
756 | *fs = detail::femtoseconds::zero(); |
757 | return true; |
758 | } |
759 | |
760 | // If we saw %z or %Ez then we want to interpret the parsed fields in |
761 | // UTC and then shift by that offset. Otherwise we want to interpret |
762 | // the fields directly in the passed time_zone. |
763 | time_zone ptz = saw_offset ? utc_time_zone() : tz; |
764 | |
765 | // Allows a leap second of 60 to normalize forward to the following ":00". |
766 | if (tm.tm_sec == 60) { |
767 | tm.tm_sec -= 1; |
768 | offset -= 1; |
769 | subseconds = detail::femtoseconds::zero(); |
770 | } |
771 | |
772 | if (!saw_year) { |
773 | year = year_t{tm.tm_year}; |
774 | if (year > kyearmax - 1900) { |
775 | // Platform-dependent, maybe unreachable. |
776 | if (err != nullptr) *err = "Out-of-range year" ; |
777 | return false; |
778 | } |
779 | year += 1900; |
780 | } |
781 | |
782 | const int month = tm.tm_mon + 1; |
783 | civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
784 | |
785 | // parse() should not allow normalization. Due to the restricted field |
786 | // ranges above (see ParseInt()), the only possibility is for days to roll |
787 | // into months. That is, parsing "Sep 31" should not produce "Oct 1". |
788 | if (cs.month() != month || cs.day() != tm.tm_mday) { |
789 | if (err != nullptr) *err = "Out-of-range field" ; |
790 | return false; |
791 | } |
792 | |
793 | // Accounts for the offset adjustment before converting to absolute time. |
794 | if ((offset < 0 && cs > civil_second::max() + offset) || |
795 | (offset > 0 && cs < civil_second::min() + offset)) { |
796 | if (err != nullptr) *err = "Out-of-range field" ; |
797 | return false; |
798 | } |
799 | cs -= offset; |
800 | |
801 | const auto tp = ptz.lookup(cs).pre; |
802 | // Checks for overflow/underflow and returns an error as necessary. |
803 | if (tp == time_point<sys_seconds>::max()) { |
804 | const auto al = ptz.lookup(time_point<sys_seconds>::max()); |
805 | if (cs > al.cs) { |
806 | if (err != nullptr) *err = "Out-of-range field" ; |
807 | return false; |
808 | } |
809 | } |
810 | if (tp == time_point<sys_seconds>::min()) { |
811 | const auto al = ptz.lookup(time_point<sys_seconds>::min()); |
812 | if (cs < al.cs) { |
813 | if (err != nullptr) *err = "Out-of-range field" ; |
814 | return false; |
815 | } |
816 | } |
817 | |
818 | *sec = tp; |
819 | *fs = subseconds; |
820 | return true; |
821 | } |
822 | |
823 | } // namespace detail |
824 | } // namespace cctz |
825 | |