Add support down to femtosecond precision

* Requires platform specific use of 128bit integral representation
  (e.g. std::chrono::duration<__int128_t, std::femto>).
This commit is contained in:
Howard Hinnant 2017-09-09 10:30:39 -04:00
parent 5f01382e24
commit 481771ef5e
4 changed files with 140 additions and 49 deletions

130
date.h
View File

@ -1014,18 +1014,86 @@ trunc(T t) NOEXCEPT
return t; return t;
} }
template <std::intmax_t Xp, std::intmax_t Yp>
struct static_gcd
{
static const std::intmax_t value = static_gcd<Yp, Xp % Yp>::value;
};
template <std::intmax_t Xp>
struct static_gcd<Xp, 0>
{
static const std::intmax_t value = Xp;
};
template <>
struct static_gcd<0, 0>
{
static const std::intmax_t value = 1;
};
template <class R1, class R2>
struct no_overflow
{
private:
static const std::intmax_t gcd_n1_n2 = static_gcd<R1::num, R2::num>::value;
static const std::intmax_t gcd_d1_d2 = static_gcd<R1::den, R2::den>::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 <std::intmax_t Xp, std::intmax_t Yp, bool overflow>
struct mul // overflow == false
{
static const std::intmax_t value = Xp * Yp;
};
template <std::intmax_t Xp, std::intmax_t Yp>
struct mul<Xp, Yp, true>
{
static const std::intmax_t value = 1;
};
public:
static const bool value = (n1 <= max / d2) && (n2 <= max / d1);
typedef std::ratio<mul<n1, d2, !value>::value,
mul<n2, d1, !value>::value> type;
};
} // detail } // detail
// trunc towards zero // trunc towards zero
template <class To, class Rep, class Period> template <class To, class Rep, class Period>
CONSTCD11 CONSTCD11
inline inline
To typename std::enable_if
<
detail::no_overflow<Period, typename To::period>::value,
To
>::type
trunc(const std::chrono::duration<Rep, Period>& d) trunc(const std::chrono::duration<Rep, Period>& d)
{ {
return To{detail::trunc(std::chrono::duration_cast<To>(d).count())}; return To{detail::trunc(std::chrono::duration_cast<To>(d).count())};
} }
template <class To, class Rep, class Period>
CONSTCD11
inline
typename std::enable_if
<
!detail::no_overflow<Period, typename To::period>::value,
To
>::type
trunc(const std::chrono::duration<Rep, Period>& d)
{
using namespace std::chrono;
using rep = typename std::common_type<Rep, typename To::rep>::type;
return To{detail::trunc(duration_cast<To>(duration_cast<duration<rep>>(d)).count())};
}
#ifndef HAS_CHRONO_ROUNDING #ifndef HAS_CHRONO_ROUNDING
# if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 # if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
# define HAS_CHRONO_ROUNDING 1 # define HAS_CHRONO_ROUNDING 1
@ -1044,7 +1112,11 @@ trunc(const std::chrono::duration<Rep, Period>& d)
template <class To, class Rep, class Period> template <class To, class Rep, class Period>
CONSTCD14 CONSTCD14
inline inline
To typename std::enable_if
<
detail::no_overflow<Period, typename To::period>::value,
To
>::type
floor(const std::chrono::duration<Rep, Period>& d) floor(const std::chrono::duration<Rep, Period>& d)
{ {
auto t = trunc<To>(d); auto t = trunc<To>(d);
@ -1053,6 +1125,21 @@ floor(const std::chrono::duration<Rep, Period>& d)
return t; return t;
} }
template <class To, class Rep, class Period>
CONSTCD14
inline
typename std::enable_if
<
!detail::no_overflow<Period, typename To::period>::value,
To
>::type
floor(const std::chrono::duration<Rep, Period>& d)
{
using namespace std::chrono;
using rep = typename std::common_type<Rep, typename To::rep>::type;
return floor<To>(floor<duration<rep>>(d));
}
// round to nearest, to even on tie // round to nearest, to even on tie
template <class To, class Rep, class Period> template <class To, class Rep, class Period>
CONSTCD14 CONSTCD14
@ -3494,18 +3581,18 @@ struct static_pow10<0>
static CONSTDATA std::uint64_t value = 1; static CONSTDATA std::uint64_t value = 1;
}; };
template <unsigned w, bool in_range = (w < 19)> template <class Rep, unsigned w, bool in_range = (w < 19)>
struct make_precision struct make_precision
{ {
using type = std::chrono::duration<std::int64_t, using type = std::chrono::duration<Rep,
std::ratio<1, static_pow10<w>::value>>; std::ratio<1, static_pow10<w>::value>>;
static CONSTDATA unsigned width = w; static CONSTDATA unsigned width = w;
}; };
template <unsigned w> template <class Rep, unsigned w>
struct make_precision<w, false> struct make_precision<Rep, w, false>
{ {
using type = std::chrono::microseconds; using type = std::chrono::duration<Rep, std::micro>;
static CONSTDATA unsigned width = 6; static CONSTDATA unsigned width = 6;
}; };
@ -3516,8 +3603,9 @@ template <class Duration,
class decimal_format_seconds class decimal_format_seconds
{ {
public: public:
using precision = typename make_precision<w>::type; using rep = typename std::common_type<Duration, std::chrono::seconds>::type::rep;
static auto CONSTDATA width = make_precision<w>::width; using precision = typename make_precision<rep, w>::type;
static auto CONSTDATA width = make_precision<rep, w>::width;
private: private:
std::chrono::seconds s_; std::chrono::seconds s_;
@ -3561,7 +3649,7 @@ public:
os << x.s_.count() << os << x.s_.count() <<
std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point(); std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point();
os.width(width); os.width(width);
os << x.sub_s_.count(); os << static_cast<std::int64_t>(x.sub_s_.count());
return os; return os;
} }
}; };
@ -3571,8 +3659,9 @@ class decimal_format_seconds<Duration, 0>
{ {
static CONSTDATA unsigned w = 0; static CONSTDATA unsigned w = 0;
public: public:
using precision = std::chrono::seconds; using rep = typename std::common_type<Duration, std::chrono::seconds>::type::rep;
static auto CONSTDATA width = make_precision<w>::width; using precision = std::chrono::duration<rep>;
static auto CONSTDATA width = make_precision<rep, w>::width;
private: private:
std::chrono::seconds s_; std::chrono::seconds s_;
@ -4032,9 +4121,9 @@ public:
{} {}
CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT
: base(std::chrono::duration_cast<std::chrono::hours>(since_midnight), : base(date::trunc<std::chrono::hours>(since_midnight),
since_midnight < Duration{0}, is24hr) since_midnight < Duration{0}, is24hr)
, m_(std::chrono::duration_cast<std::chrono::minutes>(detail::abs(since_midnight) - h_)) , m_(date::trunc<std::chrono::minutes>(detail::abs(since_midnight) - h_))
, s_(detail::abs(since_midnight) - h_ - m_) , s_(detail::abs(since_midnight) - h_ - m_)
{} {}
@ -5513,7 +5602,7 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
{ {
using CT = typename std::common_type<Duration, std::chrono::seconds>::type; using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
auto ld = floor<days>(tp); auto ld = floor<days>(tp);
fields<CT> fds{year_month_day{ld}, time_of_day<CT>{tp-ld}}; fields<CT> fds{year_month_day{ld}, time_of_day<CT>{tp-local_seconds{ld}}};
return to_stream(os, fmt, fds, abbrev, offset_sec); return to_stream(os, fmt, fds, abbrev, offset_sec);
} }
@ -5522,11 +5611,12 @@ std::basic_ostream<CharT, Traits>&
to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
const sys_time<Duration>& tp) const sys_time<Duration>& tp)
{ {
using CT = typename std::common_type<Duration, std::chrono::seconds>::type; using namespace std::chrono;
using CT = typename std::common_type<Duration, seconds>::type;
const std::string abbrev("UTC"); const std::string abbrev("UTC");
CONSTDATA std::chrono::seconds offset{0}; CONSTDATA seconds offset{0};
auto sd = floor<days>(tp); auto sd = floor<days>(tp);
fields<CT> fds{year_month_day{sd}, time_of_day<CT>{tp-sd}}; fields<CT> fds{year_month_day{sd}, time_of_day<CT>{tp-sys_seconds{sd}}};
return to_stream(os, fmt, fds, &abbrev, &offset); return to_stream(os, fmt, fds, &abbrev, &offset);
} }
@ -7033,7 +7123,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(ios::failbit); is.setstate(ios::failbit);
if (!is.fail()) if (!is.fail())
tp = round<Duration>(sys_days(fds.ymd) + fds.tod.to_duration() - *offptr); tp = round<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration());
return is; return is;
} }
@ -7051,7 +7141,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(ios::failbit); is.setstate(ios::failbit);
if (!is.fail()) if (!is.fail())
tp = round<Duration>(local_days(fds.ymd) + fds.tod.to_duration()); tp = round<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration());
return is; return is;
} }

