[flang] [llvm] [flang-rt] Add implementation for date_and_time on Windows (PR #190174)

David Truby via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 08:24:27 PDT 2026


https://github.com/DavidTruby updated https://github.com/llvm/llvm-project/pull/190174

>From 5eca5d7147167c2e904ad172eb9b71ceb33fe132 Mon Sep 17 00:00:00 2001
From: David Truby <david.truby at arm.com>
Date: Thu, 2 Apr 2026 14:10:01 +0100
Subject: [PATCH 1/4] [flang-rt] Add implementation for date_and_time on
 Windows

date_and_time currently returns -HUGE and blank strings on Windows,
which is what the standard says to do when the date and time aren't
available. This patch adds an implementation that provides the actual
values by providing implementations of the posix functions used by the
existing implementation and then piggybacking off of that
implementation.

Note: This also bumps the minimum Windows version supported by flang-rt
to Windows 10, as some of the functions used here are not available in
earlier Windows versions. Since Windows < 10 is out of support by
Microsoft themselves it should be reasonable for flang-rt to drop
support for them.
---
 flang-rt/lib/runtime/time-intrinsic.cpp      | 64 +++++++++++++++-----
 flang/include/flang/Common/windows-include.h |  6 +-
 2 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/flang-rt/lib/runtime/time-intrinsic.cpp b/flang-rt/lib/runtime/time-intrinsic.cpp
index 08f6f9b0cf681..666e3cbc6a7e0 100644
--- a/flang-rt/lib/runtime/time-intrinsic.cpp
+++ b/flang-rt/lib/runtime/time-intrinsic.cpp
@@ -302,7 +302,6 @@ static void DateAndTimeUnavailable(Fortran::runtime::Terminator &terminator,
   }
 }
 
-#ifndef _WIN32
 #ifdef _AIX
 // Compute the time difference from GMT/UTC to get around the behavior of
 // strfname on AIX that requires setting an environment variable for numeric
@@ -376,7 +375,7 @@ GetGmtOffset(const TM &tm, fallback_implementation) {
   // return -HUGE to report that this information is not available.
   const auto negHuge{-std::numeric_limits<Fortran::runtime::CppTypeFor<
       Fortran::common::TypeCategory::Integer, KIND>>::max()};
-#ifdef _AIX
+#if defined _AIX
   bool err{false};
   auto diff{computeUTCDiff(tm, &err)};
   if (err) {
@@ -384,6 +383,18 @@ GetGmtOffset(const TM &tm, fallback_implementation) {
   } else {
     return diff;
   }
+#elif defined _WIN32
+  DYNAMIC_TIME_ZONE_INFORMATION tzi;
+  std::uint32_t tzid{GetDynamicTimeZoneInformation(&tzi)};
+  if (tzid == TIME_ZONE_ID_INVALID) {
+    return negHuge;
+  }
+
+  std::int32_t bias{tzi.Bias};
+  bias += (tzid == TIME_ZONE_ID_DAYLIGHT ? tzi.DaylightBias : tzi.StandardBias);
+
+  // Bias is minutes behind GMT, and we need minuetes ahead.
+  return -bias;
 #else
   return negHuge;
 #endif
@@ -399,6 +410,41 @@ template <typename TM = struct tm> struct GmtOffsetHelper {
   };
 };
 
+#ifdef _WIN32
+struct timeval {
+  std::int64_t tv_sec;
+  std::int64_t tv_usec;
+};
+
+// gettimeofday half-implementation for win32; ignore the timezone as we don't
+// use it anyway
+int gettimeofday(timeval *tv, void *) {
+  constexpr std::uint64_t epoch_offset{116444736000000000ull};
+
+  FILETIME ftime;
+  GetSystemTimePreciseAsFileTime(&ftime);
+
+  // Convert ftime to a real 64-bit integer
+  std::uint64_t time{
+      ULARGE_INTEGER{{ftime.dwHighDateTime, ftime.dwHighDateTime}}.QuadPart};
+  // Convert to Unix epoch time
+  time -= epoch_offset;
+
+  auto [sec, usec] = std::lldiv(time, 10000000L);
+  tv->tv_sec = sec;
+  tv->tv_usec = usec;
+  return 0;
+}
+
+// localtime_s on Windows does the same thing as localtime_r but swaps the
+// arguments
+struct tm *localtime_r(const time_t *timer, struct tm *buf) {
+  _localtime64_s(buf, timer);
+  return buf;
+}
+
+#endif
+
 // Dispatch to posix implementation where gettimeofday and localtime_r are
 // available.
 static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
@@ -470,20 +516,6 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
     storeIntegerAt(7, ms);
   }
 }
