[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
Tue Jul 14 23:20:05 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