From 481771ef5e991ad620775b1b9365f8c27f6ff076 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Sat, 9 Sep 2017 10:30:39 -0400 Subject: [PATCH] Add support down to femtosecond precision * Requires platform specific use of 128bit integral representation (e.g. std::chrono::duration<__int128_t, std::femto>). --- date.h | 130 +++++++++++++++--- .../detail/decimal_format_seconds.pass.cpp | 8 +- test/date_test/detail/make_precision.pass.cpp | 28 ++-- tz.h | 23 ++-- 4 files changed, 140 insertions(+), 49 deletions(-) diff --git a/date.h b/date.h index 49438a8..bca0298 100644 --- a/date.h +++ b/date.h @@ -1014,18 +1014,86 @@ trunc(T t) NOEXCEPT return t; } +template +struct static_gcd +{ + static const std::intmax_t value = static_gcd::value; +}; + +template +struct static_gcd +{ + static const std::intmax_t value = Xp; +}; + +template <> +struct static_gcd<0, 0> +{ + static const std::intmax_t value = 1; +}; + +template +struct no_overflow +{ +private: + static const std::intmax_t gcd_n1_n2 = static_gcd::value; + static const std::intmax_t gcd_d1_d2 = static_gcd::value; + static const std::intmax_t n1 = R1::num / gcd_n1_n2; + static const std::intmax_t d1 = R1::den / gcd_d1_d2; + static const std::intmax_t n2 = R2::num / gcd_n1_n2; + static const std::intmax_t d2 = R2::den / gcd_d1_d2; + static const std::intmax_t max = -((std::intmax_t(1) << + (sizeof(std::intmax_t) * CHAR_BIT - 1)) + 1); + + template + struct mul // overflow == false + { + static const std::intmax_t value = Xp * Yp; + }; + + template + struct mul + { + static const std::intmax_t value = 1; + }; + +public: + static const bool value = (n1 <= max / d2) && (n2 <= max / d1); + typedef std::ratio::value, + mul::value> type; +}; + } // detail // trunc towards zero template CONSTCD11 inline -To +typename std::enable_if +< + detail::no_overflow::value, + To +>::type trunc(const std::chrono::duration& d) { return To{detail::trunc(std::chrono::duration_cast(d).count())}; } +template +CONSTCD11 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +trunc(const std::chrono::duration& d) +{ + using namespace std::chrono; + using rep = typename std::common_type::type; + return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; +} + #ifndef HAS_CHRONO_ROUNDING # if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 # define HAS_CHRONO_ROUNDING 1 @@ -1044,7 +1112,11 @@ trunc(const std::chrono::duration& d) template CONSTCD14 inline -To +typename std::enable_if +< + detail::no_overflow::value, + To +>::type floor(const std::chrono::duration& d) { auto t = trunc(d); @@ -1053,6 +1125,21 @@ floor(const std::chrono::duration& d) return t; } +template +CONSTCD14 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +floor(const std::chrono::duration& d) +{ + using namespace std::chrono; + using rep = typename std::common_type::type; + return floor(floor>(d)); +} + // round to nearest, to even on tie template CONSTCD14 @@ -3494,18 +3581,18 @@ struct static_pow10<0> static CONSTDATA std::uint64_t value = 1; }; -template +template struct make_precision { - using type = std::chrono::duration::value>>; static CONSTDATA unsigned width = w; }; -template -struct make_precision +template +struct make_precision { - using type = std::chrono::microseconds; + using type = std::chrono::duration; static CONSTDATA unsigned width = 6; }; @@ -3516,8 +3603,9 @@ template ::type; - static auto CONSTDATA width = make_precision::width; + using rep = typename std::common_type::type::rep; + using precision = typename make_precision::type; + static auto CONSTDATA width = make_precision::width; private: std::chrono::seconds s_; @@ -3561,7 +3649,7 @@ public: os << x.s_.count() << std::use_facet>(os.getloc()).decimal_point(); os.width(width); - os << x.sub_s_.count(); + os << static_cast(x.sub_s_.count()); return os; } }; @@ -3571,8 +3659,9 @@ class decimal_format_seconds { static CONSTDATA unsigned w = 0; public: - using precision = std::chrono::seconds; - static auto CONSTDATA width = make_precision::width; + using rep = typename std::common_type::type::rep; + using precision = std::chrono::duration; + static auto CONSTDATA width = make_precision::width; private: std::chrono::seconds s_; @@ -4032,9 +4121,9 @@ public: {} CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT - : base(std::chrono::duration_cast(since_midnight), + : base(date::trunc(since_midnight), since_midnight < Duration{0}, is24hr) - , m_(std::chrono::duration_cast(detail::abs(since_midnight) - h_)) + , m_(date::trunc(detail::abs(since_midnight) - h_)) , s_(detail::abs(since_midnight) - h_ - m_) {} @@ -5513,7 +5602,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { using CT = typename std::common_type::type; auto ld = floor(tp); - fields fds{year_month_day{ld}, time_of_day{tp-ld}}; + fields fds{year_month_day{ld}, time_of_day{tp-local_seconds{ld}}}; return to_stream(os, fmt, fds, abbrev, offset_sec); } @@ -5522,11 +5611,12 @@ std::basic_ostream& to_stream(std::basic_ostream& os, const CharT* fmt, const sys_time& tp) { - using CT = typename std::common_type::type; + using namespace std::chrono; + using CT = typename std::common_type::type; const std::string abbrev("UTC"); - CONSTDATA std::chrono::seconds offset{0}; + CONSTDATA seconds offset{0}; auto sd = floor(tp); - fields fds{year_month_day{sd}, time_of_day{tp-sd}}; + fields fds{year_month_day{sd}, time_of_day{tp-sys_seconds{sd}}}; return to_stream(os, fmt, fds, &abbrev, &offset); } @@ -7033,7 +7123,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) - tp = round(sys_days(fds.ymd) + fds.tod.to_duration() - *offptr); + tp = round(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); return is; } @@ -7051,7 +7141,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) - tp = round(local_days(fds.ymd) + fds.tod.to_duration()); + tp = round(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); return is; } diff --git a/test/date_test/detail/decimal_format_seconds.pass.cpp b/test/date_test/detail/decimal_format_seconds.pass.cpp index 426ff0b..dfb4619 100644 --- a/test/date_test/detail/decimal_format_seconds.pass.cpp +++ b/test/date_test/detail/decimal_format_seconds.pass.cpp @@ -94,7 +94,7 @@ main() { using D = decimal_format_seconds; static_assert(D::width == 3, ""); - static_assert(is_same::type>{}, ""); + static_assert(is_same::type>{}, ""); D dfs{seconds{3}}; assert(dfs.seconds() == seconds{3}); assert(dfs.to_duration() == seconds{3}); @@ -106,7 +106,7 @@ main() { using D = decimal_format_seconds; static_assert(D::width == 3, ""); - static_assert(is_same::type>{}, ""); + static_assert(is_same::type>{}, ""); D dfs{milliseconds{3}}; assert(dfs.seconds() == seconds{0}); assert(dfs.to_duration() == milliseconds{3}); @@ -118,7 +118,7 @@ main() { using D = decimal_format_seconds; static_assert(D::width == 4, ""); - using S = make_precision::type; + using S = make_precision::type; static_assert(is_same{}, ""); D dfs{microfortnights{3}}; assert(dfs.seconds() == seconds{3}); @@ -132,7 +132,7 @@ main() using CT = common_type::type; using D = decimal_format_seconds; static_assert(D::width == 4, ""); - using S = make_precision::type; + using S = make_precision::type; static_assert(is_same{}, ""); D dfs{microfortnights{3}}; assert(dfs.seconds() == seconds{3}); diff --git a/test/date_test/detail/make_precision.pass.cpp b/test/date_test/detail/make_precision.pass.cpp index bac8609..efdec1c 100644 --- a/test/date_test/detail/make_precision.pass.cpp +++ b/test/date_test/detail/make_precision.pass.cpp @@ -40,24 +40,24 @@ main() using namespace std; using namespace std::chrono; - static_assert(make_precision<0>::width == 0, ""); - static_assert(is_same::type, duration>>{}, ""); + static_assert(make_precision::width == 0, ""); + static_assert(is_same::type, duration>>{}, ""); - static_assert(make_precision<1>::width == 1, ""); - static_assert(is_same::type, duration>>{}, ""); + static_assert(make_precision::width == 1, ""); + static_assert(is_same::type, duration>>{}, ""); - static_assert(make_precision<2>::width == 2, ""); - static_assert(is_same::type, duration>>{}, ""); + static_assert(make_precision::width == 2, ""); + static_assert(is_same::type, duration>>{}, ""); - static_assert(make_precision<3>::width == 3, ""); - static_assert(is_same::type, duration>>{}, ""); + static_assert(make_precision::width == 3, ""); + static_assert(is_same::type, duration>>{}, ""); - static_assert(make_precision<18>::width == 18, ""); - static_assert(is_same::type, duration>>{}, ""); + static_assert(make_precision::width == 18, ""); + static_assert(is_same::type, duration>>{}, ""); - static_assert(make_precision<19>::width == 6, ""); - static_assert(is_same::type, microseconds>{}, ""); + static_assert(make_precision::width == 6, ""); + static_assert(is_same::type, microseconds>{}, ""); - static_assert(make_precision<20>::width == 6, ""); - static_assert(is_same::type, microseconds>{}, ""); + static_assert(make_precision::width == 6, ""); + static_assert(is_same::type, microseconds>{}, ""); } diff --git a/tz.h b/tz.h index 3008580..3195461 100644 --- a/tz.h +++ b/tz.h @@ -1913,7 +1913,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, auto tp = sys_time{t.time_since_epoch() - ls.second}; auto const sd = floor(tp); year_month_day ymd = sd; - auto time = make_time(tp - sd); + auto time = make_time(tp - sys_seconds{sd}); time.seconds() += seconds{ls.first}; fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); @@ -1947,7 +1947,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, bool is_60_sec = fds.tod.seconds() == seconds{60}; if (is_60_sec) fds.tod.seconds() -= seconds{1}; - auto tmp = to_utc_time(sys_days(fds.ymd) + (fds.tod.to_duration() - *offptr)); + auto tmp = to_utc_time(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); if (is_60_sec) tmp += seconds{1}; if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) @@ -2028,10 +2028,10 @@ to_stream(std::basic_ostream& os, const CharT* fmt, const string abbrev("TAI"); CONSTDATA seconds offset{0}; auto tp = sys_time{t.time_since_epoch()} - - (sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)); + seconds(sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)); auto const sd = floor(tp); year_month_day ymd = sd; - auto time = make_time(tp - sd); + auto time = make_time(tp - sys_seconds{sd}); fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); } @@ -2062,8 +2062,9 @@ from_stream(std::basic_istream& is, const CharT* fmt, is.setstate(ios::failbit); if (!is.fail()) tp = tai_time{duration_cast( - (sys_days(fds.ymd) + fds.tod.to_duration() + (sys_days(year{1970}/jan/1) - - sys_days(year{1958}/jan/1)) - *offptr).time_since_epoch())}; + (sys_days(fds.ymd) + + (sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)) - + *offptr + fds.tod.to_duration()).time_since_epoch())}; return is; } @@ -2135,10 +2136,10 @@ to_stream(std::basic_ostream& os, const CharT* fmt, const string abbrev("GPS"); CONSTDATA seconds offset{0}; auto tp = sys_time{t.time_since_epoch()} + - (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)); + seconds(sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)); auto const sd = floor(tp); year_month_day ymd = sd; - auto time = make_time(tp - sd); + auto time = make_time(tp - sys_seconds{sd}); fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); } @@ -2169,9 +2170,9 @@ from_stream(std::basic_istream& is, const CharT* fmt, is.setstate(ios::failbit); if (!is.fail()) tp = gps_time{duration_cast( - (sys_days(fds.ymd) + fds.tod.to_duration() - - (sys_days(year{1980}/jan/sun[1]) - - sys_days(year{1970}/jan/1)) - *offptr).time_since_epoch())}; + (sys_days(fds.ymd) - + (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)) - + *offptr + fds.tod.to_duration()).time_since_epoch())}; return is; }