#include <bits/chrono.h>
#if __cplusplus >= 202002L
+# include <bit>
# include <sstream>
# include <string>
# include <vector>
// HH_MM_SS
+ /// @cond undocumented
namespace __detail
{
consteval long long
return __r;
}
}
+ /// @endcond
+ /** Utility for splitting a duration into hours, minutes, and seconds
+ *
+ * This is a convenience type that provides accessors for the constituent
+ * parts (hours, minutes, seconds and subseconds) of a duration.
+ *
+ * @since C++20
+ */
template<typename _Duration>
class hh_mm_ss
{
+ static_assert( __is_duration<_Duration>::value );
+
private:
- static constexpr int
+ static consteval int
_S_fractional_width()
{
- int __multiplicity_2 = 0;
- int __multiplicity_5 = 0;
auto __den = _Duration::period::den;
- while ((__den % 2) == 0)
- {
- ++__multiplicity_2;
- __den /= 2;
- }
+ const int __multiplicity_2 = std::__countr_zero((uintmax_t)__den);
+ __den >>= __multiplicity_2;
+ int __multiplicity_5 = 0;
while ((__den % 5) == 0)
{
++__multiplicity_5;
return __width;
}
+ constexpr
+ hh_mm_ss(_Duration __d, bool __is_neg) noexcept
+ : _M_h (duration_cast<chrono::hours>(__d)),
+ _M_m (duration_cast<chrono::minutes>(__d - hours())),
+ _M_s (duration_cast<chrono::seconds>(__d - hours() - minutes())),
+ _M_is_neg(__is_neg)
+ {
+ auto __ss = __d - hours() - minutes() - seconds();
+ if constexpr (treat_as_floating_point_v<typename precision::rep>)
+ _M_ss._M_r = __ss.count();
+ else if constexpr (precision::period::den != 1)
+ _M_ss._M_r = duration_cast<precision>(__ss).count();
+ }
+
public:
static constexpr unsigned fractional_width = {_S_fractional_width()};
chrono::seconds::rep>,
ratio<1, __detail::__pow10(fractional_width)>>;
- constexpr
- hh_mm_ss() noexcept
- : hh_mm_ss{_Duration::zero()}
- { }
+ constexpr hh_mm_ss() noexcept = default;
constexpr explicit
hh_mm_ss(_Duration __d) noexcept
- : _M_is_neg (__d < _Duration::zero()),
- _M_h (duration_cast<chrono::hours>(abs(__d))),
- _M_m (duration_cast<chrono::minutes>(abs(__d) - hours())),
- _M_s (duration_cast<chrono::seconds>(abs(__d) - hours() - minutes()))
- {
- if constexpr (treat_as_floating_point_v<typename precision::rep>)
- _M_ss = abs(__d) - hours() - minutes() - seconds();
- else
- _M_ss = duration_cast<precision>(abs(__d) - hours()
- - minutes() - seconds());
- }
+ : hh_mm_ss(chrono::abs(__d), __d < _Duration::zero())
+ { }
constexpr bool
is_negative() const noexcept
- { return _M_is_neg; }
+ {
+ if constexpr (!__is_unsigned)
+ return _M_is_neg;
+ else
+ return false;
+ }
constexpr chrono::hours
hours() const noexcept
constexpr precision
subseconds() const noexcept
- { return _M_ss; }
+ { return static_cast<precision>(_M_ss); }
constexpr explicit
operator precision() const noexcept
constexpr precision
to_duration() const noexcept
{
- if (_M_is_neg)
- return -(_M_h + _M_m + _M_s + _M_ss);
- else
- return _M_h + _M_m + _M_s + _M_ss;
+ if constexpr (!__is_unsigned)
+ if (_M_is_neg)
+ return -(_M_h + _M_m + _M_s + subseconds());
+ return _M_h + _M_m + _M_s + subseconds();
}
// TODO: Implement operator<<.
private:
- bool _M_is_neg;
- chrono::hours _M_h;
- chrono::minutes _M_m;
- chrono::seconds _M_s;
- precision _M_ss;
+ static constexpr bool __is_unsigned
+ = __and_v<is_integral<typename _Duration::rep>,
+ is_unsigned<typename _Duration::rep>>;
+
+ template<typename _Ratio>
+ using __byte_duration = duration<unsigned char, _Ratio>;
+
+ // The type of the _M_ss member that holds the subsecond precision.
+ template<typename _Dur>
+ struct __subseconds
+ {
+ typename _Dur::rep _M_r{};
+
+ constexpr explicit
+ operator _Dur() const noexcept
+ { return _Dur(_M_r); }
+ };
+
+ // An empty class if this precision doesn't need subseconds.
+ template<typename _Rep>
+ requires (!treat_as_floating_point_v<_Rep>)
+ struct __subseconds<duration<_Rep, ratio<1>>>
+ {
+ constexpr explicit
+ operator duration<_Rep, ratio<1>>() const noexcept
+ { return {}; }
+ };
+
+ // True if the maximum constructor argument can be represented in _Tp.
+ template<typename _Tp>
+ static constexpr bool __fits
+ = duration_values<typename _Duration::rep>::max()
+ <= duration_values<_Tp>::max();
+
+ template<typename _Rep, typename _Period>
+ requires (!treat_as_floating_point_v<_Rep>)
+ && ratio_less_v<_Period, ratio<1, 1>>
+ && (ratio_greater_equal_v<_Period, ratio<1, 250>>
+ || __fits<unsigned char>)
+ struct __subseconds<duration<_Rep, _Period>>
+ {
+ unsigned char _M_r{};
+
+ constexpr explicit
+ operator duration<_Rep, _Period>() const noexcept
+ { return duration<_Rep, _Period>(_M_r); }
+ };
+
+ template<typename _Rep, typename _Period>
+ requires (!treat_as_floating_point_v<_Rep>)
+ && ratio_less_v<_Period, ratio<1, 250>>
+ && (ratio_greater_equal_v<_Period, ratio<1, 4'000'000'000>>
+ || __fits<uint_least32_t>)
+ struct __subseconds<duration<_Rep, _Period>>
+ {
+ uint_least32_t _M_r{};
+
+ constexpr explicit
+ operator duration<_Rep, _Period>() const noexcept
+ { return duration<_Rep, _Period>(_M_r); }
+ };
+
+ chrono::hours _M_h{};
+ __byte_duration<ratio<60>> _M_m{};
+ __byte_duration<ratio<1>> _M_s{};
+ bool _M_is_neg{};
+ __subseconds<precision> _M_ss{};
};
// 12/24 HOURS FUNCTIONS
static_assert(seconds{hh_mm_ss{100min}} == 100min);
- // TODO: treat_as_floating_point_v
+ // treat_as_floating_point_v
+ using fseconds = duration<double, ratio<1>>;
+ constexpr hh_mm_ss<fseconds> fsec{0x123.0004p5s};
+ static_assert(std::is_same_v<hh_mm_ss<fseconds>::precision, fseconds>);
+ static_assert(fsec.hours() == 2h);
+ static_assert(fsec.minutes() == 35min);
+ static_assert(fsec.seconds() == 12s);
+ static_assert(fsec.subseconds() == 0x.0004p5s);
+ static_assert(!fsec.is_negative());
+ static_assert(fsec.to_duration() == 0x123.0004p5s);
+
+ using fminutes = duration<double, ratio<60>>;
+ constexpr hh_mm_ss<fminutes> fmin{-0x1.23p4min};
+ static_assert(std::is_same_v<hh_mm_ss<fminutes>::precision, fseconds>);
+ static_assert(fmin.hours() == 0h);
+ static_assert(fmin.minutes() == 18min);
+ static_assert(fmin.seconds() == 11s);
+ static_assert(fmin.subseconds() == 0.25s);
+ static_assert(fmin.is_negative());
+ static_assert(fmin.to_duration() == -0x1.23p4min);
+}
+
+constexpr void
+default_construction()
+{
+ using namespace std::chrono;
+
+ constexpr hh_mm_ss<seconds> s1;
+ static_assert(s1.to_duration() == s1.to_duration().zero());
+ constexpr hh_mm_ss<duration<char>> s2;
+ static_assert(s2.to_duration() == s2.to_duration().zero());
+ constexpr hh_mm_ss<duration<int, std::centi>> s3;
+ static_assert(s3.to_duration() == s3.to_duration().zero());
+ constexpr hh_mm_ss<duration<long long, std::femto>> s4;
+ static_assert(s4.to_duration() == s4.to_duration().zero());
+ constexpr hh_mm_ss<duration<double>> s5;
+ static_assert(s5.to_duration() == s5.to_duration().zero());
+}
+
+constexpr void
+size()
+{
+ using namespace std::chrono;
+
+ struct S0 { long long h; char m; char s; bool neg; };
+ static_assert(sizeof(hh_mm_ss<seconds>) == sizeof(S0));
+ struct S1 { long long h; char m; char s; bool neg; char ss; };
+ static_assert(sizeof(hh_mm_ss<duration<int, std::centi>>) == sizeof(S1));
+ struct S2 { long long h; char m, s; bool neg; int ss; };
+ static_assert(sizeof(hh_mm_ss<duration<int, std::milli>>) == sizeof(S2));
+ static_assert(sizeof(hh_mm_ss<duration<int, std::pico>>) == sizeof(S2));
+ struct S3 { long long h; char m, s; bool neg; long long ss; };
+ static_assert(sizeof(hh_mm_ss<duration<long long, std::pico>>) == sizeof(S3));
+ struct S4 { long long h; char m, s; bool neg; double ss; };
+ static_assert(sizeof(hh_mm_ss<duration<double, std::micro>>) == sizeof(S4));
}