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
40namespace cctz {
41namespace detail {
42
43namespace {
44
45#if !HAS_STRPTIME
46// Build a strptime() using C++11's std::get_time().
47char* 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
56std::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
101const 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.
106char* 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.
134char* 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.
141char* 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).
155void 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().
172template <typename T>
173const 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).
225const int kDigits10_64 = 18;
226
227// 10^n for everything that can be represented by a signed 64-bit integer.
228const 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).
271std::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
447namespace {
448
449const 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
473const 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
482const 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).
508const 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.
530bool 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