# 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)
{
#endif // C++20
} // namespace chrono
+#if __glibcxx_chrono_cxx20 >= 202306 // C++26
+ // Hash support [time.hash]
+
+ template<typename _Tp>
+ concept __is_nothrow_copy_hashable = requires(const _Tp& __t) {
+ { hash<_Tp>{}(_Tp(__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);
+ }());
+
+ _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
+ __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<_Rep>
+ struct hash<chrono::duration<_Rep, _Period>>
+ {
+ size_t
+ operator()(const chrono::duration<_Rep, _Period>& __val) const
+ noexcept(__is_nothrow_copy_hashable<_Rep>)
+ {
+ if constexpr (is_integral_v<_Rep>)
+ return chrono::__int_hash(__val.count());
+ else
+ return hash<_Rep>{}(__val.count());
+ }
+ };
+
+ template<typename _Rep, typename _Period>
+ struct __is_fast_hash<hash<chrono::duration<_Rep, _Period>>>
+ : __is_fast_hash<hash<_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
{
--- /dev/null
+// { 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);
+#if defined __SIZEOF_INT128__
+ test_hash(duration<__int128>(123456));
+#endif
+ 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));
+#if defined __SIZEOF_INT128__
+ test_hash(sys_time<duration<__int128>>(duration<__int128>(123456)));
+#endif
+ 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<double>(123.456));
+ test(duration<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();
+}