Support formatv of TimePoint with strftime-style formats.

Summary:
Support formatv of TimePoint with strftime-style formats.

Extensions for millis/micros/nanos are added.
Inital use case is HH:MM:SS.MMM timestamps in clangd logs.

Reviewers: bkramer, ilya-biryukov

Subscribers: labath, llvm-commits

Differential Revision: https://reviews.llvm.org/D38992

llvm-svn: 316419
This commit is contained in:
Sam McCall 2017-10-24 08:30:19 +00:00
parent f4ca4a6fa1
commit fb4a9b7ede
3 changed files with 80 additions and 24 deletions

View File

@ -51,6 +51,20 @@ toTimePoint(std::time_t T) {
raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
/// Format provider for TimePoint<>
///
/// The options string is a strftime format string, with extensions:
/// - %L is millis: 000-999
/// - %f is micros: 000000-999999
/// - %N is nanos: 000000000 - 999999999
///
/// If no options are given, the default format is "%Y-%m-%d %H:%M:%S.%N".
template <>
struct format_provider<sys::TimePoint<>> {
static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS,
StringRef Style);
};
/// Implementation of format_provider<T> for duration types.
///
/// The options string of a duration type has the grammar:

View File

@ -51,4 +51,44 @@ raw_ostream &operator<<(raw_ostream &OS, TimePoint<> TP) {
.count()));
}
void format_provider<TimePoint<>>::format(const TimePoint<> &T, raw_ostream &OS,
StringRef Style) {
using namespace std::chrono;
TimePoint<seconds> Truncated = time_point_cast<seconds>(T);
auto Fractional = T - Truncated;
struct tm LT = getStructTM(Truncated);
// Handle extensions first. strftime mangles unknown %x on some platforms.
if (Style.empty()) Style = "%Y-%m-%d %H:%M:%S.%N";
std::string Format;
raw_string_ostream FStream(Format);
for (unsigned I = 0; I < Style.size(); ++I) {
if (Style[I] == '%' && Style.size() > I + 1) switch (Style[I + 1]) {
case 'L': // Milliseconds, from Ruby.
FStream << llvm::format(
"%.3lu", duration_cast<milliseconds>(Fractional).count());
++I;
continue;
case 'f': // Microseconds, from Python.
FStream << llvm::format(
"%.6lu", duration_cast<microseconds>(Fractional).count());
++I;
continue;
case 'N': // Nanoseconds, from date(1).
FStream << llvm::format(
"%.6lu", duration_cast<nanoseconds>(Fractional).count());
++I;
continue;
case '%': // Consume %%, so %%f parses as (%%)f not %(%f)
FStream << "%%";
++I;
continue;
}
FStream << Style[I];
}
FStream.flush();
char Buffer[256]; // Should be enough for anywhen.
size_t Len = strftime(Buffer, sizeof(Buffer), Format.c_str(), &LT);
OS << (Len ? Buffer : "BAD-DATE-FORMAT");
}
} // namespace llvm

View File

@ -31,33 +31,35 @@ TEST(Chrono, TimeTConversion) {
EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));
}
TEST(Chrono, StringConversion) {
TEST(Chrono, TimePointFormat) {
using namespace std::chrono;
struct tm TM {};
TM.tm_year = 106;
TM.tm_mon = 0;
TM.tm_mday = 2;
TM.tm_hour = 15;
TM.tm_min = 4;
TM.tm_sec = 5;
TM.tm_isdst = -1;
TimePoint<> T =
system_clock::from_time_t(mktime(&TM)) + nanoseconds(123456789);
// operator<< uses the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN
std::string S;
raw_string_ostream OS(S);
OS << system_clock::now();
OS << T;
EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str());
// Do a basic sanity check on the output.
// The format we expect is YYYY-MM-DD HH:MM:SS.MMMUUUNNN
StringRef Date, Time;
std::tie(Date, Time) = StringRef(OS.str()).split(' ');
SmallVector<StringRef, 3> Components;
Date.split(Components, '-');
ASSERT_EQ(3u, Components.size());
EXPECT_EQ(4u, Components[0].size());
EXPECT_EQ(2u, Components[1].size());
EXPECT_EQ(2u, Components[2].size());
StringRef Sec, Nano;
std::tie(Sec, Nano) = Time.split('.');
Components.clear();
Sec.split(Components, ':');
ASSERT_EQ(3u, Components.size());
EXPECT_EQ(2u, Components[0].size());
EXPECT_EQ(2u, Components[1].size());
EXPECT_EQ(2u, Components[2].size());
EXPECT_EQ(9u, Nano.size());
// formatv default style matches operator<<.
EXPECT_EQ("2006-01-02 15:04:05.123456789", formatv("{0}", T).str());
// formatv supports strftime-style format strings.
EXPECT_EQ("15:04:05", formatv("{0:%H:%M:%S}", T).str());
// formatv supports our strftime extensions for sub-second precision.
EXPECT_EQ("123", formatv("{0:%L}", T).str());
EXPECT_EQ("123456", formatv("{0:%f}", T).str());
EXPECT_EQ("123456789", formatv("{0:%N}", T).str());
// our extensions don't interfere with %% escaping.
EXPECT_EQ("%foo", formatv("{0:%%foo}", T).str());
}
// Test that toTimePoint and toTimeT can be called with a arguments with varying