View File

@ -94,7 +94,7 @@ main()
{ {
using D = decimal_format_seconds<milliseconds>; using D = decimal_format_seconds<milliseconds>;
static_assert(D::width == 3, ""); static_assert(D::width == 3, "");
static_assert(is_same<D::precision, make_precision<D::width>::type>{}, ""); static_assert(is_same<D::precision, make_precision<D::rep, D::width>::type>{}, "");
D dfs{seconds{3}}; D dfs{seconds{3}};
assert(dfs.seconds() == seconds{3}); assert(dfs.seconds() == seconds{3});
assert(dfs.to_duration() == seconds{3}); assert(dfs.to_duration() == seconds{3});
@ -106,7 +106,7 @@ main()
{ {
using D = decimal_format_seconds<milliseconds>; using D = decimal_format_seconds<milliseconds>;
static_assert(D::width == 3, ""); static_assert(D::width == 3, "");
static_assert(is_same<D::precision, make_precision<D::width>::type>{}, ""); static_assert(is_same<D::precision, make_precision<D::rep, D::width>::type>{}, "");
D dfs{milliseconds{3}}; D dfs{milliseconds{3}};
assert(dfs.seconds() == seconds{0}); assert(dfs.seconds() == seconds{0});
assert(dfs.to_duration() == milliseconds{3}); assert(dfs.to_duration() == milliseconds{3});
@ -118,7 +118,7 @@ main()
{ {
using D = decimal_format_seconds<microfortnights>; using D = decimal_format_seconds<microfortnights>;
static_assert(D::width == 4, ""); static_assert(D::width == 4, "");
using S = make_precision<D::width>::type; using S = make_precision<D::rep, D::width>::type;
static_assert(is_same<D::precision, S>{}, ""); static_assert(is_same<D::precision, S>{}, "");
D dfs{microfortnights{3}}; D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3}); assert(dfs.seconds() == seconds{3});
@ -132,7 +132,7 @@ main()
using CT = common_type<seconds, microfortnights>::type; using CT = common_type<seconds, microfortnights>::type;
using D = decimal_format_seconds<CT>; using D = decimal_format_seconds<CT>;
static_assert(D::width == 4, ""); static_assert(D::width == 4, "");
using S = make_precision<D::width>::type; using S = make_precision<D::rep, D::width>::type;
static_assert(is_same<D::precision, S>{}, ""); static_assert(is_same<D::precision, S>{}, "");
D dfs{microfortnights{3}}; D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3}); assert(dfs.seconds() == seconds{3});

View File

@ -40,24 +40,24 @@ main()
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
static_assert(make_precision<0>::width == 0, ""); static_assert(make_precision<int64_t, 0>::width == 0, "");
static_assert(is_same<make_precision<0>::type, duration<int64_t, ratio<1, 1>>>{}, ""); static_assert(is_same<make_precision<int64_t, 0>::type, duration<int64_t, ratio<1, 1>>>{}, "");
static_assert(make_precision<1>::width == 1, ""); static_assert(make_precision<int64_t, 1>::width == 1, "");
static_assert(is_same<make_precision<1>::type, duration<int64_t, ratio<1, 10>>>{}, ""); static_assert(is_same<make_precision<int64_t, 1>::type, duration<int64_t, ratio<1, 10>>>{}, "");
static_assert(make_precision<2>::width == 2, ""); static_assert(make_precision<int64_t, 2>::width == 2, "");
static_assert(is_same<make_precision<2>::type, duration<int64_t, ratio<1, 100>>>{}, ""); static_assert(is_same<make_precision<int64_t, 2>::type, duration<int64_t, ratio<1, 100>>>{}, "");
static_assert(make_precision<3>::width == 3, ""); static_assert(make_precision<int64_t, 3>::width == 3, "");
static_assert(is_same<make_precision<3>::type, duration<int64_t, ratio<1, 1000>>>{}, ""); static_assert(is_same<make_precision<int64_t, 3>::type, duration<int64_t, ratio<1, 1000>>>{}, "");
static_assert(make_precision<18>::width == 18, ""); static_assert(make_precision<int64_t, 18>::width == 18, "");
static_assert(is_same<make_precision<18>::type, duration<int64_t, ratio<1, 1000000000000000000>>>{}, ""); static_assert(is_same<make_precision<int64_t, 18>::type, duration<int64_t, ratio<1, 1000000000000000000>>>{}, "");
static_assert(make_precision<19>::width == 6, ""); static_assert(make_precision<int64_t, 19>::width == 6, "");
static_assert(is_same<make_precision<19>::type, microseconds>{}, ""); static_assert(is_same<make_precision<int64_t, 19>::type, microseconds>{}, "");
static_assert(make_precision<20>::width == 6, ""); static_assert(make_precision<int64_t, 20>::width == 6, "");
static_assert(is_same<make_precision<20>::type, microseconds>{}, ""); static_assert(is_same<make_precision<int64_t, 20>::type, microseconds>{}, "");
} }

