in absl/time/internal/cctz/src/time_zone_format.cc [333:577]
std::string format(const std::string& format, const time_point<seconds>& tp,
const detail::femtoseconds& fs, const time_zone& tz) {
std::string result;
result.reserve(format.size()); // A reasonable guess for the result size.
const time_zone::absolute_lookup al = tz.lookup(tp);
const std::tm tm = ToTM(al);
// Scratch buffer for internal conversions.
char buf[3 + kDigits10_64]; // enough for longest conversion
char* const ep = buf + sizeof(buf);
char* bp; // works back from ep
// Maintain three, disjoint subsequences that span format.
// [format.begin() ... pending) : already formatted into result
// [pending ... cur) : formatting pending, but no special cases
// [cur ... format.end()) : unexamined
// Initially, everything is in the unexamined part.
const char* pending = format.c_str(); // NUL terminated
const char* cur = pending;
const char* end = pending + format.length();
while (cur != end) { // while something is unexamined
// Moves cur to the next percent sign.
const char* start = cur;
while (cur != end && *cur != '%') ++cur;
// If the new pending text is all ordinary, copy it out.
if (cur != start && pending == start) {
result.append(pending, static_cast<std::size_t>(cur - pending));
pending = start = cur;
}
// Span the sequential percent signs.
const char* percent = cur;
while (cur != end && *cur == '%') ++cur;
// If the new pending text is all percents, copy out one
// percent for every matched pair, then skip those pairs.
if (cur != start && pending == start) {
std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
result.append(pending, escaped);
pending += escaped * 2;
// Also copy out a single trailing percent.
if (pending != cur && cur == end) {
result.push_back(*pending++);
}
}
// Loop unless we have an unescaped percent.
if (cur == end || (cur - percent) % 2 == 0) continue;
// Simple specifiers that we handle ourselves.
if (strchr("YmdeUuWwHMSzZs%", *cur)) {
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
switch (*cur) {
case 'Y':
// This avoids the tm.tm_year overflow problem for %Y, however
// tm.tm_year will still be used by other specifiers like %D.
bp = Format64(ep, 0, al.cs.year());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'm':
bp = Format02d(ep, al.cs.month());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'd':
case 'e':
bp = Format02d(ep, al.cs.day());
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'U':
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'u':
bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'W':
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'w':
bp = Format64(ep, 0, tm.tm_wday);
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'H':
bp = Format02d(ep, al.cs.hour());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'M':
bp = Format02d(ep, al.cs.minute());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'S':
bp = Format02d(ep, al.cs.second());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'z':
bp = FormatOffset(ep, al.offset, "");
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'Z':
result.append(al.abbr);
break;
case 's':
bp = Format64(ep, 0, ToUnixSeconds(tp));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case '%':
result.push_back('%');
break;
}
pending = ++cur;
continue;
}
// More complex specifiers that we handle ourselves.
if (*cur == ':' && cur + 1 != end) {
if (*(cur + 1) == 'z') {
// Formats %:z.
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
bp = FormatOffset(ep, al.offset, ":");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 2;
continue;
}
if (*(cur + 1) == ':' && cur + 2 != end) {
if (*(cur + 2) == 'z') {
// Formats %::z.
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
bp = FormatOffset(ep, al.offset, ":*");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 3;
continue;
}
if (*(cur + 2) == ':' && cur + 3 != end) {
if (*(cur + 3) == 'z') {
// Formats %:::z.
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
bp = FormatOffset(ep, al.offset, ":*:");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 4;
continue;
}
}
}
}
// Loop if there is no E modifier.
if (*cur != 'E' || ++cur == end) continue;
// Format our extensions.
if (*cur == 'T') {
// Formats %ET.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
result.append("T");
pending = ++cur;
} else if (*cur == 'z') {
// Formats %Ez.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = FormatOffset(ep, al.offset, ":");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = ++cur;
} else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
// Formats %E*z.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = FormatOffset(ep, al.offset, ":*");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 2;
} else if (*cur == '*' && cur + 1 != end &&
(*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
// Formats %E*S or %E*F.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
char* cp = ep;
bp = Format64(cp, 15, fs.count());
while (cp != bp && cp[-1] == '0') --cp;
switch (*(cur + 1)) {
case 'S':
if (cp != bp) *--bp = '.';
bp = Format02d(bp, al.cs.second());
break;
case 'f':
if (cp == bp) *--bp = '0';
break;
}
result.append(bp, static_cast<std::size_t>(cp - bp));
pending = cur += 2;
} else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
// Formats %E4Y.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = Format64(ep, 4, al.cs.year());
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 2;
} else if (std::isdigit(*cur)) {
// Possibly found %E#S or %E#f.
int n = 0;
if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
if (*np == 'S' || *np == 'f') {
// Formats %E#S or %E#f.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = ep;
if (n > 0) {
if (n > kDigits10_64) n = kDigits10_64;
bp = Format64(bp, n,
(n > 15) ? fs.count() * kExp10[n - 15]
: fs.count() / kExp10[15 - n]);
if (*np == 'S') *--bp = '.';
}
if (*np == 'S') bp = Format02d(bp, al.cs.second());
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur = ++np;
}
}
}
}
// Formats any remaining data.
if (end != pending) {
FormatTM(&result, std::string(pending, end), tm);
}
return result;
}