15 #if !defined(HAS_STRPTIME) 16 # if !defined(_MSC_VER) && !defined(__MINGW32__) 17 # define HAS_STRPTIME 1 // assume everyone has strptime() except windows 41 namespace time_internal {
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()));
58 std::tm
ToTM(
const time_zone::absolute_lookup& al) {
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;
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();
72 tm.tm_year =
static_cast<int>(al.cs.year() - 1900);
99 tm.tm_isdst = al.is_dst ? 1 : 0;
103 const char kDigits[] =
"0123456789";
108 char* Format64(
char* ep,
int width, std::int_fast64_t
v) {
113 if (v == std::numeric_limits<std::int_fast64_t>::min()) {
115 std::int_fast64_t last_digit = -(v % 10);
117 if (last_digit < 0) {
122 *--ep = kDigits[last_digit];
128 *--ep = kDigits[v % 10];
130 while (--width >= 0) *--ep =
'0';
131 if (neg) *--ep =
'-';
136 char* Format02d(
char* ep,
int v) {
137 *--ep = kDigits[v % 10];
138 *--ep = kDigits[(v / 10) % 10];
143 char* FormatOffset(
char* ep,
int offset,
const char* mode) {
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);
164 if (hours == 0 && minutes == 0) sign =
'+';
166 if (!ccc || minutes != 0 || seconds != 0) {
167 ep = Format02d(ep, minutes);
168 if (sep !=
'\0') *--ep = sep;
170 ep = Format02d(ep, hours);
176 void FormatTM(std::string*
out,
const std::string& fmt,
const std::tm& tm) {
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);
193 template <
typename T>
194 const char* ParseInt(
const char* dp,
int width, T min, T max, T* vp) {
196 const T kmin = std::numeric_limits<T>::min();
202 if (width <= 0 || --width != 0) {
208 if (
const char*
const bp = dp) {
209 while (
const char* cp = strchr(kDigits, *dp)) {
210 int d =
static_cast<int>(cp - kDigits);
212 if (value < kmin / 10) {
217 if (value < kmin + d) {
223 if (width > 0 && --width == 0)
break;
225 if (dp != bp && !erange && (neg || value != kmin)) {
226 if (!neg || value != 0) {
227 if (!neg) value = -
value;
228 if (min <= value && value <= max) {
246 const int kDigits10_64 = 18;
249 const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
296 result.reserve(format.size());
298 const std::tm tm =
ToTM(al);
301 char buf[3 + kDigits10_64];
302 char*
const ep = buf +
sizeof(
buf);
310 const char* pending = format.c_str();
311 const char* cur = pending;
312 const char*
end = pending + format.length();
316 const char* start = cur;
317 while (cur != end && *cur !=
'%') ++cur;
320 if (cur != start && pending == start) {
321 result.append(pending, static_cast<std::size_t>(cur - pending));
322 pending = start = cur;
326 const char* percent = cur;
327 while (cur != end && *cur ==
'%') ++cur;
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;
336 if (pending != cur && cur == end) {
337 result.push_back(*pending++);
342 if (cur == end || (cur - percent) % 2 == 0)
continue;
345 if (strchr(
"YmdeHMSzZs%", *cur)) {
346 if (cur - 1 != pending) {
347 FormatTM(&result, std::string(pending, cur - 1), tm);
353 bp = Format64(ep, 0, al.
cs.
year());
354 result.append(bp, static_cast<std::size_t>(ep - bp));
357 bp = Format02d(ep, al.
cs.
month());
358 result.append(bp, static_cast<std::size_t>(ep - bp));
362 bp = Format02d(ep, al.
cs.
day());
363 if (*cur ==
'e' && *bp ==
'0') *bp =
' ';
364 result.append(bp, static_cast<std::size_t>(ep - bp));
367 bp = Format02d(ep, al.
cs.
hour());
368 result.append(bp, static_cast<std::size_t>(ep - bp));
372 result.append(bp, static_cast<std::size_t>(ep - bp));
376 result.append(bp, static_cast<std::size_t>(ep - bp));
379 bp = FormatOffset(ep, al.
offset,
"");
380 result.append(bp, static_cast<std::size_t>(ep - bp));
383 result.append(al.
abbr);
387 result.append(bp, static_cast<std::size_t>(ep - bp));
390 result.push_back(
'%');
398 if (*cur ==
':' && cur + 1 != end) {
399 if (*(cur + 1) ==
'z') {
401 if (cur - 1 != pending) {
402 FormatTM(&result, std::string(pending, cur - 1), tm);
404 bp = FormatOffset(ep, al.
offset,
":");
405 result.append(bp, static_cast<std::size_t>(ep - bp));
409 if (*(cur + 1) ==
':' && cur + 2 != end) {
410 if (*(cur + 2) ==
'z') {
412 if (cur - 1 != pending) {
413 FormatTM(&result, std::string(pending, cur - 1), tm);
415 bp = FormatOffset(ep, al.
offset,
":*");
416 result.append(bp, static_cast<std::size_t>(ep - bp));
420 if (*(cur + 2) ==
':' && cur + 3 != end) {
421 if (*(cur + 3) ==
'z') {
423 if (cur - 1 != pending) {
424 FormatTM(&result, std::string(pending, cur - 1), tm);
426 bp = FormatOffset(ep, al.
offset,
":*:");
427 result.append(bp, static_cast<std::size_t>(ep - bp));
436 if (*cur !=
'E' || ++cur == end)
continue;
441 if (cur - 2 != pending) {
442 FormatTM(&result, std::string(pending, cur - 2), tm);
444 bp = FormatOffset(ep, al.
offset,
":");
445 result.append(bp, static_cast<std::size_t>(ep - bp));
447 }
else if (*cur ==
'*' && cur + 1 != end && *(cur + 1) ==
'z') {
449 if (cur - 2 != pending) {
450 FormatTM(&result, std::string(pending, cur - 2), tm);
452 bp = FormatOffset(ep, al.
offset,
":*");
453 result.append(bp, static_cast<std::size_t>(ep - bp));
455 }
else if (*cur ==
'*' && cur + 1 != end &&
456 (*(cur + 1) ==
'S' || *(cur + 1) ==
'f')) {
458 if (cur - 2 != pending) {
459 FormatTM(&result, std::string(pending, cur - 2), tm);
462 bp = Format64(cp, 15, fs.count());
463 while (cp != bp && cp[-1] ==
'0') --cp;
464 switch (*(cur + 1)) {
466 if (cp != bp) *--bp =
'.';
470 if (cp == bp) *--bp =
'0';
473 result.append(bp, static_cast<std::size_t>(cp - bp));
475 }
else if (*cur ==
'4' && cur + 1 != end && *(cur + 1) ==
'Y') {
477 if (cur - 2 != pending) {
478 FormatTM(&result, std::string(pending, cur - 2), tm);
480 bp = Format64(ep, 4, al.
cs.
year());
481 result.append(bp, static_cast<std::size_t>(ep - bp));
483 }
else if (std::isdigit(*cur)) {
486 if (
const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
487 if (*np ==
'S' || *np ==
'f') {
489 if (cur - 2 != pending) {
490 FormatTM(&result, std::string(pending, cur - 2), tm);
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 =
'.';
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;
508 if (end != pending) {
509 FormatTM(&result, std::string(pending, end), tm);
517 const char* ParseOffset(
const char* dp,
const char* mode,
int* offset) {
519 const char first = *dp++;
520 if (first ==
'+' || first ==
'-') {
525 const char* ap = ParseInt(dp, 2, 0, 23, &hours);
526 if (ap !=
nullptr && ap - dp == 2) {
528 if (sep !=
'\0' && *ap == sep) ++ap;
529 const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
530 if (bp !=
nullptr && bp - ap == 2) {
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;
536 *offset = ((hours * 60 + minutes) * 60) +
seconds;
537 if (first ==
'-') *offset = -*offset;
541 }
else if (first ==
'Z') {
550 const char* ParseZone(
const char* dp, std::string* zone) {
553 while (*dp !=
'\0' && !std::isspace(*dp)) zone->push_back(*dp++);
554 if (zone->empty()) 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);
575 v *= kExp10[15 - exp];
585 const char* ParseTM(
const char* dp,
const char* fmt, std::tm* tm) {
587 dp = strptime(dp, fmt, tm);
612 const char*
data = input.c_str();
615 while (std::isspace(*data)) ++
data;
617 const year_t kyearmax = std::numeric_limits<year_t>::max();
618 const year_t kyearmin = std::numeric_limits<year_t>::min();
621 bool saw_year =
false;
624 tm.tm_year = 1970 - 1900;
633 auto subseconds = detail::femtoseconds::zero();
634 bool saw_offset =
false;
636 std::string zone =
"UTC";
638 const char* fmt = format.c_str();
639 bool twelve_hour =
false;
640 bool afternoon =
false;
642 bool saw_percent_s =
false;
643 std::int_fast64_t percent_s = 0;
646 while (data !=
nullptr && *fmt !=
'\0') {
647 if (std::isspace(*fmt)) {
648 while (std::isspace(*data)) ++
data;
649 while (std::isspace(*++fmt))
continue;
663 const char* percent = fmt;
664 if (*++fmt ==
'\0') {
673 data = ParseInt(data, 0, kyearmin, kyearmax, &year);
674 if (data !=
nullptr) saw_year =
true;
677 data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
678 if (data !=
nullptr) tm.tm_mon -= 1;
682 data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
685 data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
689 data = ParseInt(data, 2, 0, 59, &tm.tm_min);
692 data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
706 data = ParseOffset(data,
"", &offset);
707 if (data !=
nullptr) saw_offset =
true;
710 data = ParseZone(data, &zone);
713 data = ParseInt(data, 0,
714 std::numeric_limits<std::int_fast64_t>::min(),
715 std::numeric_limits<std::int_fast64_t>::max(),
717 if (data !=
nullptr) saw_percent_s =
true;
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;
730 data = (*data ==
'%' ? data + 1 :
nullptr);
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;
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);
747 if (fmt[0] ==
'*' && fmt[1] ==
'f') {
748 if (data !=
nullptr && std::isdigit(*data)) {
749 data = ParseSubSeconds(data, &subseconds);
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) {
767 if (std::isdigit(*fmt)) {
769 if (
const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
771 data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
772 if (data !=
nullptr && *data ==
'.') {
773 data = ParseSubSeconds(data + 1, &subseconds);
779 if (data !=
nullptr && std::isdigit(*data)) {
780 data = ParseSubSeconds(data, &subseconds);
787 if (*fmt ==
'c') twelve_hour =
false;
788 if (*fmt ==
'X') twelve_hour =
false;
789 if (*fmt !=
'\0') ++fmt;
792 if (*fmt ==
'H') twelve_hour =
false;
793 if (*fmt ==
'I') twelve_hour =
true;
794 if (*fmt !=
'\0') ++fmt;
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);
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();
812 ParseTM(test_data,
"%I%p", &tmp);
813 afternoon = (tmp.tm_hour == 13);
818 if (twelve_hour && afternoon && tm.tm_hour < 12) {
822 if (data ==
nullptr) {
823 if (err !=
nullptr) *err =
"Failed to parse input";
828 while (std::isspace(*data)) ++data;
832 if (err !=
nullptr) *err =
"Illegal trailing data in input string";
839 *fs = detail::femtoseconds::zero();
849 if (tm.tm_sec == 60) {
852 subseconds = detail::femtoseconds::zero();
856 year =
year_t{tm.tm_year};
857 if (year > kyearmax - 1900) {
859 if (err !=
nullptr) *err =
"Out-of-range year";
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);
871 if (cs.
month() != month || cs.
day() != tm.tm_mday) {
872 if (err !=
nullptr) *err =
"Out-of-range field";
879 if (err !=
nullptr) *err =
"Out-of-range field";
884 const auto tp = ptz.
lookup(cs).pre;
889 if (err !=
nullptr) *err =
"Out-of-range field";
896 if (err !=
nullptr) *err =
"Out-of-range field";
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)
absolute_lookup lookup(const time_point< seconds > &tp) const
CONSTEXPR_M int day() const noexcept
std::chrono::duration< std::int_fast64_t, std::femto > femtoseconds
CONSTEXPR_F weekday get_weekday(const civil_day &cd) noexcept
std::chrono::duration< std::int_fast64_t > seconds
static CONSTEXPR_F civil_time() max()
static CONSTEXPR_F civil_time() min()
CONSTEXPR_M year_t year() const noexcept
static char data[kDataSize]
std::string format(const std::string &, const time_point< seconds > &, const femtoseconds &, const time_zone &)
civil_time< day_tag > civil_day
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
CONSTEXPR_M int second() const noexcept
struct tm ToTM(absl::Time t, absl::TimeZone tz)
time_zone utc_time_zone()
CONSTEXPR_M int hour() const noexcept
std::int_fast64_t ToUnixSeconds(const time_point< seconds > &tp)