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


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:20