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