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