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;
}
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
// trunc towards zero
template <class To, class Rep, class Period>
CONSTCD11
inline
To
typename std::enable_if
<
detail::no_overflow<Period, typename To::period>::value,
To
>::type
trunc(const std::chrono::duration<Rep, Period>& d)
{
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
# if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
# define HAS_CHRONO_ROUNDING 1
@ -1044,7 +1112,11 @@ trunc(const std::chrono::duration<Rep, Period>& d)
template <class To, class Rep, class Period>
CONSTCD14
inline
To
typename std::enable_if
<
detail::no_overflow<Period, typename To::period>::value,
To
>::type
floor(const std::chrono::duration<Rep, Period>& d)
{
auto t = trunc<To>(d);
@ -1053,6 +1125,21 @@ floor(const std::chrono::duration<Rep, Period>& d)
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
template <class To, class Rep, class Period>
CONSTCD14
@ -3494,18 +3581,18 @@ struct static_pow10<0>
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
{
using type = std::chrono::duration<std::int64_t,
using type = std::chrono::duration<Rep,
std::ratio<1, static_pow10<w>::value>>;
static CONSTDATA unsigned width = w;
};
template <unsigned w>
struct make_precision<w, false>
template <class Rep, unsigned w>
struct make_precision<Rep, w, false>
{
using type = std::chrono::microseconds;
using type = std::chrono::duration<Rep, std::micro>;
static CONSTDATA unsigned width = 6;
};
@ -3516,8 +3603,9 @@ template <class Duration,
class decimal_format_seconds
{
public:
using precision = typename make_precision<w>::type;
static auto CONSTDATA width = make_precision<w>::width;
using rep = typename std::common_type<Duration, std::chrono::seconds>::type::rep;
using precision = typename make_precision<rep, w>::type;
static auto CONSTDATA width = make_precision<rep, w>::width;
private:
std::chrono::seconds s_;
@ -3561,7 +3649,7 @@ public:
os << x.s_.count() <<
std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point();
os.width(width);
os << x.sub_s_.count();
os << static_cast<std::int64_t>(x.sub_s_.count());
return os;
}
};
@ -3571,8 +3659,9 @@ class decimal_format_seconds<Duration, 0>
{
static CONSTDATA unsigned w = 0;
public:
using precision = std::chrono::seconds;
static auto CONSTDATA width = make_precision<w>::width;
using rep = typename std::common_type<Duration, std::chrono::seconds>::type::rep;
using precision = std::chrono::duration<rep>;
static auto CONSTDATA width = make_precision<rep, w>::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<std::chrono::hours>(since_midnight),
: base(date::trunc<std::chrono::hours>(since_midnight),
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_)
{}
@ -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;
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);
}
@ -5522,11 +5611,12 @@ std::basic_ostream<CharT, Traits>&
to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
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");
CONSTDATA std::chrono::seconds offset{0};
CONSTDATA seconds offset{0};
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);
}
@ -7033,7 +7123,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(ios::failbit);
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;
}
@ -7051,7 +7141,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(ios::failbit);
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;
}

View File

@ -94,7 +94,7 @@ main()
{
using D = decimal_format_seconds<milliseconds>;
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}};
assert(dfs.seconds() == seconds{3});
assert(dfs.to_duration() == seconds{3});
@ -106,7 +106,7 @@ main()
{
using D = decimal_format_seconds<milliseconds>;
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}};
assert(dfs.seconds() == seconds{0});
assert(dfs.to_duration() == milliseconds{3});
@ -118,7 +118,7 @@ main()
{
using D = decimal_format_seconds<microfortnights>;
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>{}, "");
D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3});
@ -132,7 +132,7 @@ main()
using CT = common_type<seconds, microfortnights>::type;
using D = decimal_format_seconds<CT>;
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>{}, "");
D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3});

View File

@ -40,24 +40,24 @@ main()
using namespace std;
using namespace std::chrono;
static_assert(make_precision<0>::width == 0, "");
static_assert(is_same<make_precision<0>::type, duration<int64_t, ratio<1, 1>>>{}, "");
static_assert(make_precision<int64_t, 0>::width == 0, "");
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(is_same<make_precision<1>::type, duration<int64_t, ratio<1, 10>>>{}, "");
static_assert(make_precision<int64_t, 1>::width == 1, "");
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(is_same<make_precision<2>::type, duration<int64_t, ratio<1, 100>>>{}, "");
static_assert(make_precision<int64_t, 2>::width == 2, "");
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(is_same<make_precision<3>::type, duration<int64_t, ratio<1, 1000>>>{}, "");
static_assert(make_precision<int64_t, 3>::width == 3, "");
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(is_same<make_precision<18>::type, duration<int64_t, ratio<1, 1000000000000000000>>>{}, "");
static_assert(make_precision<int64_t, 18>::width == 18, "");
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(is_same<make_precision<19>::type, microseconds>{}, "");
static_assert(make_precision<int64_t, 19>::width == 6, "");
static_assert(is_same<make_precision<int64_t, 19>::type, microseconds>{}, "");
static_assert(make_precision<20>::width == 6, "");
static_assert(is_same<make_precision<20>::type, microseconds>{}, "");
static_assert(make_precision<int64_t, 20>::width == 6, "");
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 const sd = floor<days>(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<CT> fds{ymd, time};
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};
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<CharT, Traits>& os, const CharT* fmt,
const string abbrev("TAI");
CONSTDATA seconds offset{0};
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);
year_month_day ymd = sd;
auto time = make_time(tp - sd);
auto time = make_time(tp - sys_seconds{sd});
fields<CT> fds{ymd, time};
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);
if (!is.fail())
tp = tai_time<Duration>{duration_cast<Duration>(
(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<CharT, Traits>& os, const CharT* fmt,
const string abbrev("GPS");
CONSTDATA seconds offset{0};
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);
year_month_day ymd = sd;
auto time = make_time(tp - sd);
auto time = make_time(tp - sys_seconds{sd});
fields<CT> fds{ymd, time};
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);
if (!is.fail())
tp = gps_time<Duration>{duration_cast<Duration>(
(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;
}