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:
parent
f4ca4a6fa1
commit
fb4a9b7ede
|
@ -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:
|
||||
|
|
|
@ -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(), <);
|
||||
OS << (Len ? Buffer : "BAD-DATE-FORMAT");
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue