[libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274: Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR #209686)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 24 04:06:44 PDT 2026


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/2] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/2] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {



More information about the libcxx-commits mailing list