Index: include/llvm/Support/Chrono.h =================================================================== --- include/llvm/Support/Chrono.h +++ include/llvm/Support/Chrono.h @@ -51,6 +51,20 @@ 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> { + static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS, + StringRef Style); +}; + /// Implementation of format_provider for duration types. /// /// The options string of a duration type has the grammar: Index: lib/Support/Chrono.cpp =================================================================== --- lib/Support/Chrono.cpp +++ lib/Support/Chrono.cpp @@ -51,4 +51,44 @@ .count())); } +void format_provider>::format(const TimePoint<> &T, raw_ostream &OS, + StringRef Style) { + using namespace std::chrono; + TimePoint Truncated = time_point_cast(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(Fractional).count()); + ++I; + continue; + case 'f': // Microseconds, from Python. + FStream << llvm::format( + "%.6lu", duration_cast(Fractional).count()); + ++I; + continue; + case 'N': // Nanoseconds, from date(1). + FStream << llvm::format( + "%.6lu", duration_cast(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(), <); + OS << (Len ? Buffer : "BAD-DATE-FORMAT"); +} + } // namespace llvm Index: unittests/Support/Chrono.cpp =================================================================== --- unittests/Support/Chrono.cpp +++ unittests/Support/Chrono.cpp @@ -31,33 +31,35 @@ 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(); - - // 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 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()); + OS << T; + EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str()); + + // 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