time_zone_format.cc
Go to the documentation of this file.
00001 // Copyright 2016 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //   https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 //   Unless required by applicable law or agreed to in writing, software
00010 //   distributed under the License is distributed on an "AS IS" BASIS,
00011 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 //   See the License for the specific language governing permissions and
00013 //   limitations under the License.
00014 
00015 #if !defined(HAS_STRPTIME)
00016 # if !defined(_MSC_VER) && !defined(__MINGW32__)
00017 #  define HAS_STRPTIME 1  // assume everyone has strptime() except windows
00018 # endif
00019 #endif
00020 
00021 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
00022 
00023 #include <cctype>
00024 #include <chrono>
00025 #include <cstddef>
00026 #include <cstdint>
00027 #include <cstring>
00028 #include <ctime>
00029 #include <limits>
00030 #include <string>
00031 #include <vector>
00032 #if !HAS_STRPTIME
00033 #include <iomanip>
00034 #include <sstream>
00035 #endif
00036 
00037 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
00038 #include "time_zone_if.h"
00039 
00040 namespace absl {
00041 namespace time_internal {
00042 namespace cctz {
00043 namespace detail {
00044 
00045 namespace {
00046 
00047 #if !HAS_STRPTIME
00048 // Build a strptime() using C++11's std::get_time().
00049 char* strptime(const char* s, const char* fmt, std::tm* tm) {
00050   std::istringstream input(s);
00051   input >> std::get_time(tm, fmt);
00052   if (input.fail()) return nullptr;
00053   return const_cast<char*>(s) +
00054          (input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg()));
00055 }
00056 #endif
00057 
00058 std::tm ToTM(const time_zone::absolute_lookup& al) {
00059   std::tm tm{};
00060   tm.tm_sec = al.cs.second();
00061   tm.tm_min = al.cs.minute();
00062   tm.tm_hour = al.cs.hour();
00063   tm.tm_mday = al.cs.day();
00064   tm.tm_mon = al.cs.month() - 1;
00065 
00066   // Saturate tm.tm_year is cases of over/underflow.
00067   if (al.cs.year() < std::numeric_limits<int>::min() + 1900) {
00068     tm.tm_year = std::numeric_limits<int>::min();
00069   } else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) {
00070     tm.tm_year = std::numeric_limits<int>::max();
00071   } else {
00072     tm.tm_year = static_cast<int>(al.cs.year() - 1900);
00073   }
00074 
00075   switch (get_weekday(civil_day(al.cs))) {
00076     case weekday::sunday:
00077       tm.tm_wday = 0;
00078       break;
00079     case weekday::monday:
00080       tm.tm_wday = 1;
00081       break;
00082     case weekday::tuesday:
00083       tm.tm_wday = 2;
00084       break;
00085     case weekday::wednesday:
00086       tm.tm_wday = 3;
00087       break;
00088     case weekday::thursday:
00089       tm.tm_wday = 4;
00090       break;
00091     case weekday::friday:
00092       tm.tm_wday = 5;
00093       break;
00094     case weekday::saturday:
00095       tm.tm_wday = 6;
00096       break;
00097   }
00098   tm.tm_yday = get_yearday(civil_day(al.cs)) - 1;
00099   tm.tm_isdst = al.is_dst ? 1 : 0;
00100   return tm;
00101 }
00102 
00103 const char kDigits[] = "0123456789";
00104 
00105 // Formats a 64-bit integer in the given field width.  Note that it is up
00106 // to the caller of Format64() [and Format02d()/FormatOffset()] to ensure
00107 // that there is sufficient space before ep to hold the conversion.
00108 char* Format64(char* ep, int width, std::int_fast64_t v) {
00109   bool neg = false;
00110   if (v < 0) {
00111     --width;
00112     neg = true;
00113     if (v == std::numeric_limits<std::int_fast64_t>::min()) {
00114       // Avoid negating minimum value.
00115       std::int_fast64_t last_digit = -(v % 10);
00116       v /= 10;
00117       if (last_digit < 0) {
00118         ++v;
00119         last_digit += 10;
00120       }
00121       --width;
00122       *--ep = kDigits[last_digit];
00123     }
00124     v = -v;
00125   }
00126   do {
00127     --width;
00128     *--ep = kDigits[v % 10];
00129   } while (v /= 10);
00130   while (--width >= 0) *--ep = '0';  // zero pad
00131   if (neg) *--ep = '-';
00132   return ep;
00133 }
00134 
00135 // Formats [0 .. 99] as %02d.
00136 char* Format02d(char* ep, int v) {
00137   *--ep = kDigits[v % 10];
00138   *--ep = kDigits[(v / 10) % 10];
00139   return ep;
00140 }
00141 
00142 // Formats a UTC offset, like +00:00.
00143 char* FormatOffset(char* ep, int offset, const char* mode) {
00144   // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
00145   // generate a "negative zero" when we're formatting a zero offset
00146   // as the result of a failed load_time_zone().
00147   char sign = '+';
00148   if (offset < 0) {
00149     offset = -offset;  // bounded by 24h so no overflow
00150     sign = '-';
00151   }
00152   const int seconds = offset % 60;
00153   const int minutes = (offset /= 60) % 60;
00154   const int hours = offset /= 60;
00155   const char sep = mode[0];
00156   const bool ext = (sep != '\0' && mode[1] == '*');
00157   const bool ccc = (ext && mode[2] == ':');
00158   if (ext && (!ccc || seconds != 0)) {
00159     ep = Format02d(ep, seconds);
00160     *--ep = sep;
00161   } else {
00162     // If we're not rendering seconds, sub-minute negative offsets
00163     // should get a positive sign (e.g., offset=-10s => "+00:00").
00164     if (hours == 0 && minutes == 0) sign = '+';
00165   }
00166   if (!ccc || minutes != 0 || seconds != 0) {
00167     ep = Format02d(ep, minutes);
00168     if (sep != '\0') *--ep = sep;
00169   }
00170   ep = Format02d(ep, hours);
00171   *--ep = sign;
00172   return ep;
00173 }
00174 
00175 // Formats a std::tm using strftime(3).
00176 void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
00177   // strftime(3) returns the number of characters placed in the output
00178   // array (which may be 0 characters).  It also returns 0 to indicate
00179   // an error, like the array wasn't large enough.  To accommodate this,
00180   // the following code grows the buffer size from 2x the format std::string
00181   // length up to 32x.
00182   for (std::size_t i = 2; i != 32; i *= 2) {
00183     std::size_t buf_size = fmt.size() * i;
00184     std::vector<char> buf(buf_size);
00185     if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) {
00186       out->append(&buf[0], len);
00187       return;
00188     }
00189   }
00190 }
00191 
00192 // Used for %E#S/%E#f specifiers and for data values in parse().
00193 template <typename T>
00194 const char* ParseInt(const char* dp, int width, T min, T max, T* vp) {
00195   if (dp != nullptr) {
00196     const T kmin = std::numeric_limits<T>::min();
00197     bool erange = false;
00198     bool neg = false;
00199     T value = 0;
00200     if (*dp == '-') {
00201       neg = true;
00202       if (width <= 0 || --width != 0) {
00203         ++dp;
00204       } else {
00205         dp = nullptr;  // width was 1
00206       }
00207     }
00208     if (const char* const bp = dp) {
00209       while (const char* cp = strchr(kDigits, *dp)) {
00210         int d = static_cast<int>(cp - kDigits);
00211         if (d >= 10) break;
00212         if (value < kmin / 10) {
00213           erange = true;
00214           break;
00215         }
00216         value *= 10;
00217         if (value < kmin + d) {
00218           erange = true;
00219           break;
00220         }
00221         value -= d;
00222         dp += 1;
00223         if (width > 0 && --width == 0) break;
00224       }
00225       if (dp != bp && !erange && (neg || value != kmin)) {
00226         if (!neg || value != 0) {
00227           if (!neg) value = -value;  // make positive
00228           if (min <= value && value <= max) {
00229             *vp = value;
00230           } else {
00231             dp = nullptr;
00232           }
00233         } else {
00234           dp = nullptr;
00235         }
00236       } else {
00237         dp = nullptr;
00238       }
00239     }
00240   }
00241   return dp;
00242 }
00243 
00244 // The number of base-10 digits that can be represented by a signed 64-bit
00245 // integer.  That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1).
00246 const int kDigits10_64 = 18;
00247 
00248 // 10^n for everything that can be represented by a signed 64-bit integer.
00249 const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
00250     1,
00251     10,
00252     100,
00253     1000,
00254     10000,
00255     100000,
00256     1000000,
00257     10000000,
00258     100000000,
00259     1000000000,
00260     10000000000,
00261     100000000000,
00262     1000000000000,
00263     10000000000000,
00264     100000000000000,
00265     1000000000000000,
00266     10000000000000000,
00267     100000000000000000,
00268     1000000000000000000,
00269 };
00270 
00271 }  // namespace
00272 
00273 // Uses strftime(3) to format the given Time.  The following extended format
00274 // specifiers are also supported:
00275 //
00276 //   - %Ez  - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
00277 //   - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
00278 //   - %E#S - Seconds with # digits of fractional precision
00279 //   - %E*S - Seconds with full fractional precision (a literal '*')
00280 //   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
00281 //
00282 // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
00283 // handled internally for performance reasons.  strftime(3) is slow due to
00284 // a POSIX requirement to respect changes to ${TZ}.
00285 //
00286 // The TZ/GNU %s extension is handled internally because strftime() has
00287 // to use mktime() to generate it, and that assumes the local time zone.
00288 //
00289 // We also handle the %z and %Z specifiers to accommodate platforms that do
00290 // not support the tm_gmtoff and tm_zone extensions to std::tm.
00291 //
00292 // Requires that zero() <= fs < seconds(1).
00293 std::string format(const std::string& format, const time_point<seconds>& tp,
00294                    const detail::femtoseconds& fs, const time_zone& tz) {
00295   std::string result;
00296   result.reserve(format.size());  // A reasonable guess for the result size.
00297   const time_zone::absolute_lookup al = tz.lookup(tp);
00298   const std::tm tm = ToTM(al);
00299 
00300   // Scratch buffer for internal conversions.
00301   char buf[3 + kDigits10_64];  // enough for longest conversion
00302   char* const ep = buf + sizeof(buf);
00303   char* bp;  // works back from ep
00304 
00305   // Maintain three, disjoint subsequences that span format.
00306   //   [format.begin() ... pending) : already formatted into result
00307   //   [pending ... cur) : formatting pending, but no special cases
00308   //   [cur ... format.end()) : unexamined
00309   // Initially, everything is in the unexamined part.
00310   const char* pending = format.c_str();  // NUL terminated
00311   const char* cur = pending;
00312   const char* end = pending + format.length();
00313 
00314   while (cur != end) {  // while something is unexamined
00315     // Moves cur to the next percent sign.
00316     const char* start = cur;
00317     while (cur != end && *cur != '%') ++cur;
00318 
00319     // If the new pending text is all ordinary, copy it out.
00320     if (cur != start && pending == start) {
00321       result.append(pending, static_cast<std::size_t>(cur - pending));
00322       pending = start = cur;
00323     }
00324 
00325     // Span the sequential percent signs.
00326     const char* percent = cur;
00327     while (cur != end && *cur == '%') ++cur;
00328 
00329     // If the new pending text is all percents, copy out one
00330     // percent for every matched pair, then skip those pairs.
00331     if (cur != start && pending == start) {
00332       std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
00333       result.append(pending, escaped);
00334       pending += escaped * 2;
00335       // Also copy out a single trailing percent.
00336       if (pending != cur && cur == end) {
00337         result.push_back(*pending++);
00338       }
00339     }
00340 
00341     // Loop unless we have an unescaped percent.
00342     if (cur == end || (cur - percent) % 2 == 0) continue;
00343 
00344     // Simple specifiers that we handle ourselves.
00345     if (strchr("YmdeHMSzZs%", *cur)) {
00346       if (cur - 1 != pending) {
00347         FormatTM(&result, std::string(pending, cur - 1), tm);
00348       }
00349       switch (*cur) {
00350         case 'Y':
00351           // This avoids the tm.tm_year overflow problem for %Y, however
00352           // tm.tm_year will still be used by other specifiers like %D.
00353           bp = Format64(ep, 0, al.cs.year());
00354           result.append(bp, static_cast<std::size_t>(ep - bp));
00355           break;
00356         case 'm':
00357           bp = Format02d(ep, al.cs.month());
00358           result.append(bp, static_cast<std::size_t>(ep - bp));
00359           break;
00360         case 'd':
00361         case 'e':
00362           bp = Format02d(ep, al.cs.day());
00363           if (*cur == 'e' && *bp == '0') *bp = ' ';  // for Windows
00364           result.append(bp, static_cast<std::size_t>(ep - bp));
00365           break;
00366         case 'H':
00367           bp = Format02d(ep, al.cs.hour());
00368           result.append(bp, static_cast<std::size_t>(ep - bp));
00369           break;
00370         case 'M':
00371           bp = Format02d(ep, al.cs.minute());
00372           result.append(bp, static_cast<std::size_t>(ep - bp));
00373           break;
00374         case 'S':
00375           bp = Format02d(ep, al.cs.second());
00376           result.append(bp, static_cast<std::size_t>(ep - bp));
00377           break;
00378         case 'z':
00379           bp = FormatOffset(ep, al.offset, "");
00380           result.append(bp, static_cast<std::size_t>(ep - bp));
00381           break;
00382         case 'Z':
00383           result.append(al.abbr);
00384           break;
00385         case 's':
00386           bp = Format64(ep, 0, ToUnixSeconds(tp));
00387           result.append(bp, static_cast<std::size_t>(ep - bp));
00388           break;
00389         case '%':
00390           result.push_back('%');
00391           break;
00392       }
00393       pending = ++cur;
00394       continue;
00395     }
00396 
00397     // More complex specifiers that we handle ourselves.
00398     if (*cur == ':' && cur + 1 != end) {
00399       if (*(cur + 1) == 'z') {
00400         // Formats %:z.
00401         if (cur - 1 != pending) {
00402           FormatTM(&result, std::string(pending, cur - 1), tm);
00403         }
00404         bp = FormatOffset(ep, al.offset, ":");
00405         result.append(bp, static_cast<std::size_t>(ep - bp));
00406         pending = cur += 2;
00407         continue;
00408       }
00409       if (*(cur + 1) == ':' && cur + 2 != end) {
00410         if (*(cur + 2) == 'z') {
00411           // Formats %::z.
00412           if (cur - 1 != pending) {
00413             FormatTM(&result, std::string(pending, cur - 1), tm);
00414           }
00415           bp = FormatOffset(ep, al.offset, ":*");
00416           result.append(bp, static_cast<std::size_t>(ep - bp));
00417           pending = cur += 3;
00418           continue;
00419         }
00420         if (*(cur + 2) == ':' && cur + 3 != end) {
00421           if (*(cur + 3) == 'z') {
00422             // Formats %:::z.
00423             if (cur - 1 != pending) {
00424               FormatTM(&result, std::string(pending, cur - 1), tm);
00425             }
00426             bp = FormatOffset(ep, al.offset, ":*:");
00427             result.append(bp, static_cast<std::size_t>(ep - bp));
00428             pending = cur += 4;
00429             continue;
00430           }
00431         }
00432       }
00433     }
00434 
00435     // Loop if there is no E modifier.
00436     if (*cur != 'E' || ++cur == end) continue;
00437 
00438     // Format our extensions.
00439     if (*cur == 'z') {
00440       // Formats %Ez.
00441       if (cur - 2 != pending) {
00442         FormatTM(&result, std::string(pending, cur - 2), tm);
00443       }
00444       bp = FormatOffset(ep, al.offset, ":");
00445       result.append(bp, static_cast<std::size_t>(ep - bp));
00446       pending = ++cur;
00447     } else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
00448       // Formats %E*z.
00449       if (cur - 2 != pending) {
00450         FormatTM(&result, std::string(pending, cur - 2), tm);
00451       }
00452       bp = FormatOffset(ep, al.offset, ":*");
00453       result.append(bp, static_cast<std::size_t>(ep - bp));
00454       pending = cur += 2;
00455     } else if (*cur == '*' && cur + 1 != end &&
00456                (*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
00457       // Formats %E*S or %E*F.
00458       if (cur - 2 != pending) {
00459         FormatTM(&result, std::string(pending, cur - 2), tm);
00460       }
00461       char* cp = ep;
00462       bp = Format64(cp, 15, fs.count());
00463       while (cp != bp && cp[-1] == '0') --cp;
00464       switch (*(cur + 1)) {
00465         case 'S':
00466           if (cp != bp) *--bp = '.';
00467           bp = Format02d(bp, al.cs.second());
00468           break;
00469         case 'f':
00470           if (cp == bp) *--bp = '0';
00471           break;
00472       }
00473       result.append(bp, static_cast<std::size_t>(cp - bp));
00474       pending = cur += 2;
00475     } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
00476       // Formats %E4Y.
00477       if (cur - 2 != pending) {
00478         FormatTM(&result, std::string(pending, cur - 2), tm);
00479       }
00480       bp = Format64(ep, 4, al.cs.year());
00481       result.append(bp, static_cast<std::size_t>(ep - bp));
00482       pending = cur += 2;
00483     } else if (std::isdigit(*cur)) {
00484       // Possibly found %E#S or %E#f.
00485       int n = 0;
00486       if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
00487         if (*np == 'S' || *np == 'f') {
00488           // Formats %E#S or %E#f.
00489           if (cur - 2 != pending) {
00490             FormatTM(&result, std::string(pending, cur - 2), tm);
00491           }
00492           bp = ep;
00493           if (n > 0) {
00494             if (n > kDigits10_64) n = kDigits10_64;
00495             bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15]
00496                                           : fs.count() / kExp10[15 - n]);
00497             if (*np == 'S') *--bp = '.';
00498           }
00499           if (*np == 'S') bp = Format02d(bp, al.cs.second());
00500           result.append(bp, static_cast<std::size_t>(ep - bp));
00501           pending = cur = ++np;
00502         }
00503       }
00504     }
00505   }
00506 
00507   // Formats any remaining data.
00508   if (end != pending) {
00509     FormatTM(&result, std::string(pending, end), tm);
00510   }
00511 
00512   return result;
00513 }
00514 
00515 namespace {
00516 
00517 const char* ParseOffset(const char* dp, const char* mode, int* offset) {
00518   if (dp != nullptr) {
00519     const char first = *dp++;
00520     if (first == '+' || first == '-') {
00521       char sep = mode[0];
00522       int hours = 0;
00523       int minutes = 0;
00524       int seconds = 0;
00525       const char* ap = ParseInt(dp, 2, 0, 23, &hours);
00526       if (ap != nullptr && ap - dp == 2) {
00527         dp = ap;
00528         if (sep != '\0' && *ap == sep) ++ap;
00529         const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
00530         if (bp != nullptr && bp - ap == 2) {
00531           dp = bp;
00532           if (sep != '\0' && *bp == sep) ++bp;
00533           const char* cp = ParseInt(bp, 2, 0, 59, &seconds);
00534           if (cp != nullptr && cp - bp == 2) dp = cp;
00535         }
00536         *offset = ((hours * 60 + minutes) * 60) + seconds;
00537         if (first == '-') *offset = -*offset;
00538       } else {
00539         dp = nullptr;
00540       }
00541     } else if (first == 'Z') {  // Zulu
00542       *offset = 0;
00543     } else {
00544       dp = nullptr;
00545     }
00546   }
00547   return dp;
00548 }
00549 
00550 const char* ParseZone(const char* dp, std::string* zone) {
00551   zone->clear();
00552   if (dp != nullptr) {
00553     while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++);
00554     if (zone->empty()) dp = nullptr;
00555   }
00556   return dp;
00557 }
00558 
00559 const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
00560   if (dp != nullptr) {
00561     std::int_fast64_t v = 0;
00562     std::int_fast64_t exp = 0;
00563     const char* const bp = dp;
00564     while (const char* cp = strchr(kDigits, *dp)) {
00565       int d = static_cast<int>(cp - kDigits);
00566       if (d >= 10) break;
00567       if (exp < 15) {
00568         exp += 1;
00569         v *= 10;
00570         v += d;
00571       }
00572       ++dp;
00573     }
00574     if (dp != bp) {
00575       v *= kExp10[15 - exp];
00576       *subseconds = detail::femtoseconds(v);
00577     } else {
00578       dp = nullptr;
00579     }
00580   }
00581   return dp;
00582 }
00583 
00584 // Parses a string into a std::tm using strptime(3).
00585 const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
00586   if (dp != nullptr) {
00587     dp = strptime(dp, fmt, tm);
00588   }
00589   return dp;
00590 }
00591 
00592 }  // namespace
00593 
00594 // Uses strptime(3) to parse the given input.  Supports the same extended
00595 // format specifiers as format(), although %E#S and %E*S are treated
00596 // identically (and similarly for %E#f and %E*f).  %Ez and %E*z also accept
00597 // the same inputs.
00598 //
00599 // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
00600 // handled internally so that we can normally avoid strptime() altogether
00601 // (which is particularly helpful when the native implementation is broken).
00602 //
00603 // The TZ/GNU %s extension is handled internally because strptime() has to
00604 // use localtime_r() to generate it, and that assumes the local time zone.
00605 //
00606 // We also handle the %z specifier to accommodate platforms that do not
00607 // support the tm_gmtoff extension to std::tm.  %Z is parsed but ignored.
00608 bool parse(const std::string& format, const std::string& input,
00609            const time_zone& tz, time_point<seconds>* sec,
00610            detail::femtoseconds* fs, std::string* err) {
00611   // The unparsed input.
00612   const char* data = input.c_str();  // NUL terminated
00613 
00614   // Skips leading whitespace.
00615   while (std::isspace(*data)) ++data;
00616 
00617   const year_t kyearmax = std::numeric_limits<year_t>::max();
00618   const year_t kyearmin = std::numeric_limits<year_t>::min();
00619 
00620   // Sets default values for unspecified fields.
00621   bool saw_year = false;
00622   year_t year = 1970;
00623   std::tm tm{};
00624   tm.tm_year = 1970 - 1900;
00625   tm.tm_mon = 1 - 1;  // Jan
00626   tm.tm_mday = 1;
00627   tm.tm_hour = 0;
00628   tm.tm_min = 0;
00629   tm.tm_sec = 0;
00630   tm.tm_wday = 4;  // Thu
00631   tm.tm_yday = 0;
00632   tm.tm_isdst = 0;
00633   auto subseconds = detail::femtoseconds::zero();
00634   bool saw_offset = false;
00635   int offset = 0;  // No offset from passed tz.
00636   std::string zone = "UTC";
00637 
00638   const char* fmt = format.c_str();  // NUL terminated
00639   bool twelve_hour = false;
00640   bool afternoon = false;
00641 
00642   bool saw_percent_s = false;
00643   std::int_fast64_t percent_s = 0;
00644 
00645   // Steps through format, one specifier at a time.
00646   while (data != nullptr && *fmt != '\0') {
00647     if (std::isspace(*fmt)) {
00648       while (std::isspace(*data)) ++data;
00649       while (std::isspace(*++fmt)) continue;
00650       continue;
00651     }
00652 
00653     if (*fmt != '%') {
00654       if (*data == *fmt) {
00655         ++data;
00656         ++fmt;
00657       } else {
00658         data = nullptr;
00659       }
00660       continue;
00661     }
00662 
00663     const char* percent = fmt;
00664     if (*++fmt == '\0') {
00665       data = nullptr;
00666       continue;
00667     }
00668     switch (*fmt++) {
00669       case 'Y':
00670         // Symmetrically with FormatTime(), directly handing %Y avoids the
00671         // tm.tm_year overflow problem.  However, tm.tm_year will still be
00672         // used by other specifiers like %D.
00673         data = ParseInt(data, 0, kyearmin, kyearmax, &year);
00674         if (data != nullptr) saw_year = true;
00675         continue;
00676       case 'm':
00677         data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
00678         if (data != nullptr) tm.tm_mon -= 1;
00679         continue;
00680       case 'd':
00681       case 'e':
00682         data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
00683         continue;
00684       case 'H':
00685         data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
00686         twelve_hour = false;
00687         continue;
00688       case 'M':
00689         data = ParseInt(data, 2, 0, 59, &tm.tm_min);
00690         continue;
00691       case 'S':
00692         data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
00693         continue;
00694       case 'I':
00695       case 'l':
00696       case 'r':  // probably uses %I
00697         twelve_hour = true;
00698         break;
00699       case 'R':  // uses %H
00700       case 'T':  // uses %H
00701       case 'c':  // probably uses %H
00702       case 'X':  // probably uses %H
00703         twelve_hour = false;
00704         break;
00705       case 'z':
00706         data = ParseOffset(data, "", &offset);
00707         if (data != nullptr) saw_offset = true;
00708         continue;
00709       case 'Z':  // ignored; zone abbreviations are ambiguous
00710         data = ParseZone(data, &zone);
00711         continue;
00712       case 's':
00713         data = ParseInt(data, 0,
00714                         std::numeric_limits<std::int_fast64_t>::min(),
00715                         std::numeric_limits<std::int_fast64_t>::max(),
00716                         &percent_s);
00717         if (data != nullptr) saw_percent_s = true;
00718         continue;
00719       case ':':
00720         if (fmt[0] == 'z' ||
00721             (fmt[0] == ':' &&
00722              (fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) {
00723           data = ParseOffset(data, ":", &offset);
00724           if (data != nullptr) saw_offset = true;
00725           fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3;
00726           continue;
00727         }
00728         break;
00729       case '%':
00730         data = (*data == '%' ? data + 1 : nullptr);
00731         continue;
00732       case 'E':
00733         if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
00734           data = ParseOffset(data, ":", &offset);
00735           if (data != nullptr) saw_offset = true;
00736           fmt += (fmt[0] == 'z') ? 1 : 2;
00737           continue;
00738         }
00739         if (fmt[0] == '*' && fmt[1] == 'S') {
00740           data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
00741           if (data != nullptr && *data == '.') {
00742             data = ParseSubSeconds(data + 1, &subseconds);
00743           }
00744           fmt += 2;
00745           continue;
00746         }
00747         if (fmt[0] == '*' && fmt[1] == 'f') {
00748           if (data != nullptr && std::isdigit(*data)) {
00749             data = ParseSubSeconds(data, &subseconds);
00750           }
00751           fmt += 2;
00752           continue;
00753         }
00754         if (fmt[0] == '4' && fmt[1] == 'Y') {
00755           const char* bp = data;
00756           data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year);
00757           if (data != nullptr) {
00758             if (data - bp == 4) {
00759               saw_year = true;
00760             } else {
00761               data = nullptr;  // stopped too soon
00762             }
00763           }
00764           fmt += 2;
00765           continue;
00766         }
00767         if (std::isdigit(*fmt)) {
00768           int n = 0;  // value ignored
00769           if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
00770             if (*np == 'S') {
00771               data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
00772               if (data != nullptr && *data == '.') {
00773                 data = ParseSubSeconds(data + 1, &subseconds);
00774               }
00775               fmt = ++np;
00776               continue;
00777             }
00778             if (*np == 'f') {
00779               if (data != nullptr && std::isdigit(*data)) {
00780                 data = ParseSubSeconds(data, &subseconds);
00781               }
00782               fmt = ++np;
00783               continue;
00784             }
00785           }
00786         }
00787         if (*fmt == 'c') twelve_hour = false;  // probably uses %H
00788         if (*fmt == 'X') twelve_hour = false;  // probably uses %H
00789         if (*fmt != '\0') ++fmt;
00790         break;
00791       case 'O':
00792         if (*fmt == 'H') twelve_hour = false;
00793         if (*fmt == 'I') twelve_hour = true;
00794         if (*fmt != '\0') ++fmt;
00795         break;
00796     }
00797 
00798     // Parses the current specifier.
00799     const char* orig_data = data;
00800     std::string spec(percent, static_cast<std::size_t>(fmt - percent));
00801     data = ParseTM(data, spec.c_str(), &tm);
00802 
00803     // If we successfully parsed %p we need to remember whether the result
00804     // was AM or PM so that we can adjust tm_hour before time_zone::lookup().
00805     // So reparse the input with a known AM hour, and check if it is shifted
00806     // to a PM hour.
00807     if (spec == "%p" && data != nullptr) {
00808       std::string test_input = "1";
00809       test_input.append(orig_data, static_cast<std::size_t>(data - orig_data));
00810       const char* test_data = test_input.c_str();
00811       std::tm tmp{};
00812       ParseTM(test_data, "%I%p", &tmp);
00813       afternoon = (tmp.tm_hour == 13);
00814     }
00815   }
00816 
00817   // Adjust a 12-hour tm_hour value if it should be in the afternoon.
00818   if (twelve_hour && afternoon && tm.tm_hour < 12) {
00819     tm.tm_hour += 12;
00820   }
00821 
00822   if (data == nullptr) {
00823     if (err != nullptr) *err = "Failed to parse input";
00824     return false;
00825   }
00826 
00827   // Skip any remaining whitespace.
00828   while (std::isspace(*data)) ++data;
00829 
00830   // parse() must consume the entire input std::string.
00831   if (*data != '\0') {
00832     if (err != nullptr) *err = "Illegal trailing data in input string";
00833     return false;
00834   }
00835 
00836   // If we saw %s then we ignore anything else and return that time.
00837   if (saw_percent_s) {
00838     *sec = FromUnixSeconds(percent_s);
00839     *fs = detail::femtoseconds::zero();
00840     return true;
00841   }
00842 
00843   // If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields
00844   // in UTC and then shift by that offset.  Otherwise we want to interpret
00845   // the fields directly in the passed time_zone.
00846   time_zone ptz = saw_offset ? utc_time_zone() : tz;
00847 
00848   // Allows a leap second of 60 to normalize forward to the following ":00".
00849   if (tm.tm_sec == 60) {
00850     tm.tm_sec -= 1;
00851     offset -= 1;
00852     subseconds = detail::femtoseconds::zero();
00853   }
00854 
00855   if (!saw_year) {
00856     year = year_t{tm.tm_year};
00857     if (year > kyearmax - 1900) {
00858       // Platform-dependent, maybe unreachable.
00859       if (err != nullptr) *err = "Out-of-range year";
00860       return false;
00861     }
00862     year += 1900;
00863   }
00864 
00865   const int month = tm.tm_mon + 1;
00866   civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
00867 
00868   // parse() should not allow normalization. Due to the restricted field
00869   // ranges above (see ParseInt()), the only possibility is for days to roll
00870   // into months. That is, parsing "Sep 31" should not produce "Oct 1".
00871   if (cs.month() != month || cs.day() != tm.tm_mday) {
00872     if (err != nullptr) *err = "Out-of-range field";
00873     return false;
00874   }
00875 
00876   // Accounts for the offset adjustment before converting to absolute time.
00877   if ((offset < 0 && cs > civil_second::max() + offset) ||
00878       (offset > 0 && cs < civil_second::min() + offset)) {
00879     if (err != nullptr) *err = "Out-of-range field";
00880     return false;
00881   }
00882   cs -= offset;
00883 
00884   const auto tp = ptz.lookup(cs).pre;
00885   // Checks for overflow/underflow and returns an error as necessary.
00886   if (tp == time_point<seconds>::max()) {
00887     const auto al = ptz.lookup(time_point<seconds>::max());
00888     if (cs > al.cs) {
00889       if (err != nullptr) *err = "Out-of-range field";
00890       return false;
00891     }
00892   }
00893   if (tp == time_point<seconds>::min()) {
00894     const auto al = ptz.lookup(time_point<seconds>::min());
00895     if (cs < al.cs) {
00896       if (err != nullptr) *err = "Out-of-range field";
00897       return false;
00898     }
00899   }
00900 
00901   *sec = tp;
00902   *fs = subseconds;
00903   return true;
00904 }
00905 
00906 }  // namespace detail
00907 }  // namespace cctz
00908 }  // namespace time_internal
00909 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15