23
tz.h
View File

@ -1913,7 +1913,7 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
auto tp = sys_time<CT>{t.time_since_epoch() - ls.second}; auto tp = sys_time<CT>{t.time_since_epoch() - ls.second};
auto const sd = floor<days>(tp); auto const sd = floor<days>(tp);
year_month_day ymd = sd; year_month_day ymd = sd;
auto time = make_time(tp - sd); auto time = make_time(tp - sys_seconds{sd});
time.seconds() += seconds{ls.first}; time.seconds() += seconds{ls.first};
fields<CT> fds{ymd, time}; fields<CT> fds{ymd, time};
return to_stream(os, fmt, fds, &abbrev, &offset); return to_stream(os, fmt, fds, &abbrev, &offset);
@ -1947,7 +1947,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
bool is_60_sec = fds.tod.seconds() == seconds{60}; bool is_60_sec = fds.tod.seconds() == seconds{60};
if (is_60_sec) if (is_60_sec)
fds.tod.seconds() -= seconds{1}; 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) if (is_60_sec)
tmp += seconds{1}; tmp += seconds{1};
if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range())
@ -2028,10 +2028,10 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
const string abbrev("TAI"); const string abbrev("TAI");
CONSTDATA seconds offset{0}; CONSTDATA seconds offset{0};
auto tp = sys_time<CT>{t.time_since_epoch()} - auto tp = sys_time<CT>{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<days>(tp); auto const sd = floor<days>(tp);
year_month_day ymd = sd; year_month_day ymd = sd;
auto time = make_time(tp - sd); auto time = make_time(tp - sys_seconds{sd});
fields<CT> fds{ymd, time}; fields<CT> fds{ymd, time};
return to_stream(os, fmt, fds, &abbrev, &offset); return to_stream(os, fmt, fds, &abbrev, &offset);
} }
@ -2062,8 +2062,9 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
is.setstate(ios::failbit); is.setstate(ios::failbit);
if (!is.fail()) if (!is.fail())
tp = tai_time<Duration>{duration_cast<Duration>( tp = tai_time<Duration>{duration_cast<Duration>(
(sys_days(fds.ymd) + fds.tod.to_duration() + (sys_days(year{1970}/jan/1) - (sys_days(fds.ymd) +
sys_days(year{1958}/jan/1)) - *offptr).time_since_epoch())}; (sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)) -
*offptr + fds.tod.to_duration()).time_since_epoch())};
return is; return is;
} }
@ -2135,10 +2136,10 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
const string abbrev("GPS"); const string abbrev("GPS");
CONSTDATA seconds offset{0}; CONSTDATA seconds offset{0};
auto tp = sys_time<CT>{t.time_since_epoch()} + auto tp = sys_time<CT>{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<days>(tp); auto const sd = floor<days>(tp);
year_month_day ymd = sd; year_month_day ymd = sd;
auto time = make_time(tp - sd); auto time = make_time(tp - sys_seconds{sd});
fields<CT> fds{ymd, time}; fields<CT> fds{ymd, time};
return to_stream(os, fmt, fds, &abbrev, &offset); return to_stream(os, fmt, fds, &abbrev, &offset);
} }
@ -2169,9 +2170,9 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
is.setstate(ios::failbit); is.setstate(ios::failbit);
if (!is.fail()) if (!is.fail())
tp = gps_time<Duration>{duration_cast<Duration>( tp = gps_time<Duration>{duration_cast<Duration>(
(sys_days(fds.ymd) + fds.tod.to_duration() - (sys_days(fds.ymd) -
(sys_days(year{1980}/jan/sun[1]) - (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)) -
sys_days(year{1970}/jan/1)) - *offptr).time_since_epoch())}; *offptr + fds.tod.to_duration()).time_since_epoch())};
return is; return is;
} }