Skip to content

Instantly share code, notes, and snippets.

@HowardHinnant
Created October 16, 2018 02:25
Show Gist options
  • Save HowardHinnant/bc8de3b5fbead3118cb47b20222cbd24 to your computer and use it in GitHub Desktop.
Save HowardHinnant/bc8de3b5fbead3118cb47b20222cbd24 to your computer and use it in GitHub Desktop.
time_of_day prototype
#include <chrono>
#include <cstdint>
#include <istream>
#include <locale>
#include <ostream>
namespace std
{
namespace chrono
{
namespace detail
{
template<class CharT, class Traits = std::char_traits<CharT>>
class save_istream
{
protected:
std::basic_ios<CharT, Traits>& is_;
CharT fill_;
std::ios::fmtflags flags_;
std::streamsize width_;
std::basic_ostream<CharT, Traits>* tie_;
std::locale loc_;
public:
~save_istream()
{
is_.fill(fill_);
is_.flags(flags_);
is_.width(width_);
is_.imbue(loc_);
is_.tie(tie_);
}
save_istream(const save_istream&) = delete;
save_istream& operator=(const save_istream&) = delete;
explicit save_istream(std::basic_ios<CharT, Traits>& is)
: is_(is)
, fill_(is.fill())
, flags_(is.flags())
, width_(is.width(0))
, tie_(is.tie(nullptr))
, loc_(is.getloc())
{
if (tie_ != nullptr)
tie_->flush();
}
};
template<class CharT, class Traits = std::char_traits<CharT>>
class save_ostream
: private save_istream<CharT, Traits>
{
public:
~save_ostream()
{
if ((this->flags_ & std::ios::unitbuf) &&
#if __cplusplus >= 201703
std::uncaught_exceptions() == 0 &&
#else
!std::uncaught_exception() &&
#endif
this->is_.good())
this->is_.rdbuf()->pubsync();
}
save_ostream(const save_ostream&) = delete;
save_ostream& operator=(const save_ostream&) = delete;
explicit save_ostream(std::basic_ios<CharT, Traits>& os)
: save_istream<CharT, Traits>(os)
{
}
};
// width<n>::value is the number of fractional decimal digits in 1/n
// width<0>::value and width<1>::value are defined to be 0
// If 1/n takes more than 18 fractional decimal digits,
// the result is truncated to 19.
// Example: width<2>::value == 1
// Example: width<3>::value == 19
// Example: width<4>::value == 2
// Example: width<10>::value == 1
// Example: width<1000>::value == 3
template <uint64_t n, uint64_t d = 10, unsigned w = 0,
bool should_continue = !(n < 2) && d != 0 && (w < 19)>
struct width
{
static constexpr unsigned value = 1 + width<n, d%n*10, w+1>::value;
};
template <uint64_t n, uint64_t d, unsigned w>
struct width<n, d, w, false>
{
static constexpr unsigned value = 0;
};
template <unsigned exp>
struct static_pow10
{
private:
static constexpr uint64_t h = static_pow10<exp/2>::value;
public:
static constexpr uint64_t value = h * h * (exp % 2 ? 10 : 1);
};
template <>
struct static_pow10<0>
{
static constexpr uint64_t value = 1;
};
template <class Rep, unsigned w, bool in_range = (w < 19)>
struct make_precision
{
using type = chrono::duration<Rep,
ratio<1, static_pow10<w>::value>>;
static constexpr unsigned width = w;
};
template <class Rep, unsigned w>
struct make_precision<Rep, w, false>
{
using type = chrono::duration<Rep, micro>;
static constexpr unsigned width = 6;
};
template <class Duration,
unsigned w = width<common_type<
Duration,
chrono::seconds>::type::period::den>::value>
class decimal_format_seconds
{
public:
using rep = typename common_type<Duration, chrono::seconds>::type::rep;
using precision = typename make_precision<rep, w>::type;
static auto constexpr width = make_precision<rep, w>::width;
private:
chrono::seconds s_;
precision sub_s_;
public:
constexpr decimal_format_seconds()
: s_()
, sub_s_()
{}
constexpr explicit decimal_format_seconds(const Duration& d) noexcept
: s_(chrono::duration_cast<chrono::seconds>(d))
, sub_s_(chrono::duration_cast<precision>(d - s_))
{}
constexpr chrono::seconds& seconds() noexcept {return s_;}
constexpr chrono::seconds seconds() const noexcept {return s_;}
constexpr precision subseconds() const noexcept {return sub_s_;}
constexpr precision to_duration() const noexcept
{
return s_ + sub_s_;
}
constexpr bool in_conventional_range() const noexcept
{
using namespace chrono;
return sub_s_ < chrono::seconds{1} && s_ < minutes{1};
}
template <class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const decimal_format_seconds& x)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
os.flags(ios::dec | ios::right);
os.width(2);
os << x.s_.count() <<
use_facet<numpunct<char>>(os.getloc()).decimal_point();
os.width(width);
os << static_cast<int64_t>(x.sub_s_.count());
return os;
}
};
template <class Duration>
class decimal_format_seconds<Duration, 0>
{
static constexpr unsigned w = 0;
public:
using rep = typename common_type<Duration, chrono::seconds>::type::rep;
using precision = chrono::duration<rep>;
static auto constexpr width = make_precision<rep, w>::width;
private:
chrono::seconds s_;
public:
constexpr decimal_format_seconds() : s_() {}
constexpr explicit decimal_format_seconds(const precision& s) noexcept
: s_(s)
{}
constexpr chrono::seconds& seconds() noexcept {return s_;}
constexpr chrono::seconds seconds() const noexcept {return s_;}
constexpr precision to_duration() const noexcept {return s_;}
constexpr bool in_conventional_range() const noexcept
{
using namespace chrono;
return s_ < minutes{1};
}
template <class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const decimal_format_seconds& x)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
os.flags(ios::dec | ios::right);
os.width(2);
os << x.s_.count();
return os;
}
};
} // namespace detail
template <class Duration> class time_of_day;
template <>
class time_of_day<hours>
{
enum class mode : unsigned char {is24hr, am, pm};
chrono::hours h_{};
mode mode_ = mode::is24hr;
bool neg_ = false;
public:
using precision = chrono::hours;
time_of_day() = default;
explicit constexpr time_of_day(chrono::hours h) noexcept;
constexpr chrono::hours hours() const noexcept;
explicit constexpr operator precision() const noexcept;
constexpr precision to_duration() const noexcept;
constexpr void make24() noexcept;
constexpr void make12() noexcept;
private:
constexpr chrono::hours to24hr() const noexcept;
constexpr bool in_conventional_range() const noexcept;
template<class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const time_of_day& t)
{
detail::save_ostream<CharT, Traits> _(os);
if (t.neg_)
os << '-';
os.fill('0');
os.flags(ios::dec | ios::right);
if (t.mode_ == time_of_day::mode::is24hr)
os.width(2);
os << t.h_.count();
switch (t.mode_)
{
case time_of_day::mode::is24hr:
os << "00";
break;
case time_of_day::mode::am:
os << "AM";
break;
case time_of_day::mode::pm:
os << "PM";
break;
}
return os;
}
template <class D> friend class time_of_day;
};
inline
constexpr
time_of_day<hours>::time_of_day(chrono::hours h) noexcept
: h_{abs(h)}
, neg_{h < 0h}
{
}
inline
constexpr
hours
time_of_day<hours>::hours() const noexcept
{
if (neg_)
return -h_;
return h_;
}
inline
constexpr
time_of_day<hours>::operator precision() const noexcept
{
return to_duration();
}
inline
constexpr
time_of_day<hours>::precision
time_of_day<hours>::to_duration() const noexcept
{
auto h = to24hr();
if (neg_)
h = -h;
return h;
}
inline
constexpr
chrono::hours
time_of_day<hours>::to24hr() const noexcept
{
auto h = h_;
if (mode_ == mode::am || mode_ == mode::pm)
{
if (mode_ == mode::pm)
{
if (h != 12h)
h += 12h;
}
else if (h == 12h)
h = 0h;
}
return h;
}
inline
constexpr
void
time_of_day<hours>::make24() noexcept
{
h_ = to24hr();
mode_ = mode::is24hr;
}
inline
constexpr
void
time_of_day<hours>::make12() noexcept
{
if (mode_ == mode::is24hr && in_conventional_range())
{
if (h_ >= 12h)
{
if (h_ > 12h)
h_ -= 12h;
mode_ = mode::pm;
}
else
{
if (h_ == 0h)
h_ = 12h;
mode_ = mode::am;
}
}
}
inline
constexpr
bool
time_of_day<hours>::in_conventional_range() const noexcept
{
return 0h <= h_ && h_ < 24h;
}
template <>
class time_of_day<minutes>
: private time_of_day<hours>
{
chrono::minutes m_{};
public:
using precision = chrono::minutes;
time_of_day() = default;
explicit constexpr time_of_day(chrono::minutes since_midnight) noexcept;
constexpr chrono::hours hours() const noexcept;
constexpr chrono::minutes minutes() const noexcept;
explicit constexpr operator precision() const noexcept;
constexpr precision to_duration() const noexcept;
constexpr void make24() noexcept;
constexpr void make12() noexcept;
private:
constexpr bool in_conventional_range() const noexcept;
template<class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const time_of_day& t)
{
detail::save_ostream<CharT, Traits> _(os);
if (t.neg_)
os << '-';
os.fill('0');
os.flags(ios::dec | ios::right);
if (t.mode_ == time_of_day::mode::is24hr)
os.width(2);
os << t.h_.count() << ':';
os.width(2);
os << t.m_.count();
switch (t.mode_)
{
case time_of_day::mode::is24hr:
break;
case time_of_day::mode::am:
os << "AM";
break;
case time_of_day::mode::pm:
os << "PM";
break;
}
return os;
}
template <class D> friend class time_of_day;
};
inline
constexpr
time_of_day<minutes>::time_of_day(chrono::minutes m) noexcept
: time_of_day<chrono::hours>{floor<chrono::hours>(m)}
, m_{m - time_of_day<chrono::hours>::to_duration()}
{
}
inline
constexpr
hours
time_of_day<minutes>::hours() const noexcept
{
return time_of_day<chrono::hours>::hours();
}
constexpr
minutes
time_of_day<minutes>::minutes() const noexcept
{
return m_;
}
inline
constexpr
time_of_day<minutes>::operator precision() const noexcept
{
return to_duration();
}
inline
constexpr
time_of_day<minutes>::precision
time_of_day<minutes>::to_duration() const noexcept
{
chrono::minutes m = time_of_day<chrono::hours>::to_duration();
if (m < 0min)
return m - m_;
return m + m_;
}
inline
constexpr
void
time_of_day<minutes>::make24() noexcept
{
time_of_day<chrono::hours>::make24();
}
inline
constexpr
void
time_of_day<minutes>::make12() noexcept
{
time_of_day<chrono::hours>::make12();
}
inline
constexpr
bool
time_of_day<minutes>::in_conventional_range() const noexcept
{
return time_of_day<chrono::hours>::in_conventional_range() && 0min <= m_ && m_ < 60min;
}
template <>
class time_of_day<seconds>
: private time_of_day<minutes>
{
chrono::seconds s_{};
public:
using precision = chrono::seconds;
time_of_day() = default;
explicit constexpr time_of_day(chrono::seconds s) noexcept;
constexpr chrono::hours hours() const noexcept;
constexpr chrono::minutes minutes() const noexcept;
constexpr chrono::seconds seconds() const noexcept;
explicit constexpr operator precision() const noexcept;
constexpr precision to_duration() const noexcept;
constexpr void make24() noexcept;
constexpr void make12() noexcept;
private:
constexpr bool in_conventional_range() const noexcept;
template<class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const time_of_day& t)
{
detail::save_ostream<CharT, Traits> _(os);
if (t.neg_)
os << '-';
os.fill('0');
os.flags(ios::dec | ios::right);
if (t.mode_ == time_of_day::mode::is24hr)
os.width(2);
os << t.h_.count() << ':';
os.width(2);
os << t.m_.count() << ':';
os.width(2);
os << t.s_.count();
switch (t.mode_)
{
case time_of_day::mode::is24hr:
break;
case time_of_day::mode::am:
os << "AM";
break;
case time_of_day::mode::pm:
os << "PM";
break;
}
return os;
}
template <class D> friend class time_of_day;
};
inline
constexpr
time_of_day<seconds>::time_of_day(chrono::seconds s) noexcept
: time_of_day<chrono::minutes>{floor<chrono::minutes>(s)}
, s_{s - time_of_day<chrono::minutes>::to_duration()}
{
}
inline
constexpr
hours
time_of_day<seconds>::hours() const noexcept
{
return time_of_day<chrono::minutes>::hours();
}
constexpr
minutes
time_of_day<seconds>::minutes() const noexcept
{
return time_of_day<chrono::minutes>::minutes();
}
inline
constexpr
time_of_day<seconds>::operator precision() const noexcept
{
return to_duration();
}
inline
constexpr
time_of_day<seconds>::precision
time_of_day<seconds>::to_duration() const noexcept
{
chrono::seconds s = time_of_day<chrono::minutes>::to_duration();
if (s < 0s)
return s - s_;
return s + s_;
}
inline
constexpr
void
time_of_day<seconds>::make24() noexcept
{
time_of_day<chrono::minutes>::make24();
}
inline
constexpr
void
time_of_day<seconds>::make12() noexcept
{
time_of_day<chrono::minutes>::make12();
}
inline
constexpr
bool
time_of_day<seconds>::in_conventional_range() const noexcept
{
return time_of_day<chrono::minutes>::in_conventional_range() && 0s <= s_ && s_ < 60s;
}
template <class Rep, class Period>
class time_of_day<duration<Rep, Period>>
: private time_of_day<minutes>
{
using Duration = chrono::duration<Rep, Period>;
using dfs = detail::decimal_format_seconds<common_type_t<Duration, chrono::seconds>>;
static_assert(!treat_as_floating_point_v<Rep> &&
!is_convertible_v<Duration, chrono::seconds>);
dfs s_;
public:
using precision = typename dfs::precision;
time_of_day() = default;
explicit constexpr time_of_day(Duration s) noexcept;
constexpr chrono::hours hours() const noexcept;
constexpr chrono::minutes minutes() const noexcept;
constexpr chrono::seconds seconds() const noexcept;
constexpr precision subseconds() const noexcept;
explicit constexpr operator precision() const noexcept;
constexpr precision to_duration() const noexcept;
constexpr void make24() noexcept;
constexpr void make12() noexcept;
private:
constexpr bool in_conventional_range() const noexcept;
template<class CharT, class Traits>
friend
basic_ostream<CharT, Traits>&
operator<<(basic_ostream<CharT, Traits>& os, const time_of_day& t)
{
detail::save_ostream<CharT, Traits> _(os);
if (t.neg_)
os << '-';
os.fill('0');
os.flags(ios::dec | ios::right);
if (t.mode_ == time_of_day::mode::is24hr)
os.width(2);
os << t.h_.count() << ':';
os.width(2);
os << t.m_.count() << ':';
os << t.s_;
switch (t.mode_)
{
case time_of_day::mode::is24hr:
break;
case time_of_day::mode::am:
os << "AM";
break;
case time_of_day::mode::pm:
os << "PM";
break;
}
return os;
}
template <class D> friend class time_of_day;
};
template <class Rep, class Period>
inline
constexpr
time_of_day<duration<Rep, Period>>::time_of_day(Duration s) noexcept
: time_of_day<chrono::minutes>{floor<chrono::minutes>(s)}
, s_{s - time_of_day<chrono::minutes>::to_duration()}
{
}
template <class Rep, class Period>
inline
constexpr
hours
time_of_day<duration<Rep, Period>>::hours() const noexcept
{
return time_of_day<chrono::minutes>::hours();
}
template <class Rep, class Period>
inline
constexpr
minutes
time_of_day<duration<Rep, Period>>::minutes() const noexcept
{
return time_of_day<chrono::minutes>::minutes();
}
template <class Rep, class Period>
inline
constexpr
seconds
time_of_day<duration<Rep, Period>>::seconds() const noexcept
{
return s_.seconds();
}
template <class Rep, class Period>
inline
constexpr
typename time_of_day<duration<Rep, Period>>::precision
time_of_day<duration<Rep, Period>>::subseconds() const noexcept
{
return s_.subseconds();
}
template <class Rep, class Period>
inline
constexpr
time_of_day<duration<Rep, Period>>::operator precision() const noexcept
{
return to_duration();
}
template <class Rep, class Period>
inline
constexpr
typename time_of_day<duration<Rep, Period>>::precision
time_of_day<duration<Rep, Period>>::to_duration() const noexcept
{
auto t = time_of_day<chrono::minutes>::to_duration();
if (t < 0min)
return t - s_.to_duration();
return t + s_.to_duration();
}
template <class Rep, class Period>
inline
constexpr
void
time_of_day<duration<Rep, Period>>::make24() noexcept
{
time_of_day<chrono::minutes>::make24();
}
template <class Rep, class Period>
inline
constexpr
void
time_of_day<duration<Rep, Period>>::make12() noexcept
{
time_of_day<chrono::minutes>::make12();
}
template <class Rep, class Period>
inline
constexpr
bool
time_of_day<duration<Rep, Period>>::in_conventional_range() const noexcept
{
return time_of_day<chrono::minutes>::in_conventional_range()
&& s_.in_conventional_range();
}
template <class Duration> time_of_day(Duration) -> time_of_day<Duration>;
} // namespace chrono
} // namespace std
#include <iostream>
int
main()
{
using namespace std::chrono;
time_of_day tod{500'000'000ms};
std::cout << tod << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment