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