-
-#else
-// Fallback implementation where gettimeofday or localtime_r are not both
-// available (e.g. windows).
-static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
-    std::size_t dateChars, char *time, std::size_t timeChars, char *zone,
-    std::size_t zoneChars, const Fortran::runtime::Descriptor *values) {
-  // TODO: An actual implementation for non Posix system should be added.
-  // So far, implement as if the date and time is not available on those
-  // platforms.
-  DateAndTimeUnavailable(
-      terminator, date, dateChars, time, timeChars, zone, zoneChars, values);
-}
-#endif
 } // namespace
 
 namespace Fortran::runtime {
diff --git a/flang/include/flang/Common/windows-include.h b/flang/include/flang/Common/windows-include.h
index 1263768e97339..a8d279ba63f68 100644
--- a/flang/include/flang/Common/windows-include.h
+++ b/flang/include/flang/Common/windows-include.h
@@ -18,10 +18,10 @@
 #define WIN32_LEAN_AND_MEAN
 #define NOMINMAX
 
-// Target Windows 2000 and above. This is needed for newer Windows API
-// functions, e.g. GetComputerNameExA()
+// Target Windows 10 and above. This is needed for newer Windows API
+// functions, e.g. GetComputerNameExA(), GetSystemTimePreciseAsFileTime()
 #ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0500
+#define _WIN32_WINNT 0x0A00
 #endif
 
 #include <windows.h>

>From bf2d9052bf4e7d16d61f00647381f8e67db268fe Mon Sep 17 00:00:00 2001
From: David Truby <david.truby at arm.com>
Date: Thu, 2 Apr 2026 15:38:58 +0100
Subject: [PATCH 2/4] Fix double usage of dwHighDateTime

Co-authored-by: Tom Eccles <tom.eccles at arm.com>
---
 flang-rt/lib/runtime/time-intrinsic.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang-rt/lib/runtime/time-intrinsic.cpp b/flang-rt/lib/runtime/time-intrinsic.cpp
index 666e3cbc6a7e0..833f8dda1f528 100644
--- a/flang-rt/lib/runtime/time-intrinsic.cpp
+++ b/flang-rt/lib/runtime/time-intrinsic.cpp
@@ -426,7 +426,7 @@ int gettimeofday(timeval *tv, void *) {
 
   // Convert ftime to a real 64-bit integer
   std::uint64_t time{
-      ULARGE_INTEGER{{ftime.dwHighDateTime, ftime.dwHighDateTime}}.QuadPart};
+      ULARGE_INTEGER{{ftime.dwLowDateTime, ftime.dwHighDateTime}}.QuadPart};
   // Convert to Unix epoch time
   time -= epoch_offset;
 

>From 5092d5c2acbc1105ec2bb80287ff74b21d32ecb8 Mon Sep 17 00:00:00 2001
From: David Truby <david.truby at arm.com>
Date: Thu, 2 Apr 2026 16:20:14 +0100
Subject: [PATCH 3/4] Add digit separator to divisor for clarity

---
 flang-rt/lib/runtime/time-intrinsic.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang-rt/lib/runtime/time-intrinsic.cpp b/flang-rt/lib/runtime/time-intrinsic.cpp
index 833f8dda1f528..8a69b4481b6e6 100644
--- a/flang-rt/lib/runtime/time-intrinsic.cpp
+++ b/flang-rt/lib/runtime/time-intrinsic.cpp
@@ -430,7 +430,7 @@ int gettimeofday(timeval *tv, void *) {
   // Convert to Unix epoch time
   time -= epoch_offset;
 
-  auto [sec, usec] = std::lldiv(time, 10000000L);
+  auto [sec, usec] = std::lldiv(time, 10'000'000l);
   tv->tv_sec = sec;
   tv->tv_usec = usec;
   return 0;

>From a064002b43203a2e3e607df8826009fb63512496 Mon Sep 17 00:00:00 2001
From: David Truby <david.truby at arm.com>
Date: Thu, 2 Apr 2026 16:23:53 +0100
Subject: [PATCH 4/4] Fixes for review

---
 flang-rt/lib/runtime/time-intrinsic.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/flang-rt/lib/runtime/time-intrinsic.cpp b/flang-rt/lib/runtime/time-intrinsic.cpp
index 8a69b4481b6e6..e72b12663ec4c 100644
--- a/flang-rt/lib/runtime/time-intrinsic.cpp
+++ b/flang-rt/lib/runtime/time-intrinsic.cpp
@@ -393,7 +393,7 @@ GetGmtOffset(const TM &tm, fallback_implementation) {
   std::int32_t bias{tzi.Bias};
   bias += (tzid == TIME_ZONE_ID_DAYLIGHT ? tzi.DaylightBias : tzi.StandardBias);
 
-  // Bias is minutes behind GMT, and we need minuetes ahead.
+  // Bias is minutes behind GMT, and we need minutes ahead.
   return -bias;
 #else
   return negHuge;
@@ -418,7 +418,7 @@ struct timeval {
 
 // gettimeofday half-implementation for win32; ignore the timezone as we don't
 // use it anyway
-int gettimeofday(timeval *tv, void *) {
+static int gettimeofday(timeval *tv, void *) {
   constexpr std::uint64_t epoch_offset{116444736000000000ull};
 
   FILETIME ftime;
@@ -438,8 +438,11 @@ int gettimeofday(timeval *tv, void *) {
 
 // localtime_s on Windows does the same thing as localtime_r but swaps the
 // arguments
-struct tm *localtime_r(const time_t *timer, struct tm *buf) {
-  _localtime64_s(buf, timer);
+static struct tm *localtime_r(const time_t *timer, struct tm *buf) {
+  errno_t ec{_localtime64_s(buf, timer)};
+  if (ec != 0) {
+    return nullptr;
+  }
   return buf;
 }
 



More information about the llvm-commits mailing list