[llvm] [SystemZ][z/OS] Add UtcClock extension to chrono.h/.cpp (PR #67846)

Yusra Syeda via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 11 11:47:07 PDT 2023


https://github.com/ysyeda updated https://github.com/llvm/llvm-project/pull/67846

>From 637472dc67b62524b5b4cdc4687c9cea3578f39f Mon Sep 17 00:00:00 2001
From: Yusra Syeda <yusra.syeda at ibm.com>
Date: Fri, 29 Sep 2023 15:45:27 -0400
Subject: [PATCH 1/2] utc time extension to chrono

---
 llvm/include/llvm/Support/Chrono.h     | 26 ++++++++
 llvm/lib/Support/Chrono.cpp            | 91 ++++++++++++++++++--------
 llvm/unittests/Support/CMakeLists.txt  |  1 +
 llvm/unittests/Support/UTFTimeTest.cpp | 42 ++++++++++++
 4 files changed, 133 insertions(+), 27 deletions(-)
 create mode 100644 llvm/unittests/Support/UTFTimeTest.cpp

diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
index 9c2bd45d2803e56..71859af7c7e4a59 100644
--- a/llvm/include/llvm/Support/Chrono.h
+++ b/llvm/include/llvm/Support/Chrono.h
@@ -33,6 +33,19 @@ namespace sys {
 template <typename D = std::chrono::nanoseconds>
 using TimePoint = std::chrono::time_point<std::chrono::system_clock, D>;
 
+// 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 <typename D = std::chrono::nanoseconds>
+using UtcTime = std::chrono::time_point<UtcClock, D>;
+
+/// Convert a std::time_t to a UtcTime
+inline UtcTime<std::chrono::seconds> toUtcTime(std::time_t T) {
+  using namespace std::chrono;
+  return UtcTime<seconds>(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<system_clock::time_point::duration>(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<system_clock, seconds>(
+      duration_cast<seconds>(TP.time_since_epoch())));
+}
+
 /// Convert a std::time_t to a TimePoint
 inline TimePoint<std::chrono::seconds>
 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<sys::TimePoint<>> {
                      StringRef Style);
 };
 
+template <> struct format_provider<sys::UtcTime<std::chrono::seconds>> {
+  static void format(const sys::UtcTime<std::chrono::seconds> &TP,
+                     llvm::raw_ostream &OS, StringRef Style);
+};
+
 namespace detail {
 template <typename Period> struct unit { static const char value[]; };
 template <typename Period> const char unit<Period>::value[] = "";
diff --git a/llvm/lib/Support/Chrono.cpp b/llvm/lib/Support/Chrono.cpp
index 859ece8f550080c..4e64f03eea224e6 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,44 +68,63 @@ raw_ostream &operator<<(raw_ostream &OS, TimePoint<> TP) {
                                .count()));
 }
 
-void format_provider<TimePoint<>>::format(const TimePoint<> &T, raw_ostream &OS,
-                                          StringRef Style) {
+template <class T>
+static void format(const T &Fractional, struct tm &LT, 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";
+  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", (long)duration_cast<milliseconds>(Fractional).count());
-          ++I;
-          continue;
-        case 'f':  // Microseconds, from Python.
-          FStream << llvm::format(
-              "%.6lu", (long)duration_cast<microseconds>(Fractional).count());
-          ++I;
-          continue;
-        case 'N':  // Nanoseconds, from date(1).
-          FStream << llvm::format(
-              "%.9lu", (long)duration_cast<nanoseconds>(Fractional).count());
-          ++I;
-          continue;
-        case '%':  // Consume %%, so %%f parses as (%%)f not %(%f)
-          FStream << "%%";
-          ++I;
-          continue;
+    if (Style[I] == '%' && Style.size() > I + 1)
+      switch (Style[I + 1]) {
+      case 'L': // Milliseconds, from Ruby.
+        FStream << llvm::format(
+            "%.3lu", (long)duration_cast<milliseconds>(Fractional).count());
+        ++I;
+        continue;
+      case 'f': // Microseconds, from Python.
+        FStream << llvm::format(
+            "%.6lu", (long)duration_cast<microseconds>(Fractional).count());
+        ++I;
+        continue;
+      case 'N': // Nanoseconds, from date(1).
+        FStream << llvm::format(
+            "%.9lu", (long)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.
+  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");
 }
 
+void format_provider<UtcTime<std::chrono::seconds>>::format(
+    const UtcTime<std::chrono::seconds> &T, raw_ostream &OS, StringRef Style) {
+  using namespace std::chrono;
+  UtcTime<seconds> Truncated =
+      UtcTime<seconds>(duration_cast<seconds>(T.time_since_epoch()));
+  auto Fractional = T - Truncated;
+  struct tm LT = getStructTMUtc(Truncated);
+  llvm::format(Fractional, LT, OS, Style);
+}
+
+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);
+  llvm::format(Fractional, LT, OS, Style);
+}
+
 } // namespace llvm
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 12f2e9959326045..064a31effaeb0fa 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
+  UTFTimeTest.cpp
   VersionTupleTest.cpp
   VirtualFileSystemTest.cpp
   WithColorTest.cpp
diff --git a/llvm/unittests/Support/UTFTimeTest.cpp b/llvm/unittests/Support/UTFTimeTest.cpp
new file mode 100644
index 000000000000000..de69bd0313455b2
--- /dev/null
+++ b/llvm/unittests/Support/UTFTimeTest.cpp
@@ -0,0 +1,42 @@
+//===- unittests/Support/UTFTestTest.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(UTFTime, convertutc) {
+  // Get the current time.
+  time_t currentTime;
+  time(&currentTime);
+
+  // 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(&currentTime);
+  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

>From 799f3e01ffecf79de6082efc7a768684a587b94b Mon Sep 17 00:00:00 2001
From: Yusra Syeda <yusra.syeda at ibm.com>
Date: Wed, 11 Oct 2023 14:46:41 -0400
Subject: [PATCH 2/2] fix formatting + typo

---
 llvm/lib/Support/Chrono.cpp                   | 46 +++++++++----------
 llvm/unittests/Support/CMakeLists.txt         |  2 +-
 .../{UTFTimeTest.cpp => UTCTimeTest.cpp}      |  5 +-
 3 files changed, 25 insertions(+), 28 deletions(-)
 rename llvm/unittests/Support/{UTFTimeTest.cpp => UTCTimeTest.cpp} (92%)

diff --git a/llvm/lib/Support/Chrono.cpp b/llvm/lib/Support/Chrono.cpp
index 4e64f03eea224e6..993d200675fe572 100644
--- a/llvm/lib/Support/Chrono.cpp
+++ b/llvm/lib/Support/Chrono.cpp
@@ -73,37 +73,35 @@ static void format(const T &Fractional, struct tm &LT, raw_ostream &OS,
                    StringRef Style) {
   using namespace std::chrono;
   // Handle extensions first. strftime mangles unknown %x on some platforms.
-  if (Style.empty())
-    Style = "%Y-%m-%d %H:%M:%S.%N";
+  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", (long)duration_cast<milliseconds>(Fractional).count());
-        ++I;
-        continue;
-      case 'f': // Microseconds, from Python.
-        FStream << llvm::format(
-            "%.6lu", (long)duration_cast<microseconds>(Fractional).count());
-        ++I;
-        continue;
-      case 'N': // Nanoseconds, from date(1).
-        FStream << llvm::format(
-            "%.9lu", (long)duration_cast<nanoseconds>(Fractional).count());
-        ++I;
-        continue;
-      case '%': // Consume %%, so %%f parses as (%%)f not %(%f)
-        FStream << "%%";
-        ++I;
-        continue;
+    if (Style[I] == '%' && Style.size() > I + 1) switch (Style[I + 1]) {
+        case 'L':  // Milliseconds, from Ruby.
+          FStream << llvm::format(
+              "%.3lu", (long)duration_cast<milliseconds>(Fractional).count());
+          ++I;
+          continue;
+        case 'f':  // Microseconds, from Python.
+          FStream << llvm::format(
+              "%.6lu", (long)duration_cast<microseconds>(Fractional).count());
+          ++I;
+          continue;
+        case 'N':  // Nanoseconds, from date(1).
+          FStream << llvm::format(
+              "%.9lu", (long)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.
+  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");
 }
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 064a31effaeb0fa..5baece87d3ee101 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -90,7 +90,7 @@ add_llvm_unittest(SupportTests
   TypeTraitsTest.cpp
   TrailingObjectsTest.cpp
   UnicodeTest.cpp
-  UTFTimeTest.cpp
+  UTCTimeTest.cpp
   VersionTupleTest.cpp
   VirtualFileSystemTest.cpp
   WithColorTest.cpp
diff --git a/llvm/unittests/Support/UTFTimeTest.cpp b/llvm/unittests/Support/UTCTimeTest.cpp
similarity index 92%
rename from llvm/unittests/Support/UTFTimeTest.cpp
rename to llvm/unittests/Support/UTCTimeTest.cpp
index de69bd0313455b2..64e04d29376c3af 100644
--- a/llvm/unittests/Support/UTFTimeTest.cpp
+++ b/llvm/unittests/Support/UTCTimeTest.cpp
@@ -1,4 +1,4 @@
-//===- unittests/Support/UTFTestTest.cpp ----------------- ----------------===//
+//===- 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.
@@ -16,7 +16,7 @@ namespace llvm {
 namespace sys {
 namespace {
 
-TEST(UTFTime, convertutc) {
+TEST(UTCTime, convertutc) {
   // Get the current time.
   time_t currentTime;
   time(&currentTime);
@@ -26,7 +26,6 @@ TEST(UTFTime, convertutc) {
   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(&currentTime);



More information about the llvm-commits mailing list