Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Oct 12, 2023

Conversation

ysyeda
Copy link
Contributor

@ysyeda ysyeda commented Sep 29, 2023

This PR adds handling for UtcClock to chrono.h/.cpp.

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 29, 2023

@llvm/pr-subscribers-llvm-support

Changes

This PR adds handling for UtcClock to chrono.h/.cpp.


Full diff: https://github.com/llvm/llvm-project/pull/67846.diff

4 Files Affected:

  • (modified) llvm/include/llvm/Support/Chrono.h (+26)
  • (modified) llvm/lib/Support/Chrono.cpp (+64-27)
  • (modified) llvm/unittests/Support/CMakeLists.txt (+1)
  • (added) llvm/unittests/Support/UTFTimeTest.cpp (+42)
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

@github-actions
Copy link

github-actions bot commented Sep 29, 2023

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 7ebf3e019488bcc6209df757e823b08f631aa897 799f3e01ffecf79de6082efc7a768684a587b94b -- llvm/unittests/Support/UTCTimeTest.cpp llvm/include/llvm/Support/Chrono.h llvm/lib/Support/Chrono.cpp
View the diff from clang-format here.
diff --git a/llvm/unittests/Support/UTCTimeTest.cpp b/llvm/unittests/Support/UTCTimeTest.cpp
index 64e04d29376c..aa2b8716dd46 100644
--- a/llvm/unittests/Support/UTCTimeTest.cpp
+++ b/llvm/unittests/Support/UTCTimeTest.cpp
@@ -7,10 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Chrono.h"
-#include "gtest/gtest.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FormatProviders.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "gtest/gtest.h"
 
 namespace llvm {
 namespace sys {
@@ -34,7 +34,6 @@ TEST(UTCTime, convertutc) {
 
   // Compare the formatted strings.
   EXPECT_EQ(customResultString, StringRef(gmtimeResultString, 19));
-
 }
 } // namespace
 } // namespace sys

@ysyeda
Copy link
Contributor Author

ysyeda commented Oct 5, 2023

ping

Comment on lines 64 to 65
if (Style[I] == '%' && Style.size() > I + 1) switch (Style[I + 1]) {
case 'L': // Milliseconds, from Ruby.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and other parts seem to be reformatted without changes to the code? Could you remove the unrelated reformatting and/or commit it separately if there's a need?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the unrelated formatting is removed now.

@ysyeda
Copy link
Contributor Author

ysyeda commented Oct 12, 2023

ping

@dwblaikie
Copy link
Collaborator

Are there patches outstanding that'll use this in the LLVM project?

@ysyeda
Copy link
Contributor Author

ysyeda commented Oct 12, 2023

Are there patches outstanding that'll use this in the LLVM project?

@dwblaikie This patch is required for a change in the SystemZ backend for z/OS: #65407 I will post the PR once this one has been merged. Thanks for your review.

@ysyeda ysyeda merged commit 1c9035d into llvm:main Oct 12, 2023
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants