On Thu, Nov 20, 2025 at 6:37 PM Jonathan Wakely <[email protected]> wrote:

> On Thu, 20 Nov 2025 at 14:28 +0100, Tomasz Kamiński wrote:
> >This patch implements P2592R3 Hashing support for std::chrono value
> classes.
> >
> >To avoid the know issues with current hashing of integer types (see
> PR104945),
> >we use chrono::__int_hash function that hash the bytes of representation,
> >instead of hash<T>, as the later simply cast to value. Currently
> _Hash_impl
> >it used, but we should consider replacing it (see PR55815) before C++26
> ABI
> >is made stable. The function is declared inside <chrono> header and chrono
> >namespace, to make sure that only chrono components would be affected by
> >such change. Finally, chrono::__int_hash is made variadic, to support
> >combining hashes of multiple integers.
> >
> >To reduce the number of calls to hasher (defined out of line), the
> calendar
> >types are packed into single unsigned integer value. This is done by
> >chrono::__hash helper, that calls:
> >* chrono::__as_int to cast the value of single component, to unsigned
> integer
> >  with size matching the one used by internal representation: unsigned
> short
> >  for year/weekday_indexed, and unsigned char in all other cases.
> >* chrono::__pack_ints to pack integers (if more than one) into single
> integer
> >  by performing bit shift operations
> >* chrono::__int_hash to hash the value produced by above.
> >
> >For type chrono::duration, __int_hash is used when the representation is
> >integral type, and for other types (floating point due special handling
> >of +/-0.0 and user defined types) we delegate to hash specialization.
> >This is automatically picked up by time_point, that delegates to hasher
> >of duration. Similarly for leap_second that is specified to use integer
> >durations, we simply hash representations of date() and value(). Finally
> >zoned_time in addition to handling integer durations as described above,
> >we also use __int_hash for const time_zone* (if used), as hash<T*> have
> >similar problems as hash specialization for integers. This is limited
> >only to _TimeZonePtr being const time_zone* (default), as user can define
> >hash specializations for raw pointers to they zones.
> >
> >As accessing the representation for duration requires calling count()
> >method that returns a copy of representation by value, the noexcept
> >specification of the hasher needs to take into consideration copy
> >constructor of duration. Similar reasoning applies for time_since_epoch
> >for time_points, and get_sys_time, get_time_zone for zoned_time.
> >For all this cases we use internal __is_nothrow_copy_hashable concept.
> >
> >Finally support for zoned_time is provided only for CXX11 string ABI,
> >__cpp_lib_chrono feature test macro cannot be bumped if COW string are
> used.
> >To indicate presence of hasher for remaining types this patch also bumps
> >the internal __glibcxx_chrono_cxx20 macro, and uses it as guard to new
> >features.
> >
> >libstdc++-v3/ChangeLog:
> >
> >       * include/bits/version.def (chrono, chrono_cxx20): Bump values.
> >       * include/bits/version.h: Regenerate.
> >       * include/std/chrono (__is_nothrow_copy_hashable)
> >       (chrono::__pack_ints, chrono::__as_int, chrono::__int_hash)
> >       (chrono::__hash): Define.
> >       (std::hash): Define partial specialization for duration,
> time_point,
> >       and zoned_time, and full specializations for calendar types and
> >       leap_second.
> >       (std::__is_fast_hash): Define partial specializations for duration,
> >       time_point, zoned_time.
> >       * testsuite/std/time/hash.cc: New test.
> >
> >Co-authored-by: Giuseppe D'Angelo <[email protected]>
> >Signed-off-by: Tomasz Kamiński <[email protected]>
> >Signed-off-by: Giuseppe D'Angelo <[email protected]>
> >---
> >v3 reimplements __pack_ints so the size of produced type,
> >is not dependend on order of arguments.
> >
> >Tested on x86_64-linux. Additionally std/time/hash.cc tested with
> >all standard modes and both string ABIs. OK for trunk?
> >
> > libstdc++-v3/include/bits/version.def   |  13 +-
> > libstdc++-v3/include/bits/version.h     |  13 +-
> > libstdc++-v3/include/std/chrono         | 328 ++++++++++++++++++++++++
> > libstdc++-v3/testsuite/std/time/hash.cc | 281 ++++++++++++++++++++
> > 4 files changed, 632 insertions(+), 3 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/std/time/hash.cc
> >
> >diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> >index 29ecf15c7e3..1fde9eef9d3 100644
> >--- a/libstdc++-v3/include/bits/version.def
> >+++ b/libstdc++-v3/include/bits/version.def
> >@@ -593,6 +593,12 @@ ftms = {
> >
> > ftms = {
> >   name = chrono;
> >+  values = {
> >+    v = 202306;
> >+    cxxmin = 26;
> >+    hosted = yes;
> >+    cxx11abi = yes;
> >+  };
> >   values = {
> >     v = 201907;
> >     cxxmin = 20;
> >@@ -607,8 +613,13 @@ ftms = {
> > };
> >
> > ftms = {
> >-  // Unofficial macro for C++20 chrono features supported for old string
> ABI.
> >+  // Unofficial macro for chrono features supported for old string ABI.
> >   name = chrono_cxx20;
> >+  values = {
> >+    v = 202306;
> >+    cxxmin = 26;
> >+    no_stdname = yes;
> >+  };
> >   values = {
> >     v = 201800;
> >     cxxmin = 20;
> >diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> >index 5901d27113d..2ebc48b234b 100644
> >--- a/libstdc++-v3/include/bits/version.h
> >+++ b/libstdc++-v3/include/bits/version.h
> >@@ -666,7 +666,12 @@
> > #undef __glibcxx_want_boyer_moore_searcher
> >
> > #if !defined(__cpp_lib_chrono)
> >-# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> >+# if (__cplusplus >  202302L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> >+#  define __glibcxx_chrono 202306L
> >+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono)
> >+#   define __cpp_lib_chrono 202306L
> >+#  endif
> >+# elif (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> > #  define __glibcxx_chrono 201907L
> > #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono)
> > #   define __cpp_lib_chrono 201907L
> >@@ -681,7 +686,11 @@
> > #undef __glibcxx_want_chrono
> >
> > #if !defined(__cpp_lib_chrono_cxx20)
> >-# if (__cplusplus >= 202002L)
> >+# if (__cplusplus >  202302L)
> >+#  define __glibcxx_chrono_cxx20 202306L
> >+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono_cxx20)
> >+#  endif
> >+# elif (__cplusplus >= 202002L)
> > #  define __glibcxx_chrono_cxx20 201800L
> > #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_chrono_cxx20)
> > #  endif
> >diff --git a/libstdc++-v3/include/std/chrono
> b/libstdc++-v3/include/std/chrono
> >index f0207eaae8e..b25e9979254 100644
> >--- a/libstdc++-v3/include/std/chrono
> >+++ b/libstdc++-v3/include/std/chrono
> >@@ -64,6 +64,9 @@
> > # include <bits/shared_ptr.h>
> > # include <bits/unique_ptr.h>
> > #endif
> >+#if __glibcxx_chrono_cxx20 >= 202306L // C++26
> >+# include <bits/functional_hash.h>
> >+#endif
> >
> > namespace std _GLIBCXX_VISIBILITY(default)
> > {
> >@@ -3322,6 +3325,331 @@ namespace __detail
> > #endif // C++20
> >   } // namespace chrono
> >
> >+#if __glibcxx_chrono_cxx20 >= 202306 // C++26
> >+  // Hash support [time.hash]
> >+
> >+  template<typename _Tp, typename _Td = remove_cv_t<_Tp>>
> >+    concept __is_nothrow_copy_hashable = requires(const _Td& __t) {
> >+      { hash<_Td>{}(_Td(__t)) } noexcept -> same_as<size_t>;
> >+    };
> >+
> >+ namespace chrono {
> >+
> >+   template<typename _T1, typename... _Ts>
> >+     [[__gnu__::__always_inline__]]
> >+     constexpr auto
> >+     __pack_ints(_T1 __v1, _Ts... __vs)
> >+     {
> >+       using _ResT = decltype([] {
> >+         constexpr size_t __tsize = (sizeof(_T1) + ... + sizeof(_Ts));
> >+       if constexpr (__tsize <= 1)
> >+         return static_cast<unsigned char>(0);
> >+       else if constexpr (__tsize <= 2)
> >+         return static_cast<__UINT16_TYPE__>(0);
> >+       else if constexpr (__tsize <= 4)
> >+         return static_cast<__UINT32_TYPE__>(0);
> >+       else if constexpr (__tsize <= 8)
> >+         return static_cast<__UINT64_TYPE__>(0);
> >+       else
> >+         static_assert(__tsize <= 8);
> >+       }());
>
> Does this mean std::chrono::duration<__int128> can't be hashed?
>
No, durations representations are not packed, they go directly to
__int_hash,
and not chrono::__hash
There would be no point of doing so. We pack only calendar types.

>
> >+
> >+       _ResT __res = __v1;
> >+       ((__res = (__res <= (sizeof(_Ts) * __CHAR_BIT__) | _ResT(__vs))),
> ...);
> >+       return __res;
> >+     }
> >+
> >+   template<typename _Tp>
> >+     [[__gnu__::__always_inline__]]
> >+     constexpr auto
> >+     __as_int(_Tp __val)
> >+     {
> >+       if constexpr (is_same_v<_Tp, year>)
> >+       return static_cast<unsigned short>(static_cast<int>(__val));
> >+       else if constexpr (is_same_v<_Tp, month> || is_same_v<_Tp, day>)
> >+       return static_cast<unsigned char>(static_cast<unsigned>(__val));
> >+       else if constexpr (is_same_v<_Tp, weekday>)
> >+       return static_cast<unsigned char>(__val.c_encoding());
> >+       else if constexpr (is_same_v<_Tp, weekday_indexed>)
> >+       return __pack_ints(chrono::__as_int(__val.weekday()),
> >+                          static_cast<unsigned char>(__val.index()));
> >+       else if constexpr (is_same_v<_Tp, weekday_last>)
> >+       return chrono::__as_int(__val.weekday());
> >+       else
> >+       static_assert(false);
> >+     }
> >+
> >+   template<typename _Arg, typename... _Args>
> >+     size_t
>
> Is this intentionally not 'inline' ?
>
Yes,  other functions are very thin wrappers over value, this one contains
out of
line calls.

>
> >+     __int_hash(_Arg __arg, _Args... __args)
> >+     {
> >+       static_assert((is_integral_v<_Arg> && ... &&
> is_integral_v<_Args>));
> >+
> >+       // TODO consider using a better quality hasher
> >+       using _Hasher = _Hash_impl;
> >+       size_t __result = _Hasher::hash(__arg);
> >+       ((__result = _Hasher::__hash_combine(__args, __result)), ...);
> >+       return __result;
> >+     }
> >+
> >+   template<typename... _Tps>
> >+     [[__gnu__::__always_inline__]]
> >+     inline size_t
> >+     __hash(_Tps... __vals)
> >+     {
> >+       if constexpr (sizeof...(_Tps) == 1)
> >+       return chrono::__int_hash(chrono::__as_int(__vals)...);
> >+       else
> >+       {
> >+         auto __packed =
> chrono::__pack_ints(chrono::__as_int(__vals)...);
> >+         return chrono::__int_hash(__packed);
> >+       }
> >+     }
> >+ } // namespace chrono
> >+
> >+  // duration
> >+  template<typename _Rep, typename _Period>
> >+    requires __is_hash_enabled_for<remove_cv_t<_Rep>>
> >+    struct hash<chrono::duration<_Rep, _Period>>
> >+    {
> >+    private:
> >+      using _ActualRep = remove_cv_t<_Rep>;
>
> Do we allow duration<const int> ?!?
>
Giuseppe added them in the test, and they complied, so I guess we do.

>
> >+
> >+    public:
> >+      size_t
> >+      operator()(const chrono::duration<_Rep, _Period>& __val) const
> >+      noexcept(__is_nothrow_copy_hashable<_Rep>)
> >+      {
> >+      if constexpr (is_integral_v<_ActualRep>)
> >+        return chrono::__int_hash(__val.count());
> >+      else
> >+        return hash<_ActualRep>{}(__val.count());
> >+      }
>
> This definition means that 1s and 1ms and 1ns will hash to the same
> value. That's not necessarily a problem, but could be surprising.
>
> I think we did discuss it with Giuseppe, and I guess we decided not to
> hash the period, since that's what his original patches did? I think
> since you can't store 1ms and 1ns in the same std::unordered_xxx
> container, it doesn't matter. You'd need to convert them to a common
> type, and then 1'000'000ns and 1ns would hash to different values.
>
> If we wanted to hash the period, it would be quite easy, as we could
> just combine val.count() and Period::type::num and Period::type::den.
> Then people might be surprised that 1s and 1000ms don't hash the same
> though.
>
> So I think not hashing the period is probably the right answer.
>
> >+    };
> >+
> >+  template<typename _Rep, typename _Period>
> >+    struct __is_fast_hash<hash<chrono::duration<_Rep, _Period>>>
> >+    : __is_fast_hash<hash<remove_cv_t<_Rep>>>
> >+    {};
> >+
> >+  // time_point
> >+  template<typename _Clock, typename _Dur>
> >+    requires __is_hash_enabled_for<_Dur>
> >+    struct hash<chrono::time_point<_Clock, _Dur>>
> >+    {
> >+      size_t
> >+      operator()(const chrono::time_point<_Clock, _Dur>& __val) const
> >+      noexcept(__is_nothrow_copy_hashable<_Dur>)
> >+      { return hash<_Dur>{}(__val.time_since_epoch()); }
> >+    };
> >+
> >+  template<typename _Clock, typename _Dur>
> >+    struct __is_fast_hash<hash<chrono::time_point<_Clock, _Dur>>>
> >+    : __is_fast_hash<hash<_Dur>>
> >+    {};
> >+
> >+  // day
> >+  template<>
> >+    struct hash<chrono::day>
> >+    {
> >+      size_t
> >+      operator()(chrono::day __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // month
> >+  template<>
> >+    struct hash<chrono::month>
> >+    {
> >+      size_t
> >+      operator()(chrono::month __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // year
> >+  template<>
> >+    struct hash<chrono::year>
> >+    {
> >+      size_t
> >+      operator()(chrono::year __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // weekday
> >+  template<>
> >+    struct hash<chrono::weekday>
> >+    {
> >+      size_t
> >+      operator()(chrono::weekday __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // weekday_indexed
> >+  template<>
> >+    struct hash<chrono::weekday_indexed>
> >+    {
> >+      size_t
> >+      operator()(chrono::weekday_indexed __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // weekday_last
> >+  template<>
> >+    struct hash<chrono::weekday_last>
> >+    {
> >+      size_t
> >+      operator()(chrono::weekday_last __val) const noexcept
> >+      { return chrono::__hash(__val); }
> >+    };
> >+
> >+  // month_day
> >+  template<>
> >+    struct hash<chrono::month_day>
> >+    {
> >+      size_t
> >+      operator()(chrono::month_day __val) const noexcept
> >+      { return chrono::__hash(__val.month(), __val.day()); }
> >+    };
> >+
> >+  // month_day_last
> >+  template<>
> >+    struct hash<chrono::month_day_last>
> >+    {
> >+      size_t operator()(chrono::month_day_last __val) const noexcept
> >+      { return chrono::__hash(__val.month()); }
> >+    };
> >+
> >+  // month_weekday
> >+  template<>
> >+    struct hash<chrono::month_weekday>
> >+    {
> >+      size_t
> >+      operator()(chrono::month_weekday __val) const noexcept
> >+      { return chrono::__hash(__val.month(), __val.weekday_indexed()); }
> >+    };
> >+
> >+  // month_weekday_last
> >+  template<>
> >+    struct hash<chrono::month_weekday_last>
> >+    {
> >+      size_t
> >+      operator()(chrono::month_weekday_last __val) const noexcept
> >+      { return chrono::__hash(__val.month(), __val.weekday_last()); }
> >+    };
> >+
> >+  // year_month
> >+  template<>
> >+    struct hash<chrono::year_month>
> >+    {
> >+      size_t
> >+      operator()(chrono::year_month __val) const noexcept
> >+      { return chrono::__hash(__val.year(), __val.month()); }
> >+    };
> >+
> >+  // year_month_day
> >+  template<>
> >+    struct hash<chrono::year_month_day>
> >+    {
> >+      size_t
> >+      operator()(chrono::year_month_day __val) const noexcept
> >+      { return chrono::__hash(__val.year(), __val.month(), __val.day());
> }
> >+    };
> >+
> >+  // year_month_day_last
> >+  template<>
> >+    struct hash<chrono::year_month_day_last>
> >+    {
> >+      size_t
> >+      operator()(chrono::year_month_day_last __val) const noexcept
> >+      { return chrono::__hash(__val.year(), __val.month()); }
> >+    };
> >+
> >+  // year_month_weekday
> >+  template<>
> >+    struct hash<chrono::year_month_weekday>
> >+    {
> >+      size_t
> >+      operator()(chrono::year_month_weekday __val) const noexcept
> >+      {
> >+      return chrono::__hash(__val.year(), __val.month(),
> >+                            __val.weekday_indexed());
> >+      }
> >+    };
> >+
> >+  // year_month_weekday_last
> >+  template<>
> >+    struct hash<chrono::year_month_weekday_last>
> >+    {
> >+      size_t
> >+      operator()(chrono::year_month_weekday_last __val) const noexcept
> >+      {
> >+      return chrono::__hash(__val.year(), __val.month(),
> >+                            __val.weekday_last());
> >+      }
> >+    };
> >+
> >+#if _GLIBCXX_HOSTED
> >+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> >+  // zoned_time
> >+  template<typename _Duration, typename _TimeZonePtr>
> >+    requires __is_hash_enabled_for<
> >+             typename chrono::zoned_time<_Duration,
> _TimeZonePtr>::duration>
> >+           && __is_hash_enabled_for<_TimeZonePtr>
> >+    struct hash<chrono::zoned_time<_Duration, _TimeZonePtr>>
> >+    {
> >+    private:
> >+      using _ActualDuration =
> >+      typename chrono::zoned_time<_Duration, _TimeZonePtr>::duration;
> >+
> >+    public:
> >+      size_t
> >+      operator()(const chrono::zoned_time<_Duration, _TimeZonePtr>&
> __val) const
> >+      noexcept(__is_nothrow_copy_hashable<_ActualDuration>
> >+             && __is_nothrow_copy_hashable<_TimeZonePtr>)
> >+      {
> >+      const auto __iduration = [&] {
> >+        const _ActualDuration __sd =
> __val.get_sys_time().time_since_epoch();
> >+        if constexpr (is_integral_v<typename _ActualDuration::rep>)
> >+          return __sd.count();
> >+        else
> >+          return hash<_ActualDuration>{}(__sd);
> >+      }();
> >+
> >+      const auto __izone = [&] {
> >+        const _TimeZonePtr __tz = __val.get_time_zone();
> >+        if constexpr (is_same_v<_TimeZonePtr, const chrono::time_zone*>)
> >+          return reinterpret_cast<uintptr_t>(__tz);
> >+        else
> >+          return hash<_TimeZonePtr>{}(__tz);
> >+      }();
> >+
> >+      return chrono::__int_hash(__iduration, __izone);
> >+      }
> >+    };
> >+
> >+  template<typename _Duration, typename _TimeZonePtr>
> >+    struct __is_fast_hash<hash<chrono::zoned_time<_Duration,
> _TimeZonePtr>>>
> >+    : __and_<__is_fast_hash<hash<
> >+             typename chrono::zoned_time<_Duration,
> _TimeZonePtr>::duration>>,
> >+           __is_fast_hash<hash<_TimeZonePtr>>>
> >+    {};
> >+
> >+  // leap_second
> >+  template<>
> >+    struct hash<chrono::leap_second>
> >+    {
> >+      size_t
> >+      operator()(chrono::leap_second __val) const noexcept
> >+      {
> >+      return chrono::__int_hash(
> >+               __val.date().time_since_epoch().count(),
> >+               __val.value().count());
> >+      }
> >+    };
> >+#endif // _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> >+#endif // _GLIBCXX_HOSTED
> >+#endif // __glibcxx_chrono_cxx20 >= 202306
> >+
> > #ifdef __glibcxx_chrono_cxx20
> >   inline namespace literals
> >   {
> >diff --git a/libstdc++-v3/testsuite/std/time/hash.cc
> b/libstdc++-v3/testsuite/std/time/hash.cc
> >new file mode 100644
> >index 00000000000..8d3b54c0931
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/time/hash.cc
> >@@ -0,0 +1,281 @@
> >+// { dg-do run { target c++26 } }
> >+
> >+#include <chrono>
> >+#include <unordered_set>
> >+#include <limits.h>
> >+#include <testsuite_hooks.h>
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+#  if !defined(__cpp_lib_chrono)
> >+#    error "__cpp_lib_chrono not defined"
> >+#  elif __cpp_lib_chrono < 202306L
> >+#    error "Wrong value for __cpp_lib_chrono"
> >+#  endif
> >+#endif
> >+
> >+template <typename T>
> >+struct arithmetic_wrapper
> >+{
> >+  arithmetic_wrapper() = default;
> >+  arithmetic_wrapper(T t) : t(t) {}
> >+  friend bool operator==(arithmetic_wrapper, arithmetic_wrapper) =
> default;
> >+  T t;
> >+};
> >+
> >+template <typename T>
> >+struct std::hash<arithmetic_wrapper<T>>
> >+{
> >+  size_t operator()(arithmetic_wrapper<T> val) const noexcept
> >+  { return std::hash<T>{}(val.t); }
> >+};
> >+
> >+template <typename T>
> >+struct non_hashable_arithmetic_wrapper
> >+{
> >+  non_hashable_arithmetic_wrapper() = default;
> >+  non_hashable_arithmetic_wrapper(T t) : t(t) {}
> >+  friend bool operator==(non_hashable_arithmetic_wrapper,
> non_hashable_arithmetic_wrapper) = default;
> >+  T t;
> >+};
> >+
> >+template <typename T>
> >+constexpr bool is_hash_poisoned =
> !std::is_default_constructible_v<std::hash<T>>;
> >+
> >+template <typename T>
> >+void test_unordered_set(const T& t)
> >+{
> >+  std::unordered_set<T> set;
> >+
> >+  set.insert(t);
> >+  VERIFY(set.size() == 1);
> >+  VERIFY(set.contains(t));
> >+
> >+  set.erase(t);
> >+  VERIFY(set.size() == 0);
> >+  VERIFY(!set.contains(t));
> >+}
> >+
> >+template <typename T>
> >+void test_hash(const T& t)
> >+{
> >+  static_assert(noexcept(std::hash<T>{}(t)));
> >+  static_assert(std::__is_fast_hash<T>::value);
> >+  test_unordered_set(t);
> >+}
> >+
> >+void test01()
> >+{
> >+  using namespace std::chrono;
> >+  using namespace std::literals::chrono_literals;
> >+
> >+  // duration
> >+  test_hash(-999s);
> >+  test_hash(1234ms);
> >+  test_hash(duration<double>(123.45));
> >+  using AWint = arithmetic_wrapper<int>;
> >+  test_hash(duration<AWint>(AWint(1234)));
> >+  using AWdouble = arithmetic_wrapper<double>;
> >+  test_hash(duration<AWdouble>(AWdouble(123.45)));
> >+
> >+  // time_point
> >+  test_hash(sys_seconds(1234s));
> >+  test_hash(sys_time<duration<double>>(duration<double>(123.45)));
> >+  test_hash(utc_seconds(1234s));
> >+  test_hash(local_days(days(1234)));
> >+  test_hash(system_clock::now());
> >+  test_hash(steady_clock::now());
> >+  test_hash(utc_clock::now());
> >+  test_hash(gps_clock::now());
> >+
> >+  // day
> >+  test_hash(1d);
> >+  test_hash(0d);
> >+  test_hash(255d);
> >+  test_hash(1234d);
> >+  test_hash(day(UINT_MAX));
> >+
> >+  // month
> >+  test_hash(January);
> >+  test_hash(September);
> >+  test_hash(month(0u));
> >+  test_hash(month(255u));
> >+  test_hash(month(1234u));
> >+  test_hash(month(UINT_MAX));
> >+
> >+  // year
> >+  test_hash(2024y);
> >+  test_hash(0y);
> >+  test_hash(year::min());
> >+  test_hash(year::max());
> >+  test_hash(year(INT_MAX));
> >+  test_hash(year(INT_MIN));
> >+
> >+  // weekday
> >+  test_hash(Monday);
> >+  test_hash(Thursday);
> >+  test_hash(weekday(255u));
> >+  test_hash(weekday(UINT_MAX));
> >+
> >+  // weekday_indexed
> >+  test_hash(Monday[0u]);
> >+  test_hash(Monday[7u]);
> >+  test_hash(Monday[1234u]);
> >+  test_hash(weekday(1234u)[0u]);
> >+
> >+  // weekday_last
> >+  test_hash(Monday[last]);
> >+  test_hash(Friday[last]);
> >+  test_hash(weekday(1234u)[last]);
> >+
> >+  // month_day
> >+  test_hash(March / 3);
> >+  test_hash(March / 31);
> >+  test_hash(February / 31);
> >+  test_hash(February / 1234);
> >+  test_hash(month(1234u) / 1);
> >+
> >+  // month_day_last
> >+  test_hash(March / last);
> >+  test_hash(month(1234u) / last);
> >+
> >+  // month_weekday
> >+  test_hash(March / Tuesday[2u]);
> >+  test_hash(month(1234u) / Tuesday[2u]);
> >+  test_hash(March / weekday(1234u)[2u]);
> >+  test_hash(March / Tuesday[1234u]);
> >+
> >+  // month_weekday_last
> >+  test_hash(April / Sunday[last]);
> >+  test_hash(month(1234u) / Tuesday[last]);
> >+  test_hash(April / weekday(1234u)[last]);
> >+
> >+  // year_month
> >+  test_hash(2024y / August);
> >+  test_hash(1'000'000y / August);
> >+  test_hash(2024y / month(1234u));
> >+
> >+  // year_month_day
> >+  test_hash(2024y / August / 31);
> >+  test_hash(-10y / March / 5);
> >+  test_hash(2024y / February / 31);
> >+  test_hash(1'000'000y / March / 5);
> >+  test_hash(2024y / month(1234u) / 5);
> >+  test_hash(2024y / March / 1234);
> >+
> >+  // year_month_day_last
> >+  test_hash(2024y / August / last);
> >+  test_hash(1'000'000y / August / last);
> >+  test_hash(2024y / month(1234u) / last);
> >+
> >+  // year_month_weekday
> >+  test_hash(2024y / August / Tuesday[2u]);
> >+  test_hash(-10y / August / Tuesday[2u]);
> >+  test_hash(1'000'000y / August / Tuesday[2u]);
> >+  test_hash(2024y / month(1234u) / Tuesday[2u]);
> >+  test_hash(2024y / August / weekday(1234u)[2u]);
> >+  test_hash(2024y / August / Tuesday[1234u]);
> >+
> >+  // year_month_weekday_last
> >+  test_hash(2024y / August / Tuesday[last]);
> >+  test_hash(-10y / August / Tuesday[last]);
> >+  test_hash(1'000'000y / August / Tuesday[last]);
> >+  test_hash(2024y / month(1234u) / Tuesday[last]);
> >+  test_hash(2024y / August / weekday(1234u)[last]);
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+  // zoned_time
> >+  test_hash(zoned_seconds("Europe/Rome", sys_seconds(1234s)));
> >+  test_hash(zoned_time("Europe/Rome", system_clock::now()));
> >+
> >+  // leap_second
> >+  for (leap_second l : get_tzdb().leap_seconds)
> >+    test_hash(l);
> >+#endif
> >+}
> >+
> >+void test02()
> >+{
> >+  using namespace std::chrono;
> >+  using namespace std::literals::chrono_literals;
> >+
> >+  {
> >+    std::unordered_set<milliseconds> set;
> >+    set.insert(2000ms);
> >+    VERIFY(set.contains(2000ms));
> >+    VERIFY(set.contains(2s));
> >+    VERIFY(!set.contains(1234ms));
> >+    VERIFY(!set.contains(1234s));
> >+  }
> >+  {
> >+    using TP = sys_time<milliseconds>;
> >+    std::unordered_set<TP> set;
> >+    set.insert(TP(2000ms));
> >+    VERIFY(set.contains(TP(2000ms)));
> >+    VERIFY(set.contains(sys_seconds(2s)));
> >+    VERIFY(!set.contains(TP(1234ms)));
> >+    VERIFY(!set.contains(sys_seconds(1234s)));
> >+  }
> >+}
> >+
> >+void test03()
> >+{
> >+  using namespace std::chrono;
> >+
> >+  static constexpr
> >+  auto test_hash = []<typename T>(const T& t)
> >+  {
> >+    static_assert(noexcept(std::hash<T>{}(t)));
> >+  };
> >+
> >+  static constexpr
> >+  auto test = []<typename D>(const D& d)
> >+  {
> >+    test_hash(d);
> >+    test_hash(sys_time<D>(d));
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+    test_hash(zoned_time<D>(sys_time<D>(d)));
> >+#endif
> >+  };
> >+
> >+
> >+  test(duration<int>(123));
> >+  test(duration<int, std::ratio<1, 1000>>(123));
> >+  test(duration<int, std::ratio<1000, 1>>(123));
> >+  test(duration<volatile double>(123.456));
> >+  test(duration<arithmetic_wrapper<int>>(arithmetic_wrapper<int>(123)));
> >+
> >+  test(duration<const int>(123));
> >+  test(duration<const int, std::ratio<1, 1000>>(123));
> >+  test(duration<const int, std::ratio<1000, 1>>(123));
> >+  test(duration<const volatile double>(123.456));
> >+  test(duration<const
> arithmetic_wrapper<int>>(arithmetic_wrapper<int>(123)));
> >+}
> >+
> >+void test04()
> >+{
> >+  using namespace std::chrono;
> >+
> >+  static_assert(!is_hash_poisoned<duration<int>>);
> >+  static_assert(!is_hash_poisoned<duration<double>>);
> >+  static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<int>>>);
> >+  static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<double>>>);
> >+
> static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<int>>>);
> >+
> static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<double>>>);
> >+
> >+#if _GLIBCXX_USE_CXX11_ABI
> >+  static_assert(!is_hash_poisoned<zoned_time<duration<int>>>);
> >+  static_assert(!is_hash_poisoned<zoned_time<duration<double>>>);
> >+
> static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<int>>>>);
> >+
> static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<double>>>>);
> >+
> static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<int>>>>);
> >+
> static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<double>>>>);
> >+#endif
> >+}
> >+
> >+int main()
> >+{
> >+  test01();
> >+  test02();
> >+  test03();
> >+  test04();
> >+}
> >--
> >2.51.1
> >
> >
>
>

Reply via email to