Created
October 16, 2018 02:25
-
-
Save HowardHinnant/bc8de3b5fbead3118cb47b20222cbd24 to your computer and use it in GitHub Desktop.
time_of_day prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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