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