diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h index 9c2bd45d2803e..71859af7c7e4a 100644 --- a/llvm/include/llvm/Support/Chrono.h +++ b/llvm/include/llvm/Support/Chrono.h @@ -33,6 +33,19 @@ namespace sys { template using TimePoint = std::chrono::time_point; +// utc_clock and utc_time are only available since C++20. Add enough code to +// support formatting date/time in UTC. +class UtcClock : public std::chrono::system_clock {}; + +template +using UtcTime = std::chrono::time_point; + +/// Convert a std::time_t to a UtcTime +inline UtcTime toUtcTime(std::time_t T) { + using namespace std::chrono; + return UtcTime(seconds(T)); +} + /// Convert a TimePoint to std::time_t inline std::time_t toTimeT(TimePoint<> TP) { using namespace std::chrono; @@ -40,6 +53,13 @@ inline std::time_t toTimeT(TimePoint<> TP) { time_point_cast(TP)); } +/// Convert a UtcTime to std::time_t +inline std::time_t toTimeT(UtcTime<> TP) { + using namespace std::chrono; + return system_clock::to_time_t(time_point( + duration_cast(TP.time_since_epoch()))); +} + /// Convert a std::time_t to a TimePoint inline TimePoint toTimePoint(std::time_t T) { @@ -58,6 +78,7 @@ toTimePoint(std::time_t T, uint32_t nsec) { } // namespace sys raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP); +raw_ostream &operator<<(raw_ostream &OS, sys::UtcTime<> TP); /// Format provider for TimePoint<> /// @@ -73,6 +94,11 @@ struct format_provider> { StringRef Style); }; +template <> struct format_provider> { + static void format(const sys::UtcTime &TP, + llvm::raw_ostream &OS, StringRef Style); +}; + namespace detail { template struct unit { static const char value[]; }; template const char unit::value[] = ""; diff --git a/llvm/lib/Support/Chrono.cpp b/llvm/lib/Support/Chrono.cpp index 859ece8f55008..993d200675fe5 100644 --- a/llvm/lib/Support/Chrono.cpp +++ b/llvm/lib/Support/Chrono.cpp @@ -40,6 +40,24 @@ static inline struct tm getStructTM(TimePoint<> TP) { return Storage; } +static inline struct tm getStructTMUtc(UtcTime<> TP) { + struct tm Storage; + std::time_t OurTime = toTimeT(TP); + +#if defined(LLVM_ON_UNIX) + struct tm *LT = ::gmtime_r(&OurTime, &Storage); + assert(LT); + (void)LT; +#endif +#if defined(_WIN32) + int Error = ::gmtime_s(&Storage, &OurTime); + assert(!Error); + (void)Error; +#endif + + return Storage; +} + raw_ostream &operator<<(raw_ostream &OS, TimePoint<> TP) { struct tm LT = getStructTM(TP); char Buffer[sizeof("YYYY-MM-DD HH:MM:SS")]; @@ -50,12 +68,10 @@ raw_ostream &operator<<(raw_ostream &OS, TimePoint<> TP) { .count())); } -void format_provider>::format(const TimePoint<> &T, raw_ostream &OS, - StringRef Style) { +template +static void format(const T &Fractional, struct tm <, 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; @@ -90,4 +106,23 @@ void format_provider>::format(const TimePoint<> &T, raw_ostream &OS, OS << (Len ? Buffer : "BAD-DATE-FORMAT"); } +void format_provider>::format( + const UtcTime &T, raw_ostream &OS, StringRef Style) { + using namespace std::chrono; + UtcTime Truncated = + UtcTime(duration_cast(T.time_since_epoch())); + auto Fractional = T - Truncated; + struct tm LT = getStructTMUtc(Truncated); + llvm::format(Fractional, LT, OS, Style); +} + +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); + llvm::format(Fractional, LT, OS, Style); +} + } // namespace llvm diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 12f2e99593260..5baece87d3ee1 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -90,6 +90,7 @@ add_llvm_unittest(SupportTests TypeTraitsTest.cpp TrailingObjectsTest.cpp UnicodeTest.cpp + UTCTimeTest.cpp VersionTupleTest.cpp VirtualFileSystemTest.cpp WithColorTest.cpp diff --git a/llvm/unittests/Support/UTCTimeTest.cpp b/llvm/unittests/Support/UTCTimeTest.cpp new file mode 100644 index 0000000000000..64e04d29376c3 --- /dev/null +++ b/llvm/unittests/Support/UTCTimeTest.cpp @@ -0,0 +1,41 @@ +//===- unittests/Support/UTCTimeTest.cpp ----------------- ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Chrono.h" +#include "gtest/gtest.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadic.h" + +namespace llvm { +namespace sys { +namespace { + +TEST(UTCTime, convertutc) { + // Get the current time. + time_t currentTime; + time(¤tTime); + + // Convert with toUtcTime. + SmallString<15> customResultString; + raw_svector_ostream T(customResultString); + T << formatv("{0:%Y-%m-%d %H:%M:%S}", llvm::sys::toUtcTime(currentTime)); + + // Convert with gmtime. + char gmtimeResultString[20]; + std::tm *gmtimeResult = std::gmtime(¤tTime); + assert(gmtimeResult != NULL); + std::strftime(gmtimeResultString, 20, "%Y-%m-%d %H:%M:%S", gmtimeResult); + + // Compare the formatted strings. + EXPECT_EQ(customResultString, StringRef(gmtimeResultString, 19)); + +} +} // namespace +} // namespace sys +} // namespace llvm