diff --git a/date.h b/date.h index ea08a6f..587b7b9 100644 --- a/date.h +++ b/date.h @@ -3460,6 +3460,11 @@ private: precision sub_s_; public: + CONSTCD11 decimal_format_seconds() + : s_() + , sub_s_() + {} + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT : s_(std::chrono::duration_cast(d)) , sub_s_(std::chrono::duration_cast(d - s_)) @@ -3503,10 +3508,15 @@ private: std::chrono::seconds s_; public: + CONSTCD11 decimal_format_seconds() : s_() {}; CONSTCD11 explicit decimal_format_seconds(const precision& s) NOEXCEPT : s_(s) {} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD14 precision to_duration() const NOEXCEPT {return s_;} + template friend std::basic_ostream& @@ -3521,6 +3531,22 @@ public: } }; +template +struct fields; + +template +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr); + +template +void +parse(std::basic_istream& is, + const CharT* fmt, fields& fds, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr); + enum class classify { not_valid, @@ -3580,6 +3606,13 @@ protected: enum {is24hr}; + CONSTCD11 time_of_day_base() NOEXCEPT + : h_(0) + , mode_(static_cast(is24hr)) + , neg_(false) + {} + + CONSTCD11 time_of_day_base(std::chrono::hours h, bool neg, unsigned m) NOEXCEPT : h_(detail::abs(h)) , mode_(static_cast(m)) @@ -3656,6 +3689,8 @@ class time_of_day_storage, detail::classify:: public: using precision = std::chrono::hours; + CONSTCD11 explicit time_of_day_storage() NOEXCEPT = default; + CONSTCD11 explicit time_of_day_storage(std::chrono::hours since_midnight) NOEXCEPT : base(since_midnight, since_midnight < std::chrono::hours{0}, is24hr) {} @@ -3724,6 +3759,11 @@ class time_of_day_storage, detail::classify:: public: using precision = std::chrono::minutes; + CONSTCD11 explicit time_of_day_storage() NOEXCEPT + : base() + , m_(0) + {} + CONSTCD11 explicit time_of_day_storage(std::chrono::minutes since_midnight) NOEXCEPT : base(std::chrono::duration_cast(since_midnight), since_midnight < std::chrono::minutes{0}, is24hr) @@ -3790,13 +3830,20 @@ class time_of_day_storage, detail::classify:: : private detail::time_of_day_base { using base = detail::time_of_day_base; + using dfs = decimal_format_seconds; std::chrono::minutes m_; - std::chrono::seconds s_; + dfs s_; public: using precision = std::chrono::seconds; + CONSTCD11 explicit time_of_day_storage() NOEXCEPT + : base() + , m_(0) + , s_() + {} + CONSTCD11 explicit time_of_day_storage(std::chrono::seconds since_midnight) NOEXCEPT : base(std::chrono::duration_cast(since_midnight), since_midnight < std::chrono::seconds{0}, is24hr) @@ -3813,13 +3860,13 @@ public: CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_.seconds();} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} CONSTCD14 explicit operator precision() const NOEXCEPT { - auto p = to24hr() + s_ + m_; + auto p = to24hr() + s_.to_duration() + m_; if (neg_) p = -p; return p; @@ -3848,9 +3895,7 @@ public: os.width(2); os << t.h_.count() << ':'; os.width(2); - os << t.m_.count() << ':'; - os.width(2); - os << t.s_.count(); + os << t.m_.count() << ':' << t.s_; switch (t.mode_) { case am: @@ -3862,6 +3907,20 @@ public: } return os; } + + template + friend + void + detail::to_stream(std::basic_ostream& os, const CharT* fmt, + const detail::fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template + friend + void + detail::parse(std::basic_istream& is, const CharT* fmt, + detail::fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset); }; template @@ -3871,7 +3930,7 @@ class time_of_day_storage, detail::classify:: public: using Duration = std::chrono::duration; using dfs = decimal_format_seconds::type>; + std::chrono::seconds>::type>; using precision = typename dfs::precision; private: @@ -3881,6 +3940,12 @@ private: dfs s_; public: + CONSTCD11 explicit time_of_day_storage() NOEXCEPT + : base() + , m_(0) + , s_() + {} + CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT : base(std::chrono::duration_cast(since_midnight), since_midnight < Duration{0}, is24hr) @@ -3946,6 +4011,20 @@ public: } return os; } + + template + friend + void + detail::to_stream(std::basic_ostream& os, const CharT* fmt, + const detail::fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template + friend + void + detail::parse(std::basic_istream& is, const CharT* fmt, + detail::fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset); }; } // namespace detail @@ -4054,333 +4133,18 @@ operator<<(std::basic_ostream& os, const local_time& ut namespace detail { -template -struct range +template +struct fields { - const CharT* beg; - const CharT* end; - - range(const CharT* begin, const CharT* theend) - : beg(begin) - , end(theend) - {} + year_month_day ymd; + time_of_day tod; }; -template -void -to_stream(std::basic_ostream& os, detail::range r, - const std::chrono::duration& d) -{ - using namespace std; - using namespace std::chrono; - tm tm; - auto& facet = use_facet>(os.getloc()); - auto command = false; - CharT modified = CharT{}; - for (auto fmt = r.beg; fmt < r.end; ++fmt) - { - if (!command && modified != CharT{}) - throw std::logic_error("loop invariant broken: !command && modified"); - else if (modified != CharT{} && modified != CharT{'E'} && modified != CharT{'O'}) - throw std::logic_error(std::string("bad value for modified: ") + char(modified)); - switch (*fmt) - { - case 'X': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - tm = std::tm{}; - auto hms = make_time(floor(d)); - tm.tm_sec = static_cast(hms.seconds().count()); - tm.tm_min = static_cast(hms.minutes().count()); - tm.tm_hour = static_cast(hms.hours().count()); - CharT f[3] = {'%'}; - auto fe = begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, begin(f), fe); - } - command = false; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'H': - case 'I': - if (command) - { - auto hms = make_time(floor(d)); - if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_hour = static_cast(hms.hours().count()); - facet.put(os, os, os.fill(), &tm, begin(f), end(f)); - modified = CharT{}; - } - else if (modified == CharT{}) - { - if (*fmt == CharT{'I'}) - hms.make12(); - if (hms.hours() < hours{10}) - os << CharT{'0'}; - os << hms.hours().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'M': - if (command) - { - auto hms = make_time(floor(d)); - if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_min = static_cast(hms.minutes().count()); - facet.put(os, os, os.fill(), &tm, begin(f), end(f)); - modified = CharT{}; - } - else if (modified == CharT{}) - { - if (hms.minutes() < minutes{10}) - os << CharT{'0'}; - os << hms.minutes().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'n': - if (command) - { - if (modified == CharT{}) - os << CharT{'\n'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - auto h = floor(d); - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(h.count()); - facet.put(os, os, os.fill(), &tm, begin(f), end(f)); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { - auto hms = make_time(floor(d)); - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(hms.hours().count()); - tm.tm_min = static_cast(hms.minutes().count()); - tm.tm_sec = static_cast(hms.seconds().count()); - facet.put(os, os, os.fill(), &tm, begin(f), end(f)); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - auto hms = make_time(floor(d)); - if (hms.hours() < hours{10}) - os << CharT{'0'}; - os << hms.hours().count() << CharT{':'}; - if (hms.minutes() < minutes{10}) - os << CharT{'0'}; - os << hms.minutes().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'S': - if (command) - { - if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - auto hms = make_time(floor(d)); - tm.tm_sec = static_cast(hms.seconds().count()); - facet.put(os, os, os.fill(), &tm, begin(f), end(f)); - modified = CharT{}; - } - else if (modified == CharT{}) - { - auto fs = d % minutes{1}; - os << detail::decimal_format_seconds(fs); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 't': - if (command) - { - if (modified == CharT{}) - os << CharT{'\t'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - using CT = typename common_type>::type; - os << time_of_day{d}; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = false; - } - else - os << *fmt; - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - os << CharT{'%'} << modified << *fmt; - command = false; - modified = CharT{}; - } - } - else - os << *fmt; - break; - case '%': - if (command) - { - if (modified == CharT{}) - { - os << CharT{'%'}; - command = false; - } - else - { - os << CharT{'%'} << modified << CharT{'%'}; - command = false; - modified = CharT{}; - } - } - else - command = true; - break; - default: - if (command) - { - os << CharT{'%'}; - command = false; - } - if (modified != CharT{}) - { - os << modified; - modified = CharT{}; - } - os << *fmt; - break; - } - } - if (command) - os << CharT{'%'}; - if (modified != CharT{}) - os << modified; -} - -} // namespace detail - -template -inline -void -to_stream(std::basic_ostream& os, const CharT* fmt, - const std::chrono::duration& d) -{ - using detail::range; - using Tr = std::char_traits; - detail::to_stream(os, range{fmt, fmt + Tr::length(fmt)}, d); -} - template void to_stream(std::basic_ostream& os, const CharT* fmt, - const local_time& tp, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr) + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec) { using namespace std; using namespace std::chrono; @@ -4396,33 +4160,13 @@ to_stream(std::basic_ostream& os, const CharT* fmt, throw std::logic_error(std::string("bad value for modified: ") + char(modified)); switch (*fmt) { - case 'H': - case 'I': - case 'M': - case 'p': - case 'r': - case 'R': - case 'S': - case 't': - case 'T': - case 'X': - if (command) - { - detail::to_stream(os, {command, fmt+1}, tp - floor(tp)); - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; case 'a': case 'A': if (command) { if (modified == CharT{}) { - tm.tm_wday = static_cast(static_cast( - weekday{floor(tp)})); + tm.tm_wday = static_cast(unsigned(weekday{fds.ymd})); const CharT f[] = {'%', *fmt}; facet.put(os, os, os.fill(), &tm, begin(f), end(f)); } @@ -4443,8 +4187,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { if (modified == CharT{}) { - tm.tm_mon = static_cast(static_cast( - year_month_day{floor(tp)}.month())) - 1; + tm.tm_mon = static_cast(unsigned(fds.ymd.month())) - 1; const CharT f[] = {'%', *fmt}; facet.put(os, os, os.fill(), &tm, begin(f), end(f)); } @@ -4467,9 +4210,9 @@ to_stream(std::basic_ostream& os, const CharT* fmt, else { tm = std::tm{}; - auto ld = floor(tp); - auto ymd = year_month_day{ld}; - auto hms = make_time(floor(tp - ld)); + auto const& ymd = fds.ymd; + time_of_day hms{floor(fds.tod.to_duration())}; + auto ld = local_days(ymd); tm.tm_sec = static_cast(hms.seconds().count()); tm.tm_min = static_cast(hms.minutes().count()); tm.tm_hour = static_cast(hms.hours().count()); @@ -4494,7 +4237,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'C': if (command) { - auto y = static_cast(year_month_day{floor(tp)}.year()); + auto y = static_cast(fds.ymd.year()); if (modified == CharT{'E'}) { tm.tm_year = y - 1900; @@ -4532,8 +4275,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'e': if (command) { - auto d = static_cast(static_cast( - year_month_day{floor(tp)}.day())); + auto d = static_cast(static_cast(fds.ymd.day())); if (modified == CharT{'O'}) { tm.tm_mday = d; @@ -4564,7 +4306,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { if (modified == CharT{}) { - auto ymd = year_month_day{floor(tp)}; + auto const& ymd = fds.ymd; detail::save_stream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); @@ -4589,7 +4331,18 @@ to_stream(std::basic_ostream& os, const CharT* fmt, if (command) { if (modified == CharT{}) - os << floor(tp); + { + auto const& ymd = fds.ymd; + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(4); + os << static_cast(ymd.year()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.month()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.day()); + } else { os << CharT{'%'} << modified << *fmt; @@ -4606,7 +4359,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { if (modified == CharT{}) { - auto ld = floor(tp); + auto ld = local_days(fds.ymd); auto y = year_month_day{ld + days{3}}.year(); auto start = local_days((y - years{1})/date::dec/thu[last]) + (mon-thu); if (ld < start) @@ -4632,13 +4385,43 @@ to_stream(std::basic_ostream& os, const CharT* fmt, else os << *fmt; break; + case 'H': + case 'I': + if (command) + { + auto hms = fds.tod; + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + if (*fmt == CharT{'I'}) + hms.make12(); + if (hms.hours() < hours{10}) + os << CharT{'0'}; + os << hms.hours().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; case 'j': if (command) { if (modified == CharT{}) { - auto ld = floor(tp); - auto y = year_month_day{ld}.year(); + auto ld = local_days(fds.ymd); + auto y = fds.ymd.year(); auto doy = ld - local_days(y/jan/1) + days{1}; detail::save_stream _(os); os.fill('0'); @@ -4659,7 +4442,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'm': if (command) { - auto m = static_cast(year_month_day{floor(tp)}.month()); + auto m = static_cast(fds.ymd.month()); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; @@ -4683,6 +4466,33 @@ to_stream(std::basic_ostream& os, const CharT* fmt, else os << *fmt; break; + case 'M': + if (command) + { + time_of_day hms{floor(fds.tod.to_duration())}; + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_min = static_cast(hms.minutes().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + if (hms.minutes() < minutes{10}) + os << CharT{'0'}; + os << hms.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; case 'n': if (command) { @@ -4698,10 +4508,130 @@ to_stream(std::basic_ostream& os, const CharT* fmt, else os << *fmt; break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(fds.tod.hours().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { + time_of_day hms{floor(fds.tod.to_duration())}; + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + tm.tm_min = static_cast(hms.minutes().count()); + tm.tm_sec = static_cast(hms.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + time_of_day hms{floor(fds.tod.to_duration())}; + if (hms.hours() < hours{10}) + os << CharT{'0'}; + os << hms.hours().count() << CharT{':'}; + if (hms.minutes() < minutes{10}) + os << CharT{'0'}; + os << hms.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'S': + if (command) + { + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + os << fds.tod.s_; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 't': + if (command) + { + if (modified == CharT{}) + os << CharT{'\t'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + os << fds.tod; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; case 'u': if (command) { - auto wd = static_cast(weekday{floor(tp)}); + auto wd = static_cast(weekday{fds.ymd}); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; @@ -4726,8 +4656,8 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'U': if (command) { - auto ld = floor(tp); - auto ymd = year_month_day{ld}; + auto const& ymd = fds.ymd; + auto ld = local_days(ymd); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; @@ -4763,11 +4693,11 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'V': if (command) { - auto ld = floor(tp); + auto ld = local_days(fds.ymd); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; - auto ymd = year_month_day{ld}; + auto const& ymd = fds.ymd; tm.tm_year = static_cast(ymd.year()) - 1900; tm.tm_wday = static_cast(static_cast(weekday{ld})); tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); @@ -4801,7 +4731,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'w': if (command) { - auto wd = static_cast(weekday{floor(tp)}); + auto wd = static_cast(weekday{fds.ymd}); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; @@ -4826,8 +4756,8 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'W': if (command) { - auto ld = floor(tp); - auto ymd = year_month_day{ld}; + auto const& ymd = fds.ymd; + auto ld = local_days(ymd); if (modified == CharT{'O'}) { const CharT f[] = {'%', modified, *fmt}; @@ -4860,10 +4790,36 @@ to_stream(std::basic_ostream& os, const CharT* fmt, else os << *fmt; break; + case 'X': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + tm = std::tm{}; + using CT = typename common_type::type; + time_of_day hms{duration_cast(fds.tod.to_duration())}; + tm.tm_sec = static_cast(hms.seconds().count()); + tm.tm_min = static_cast(hms.minutes().count()); + tm.tm_hour = static_cast(hms.hours().count()); + CharT f[3] = {'%'}; + auto fe = begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, begin(f), fe); + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; case 'y': if (command) { - auto y = static_cast(year_month_day{floor(tp)}.year()); + auto y = static_cast(fds.ymd.year()); if (modified != CharT{}) { const CharT f[] = {'%', modified, *fmt}; @@ -4886,7 +4842,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, case 'Y': if (command) { - auto y = year_month_day{floor(tp)}.year(); + auto y = fds.ymd.year(); if (modified == CharT{'E'}) { const CharT f[] = {'%', modified, *fmt}; @@ -5013,14 +4969,43 @@ to_stream(std::basic_ostream& os, const CharT* fmt, os << modified; } +} // namespace detail + +template +inline +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const std::chrono::duration& d) +{ + using Duration = std::chrono::duration; + using CT = typename std::common_type::type; + detail::fields fds{year_month_day{}, time_of_day{d}}; + detail::to_stream(os, fmt, fds); +} + +template +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const local_time& tp, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr) +{ + using CT = typename std::common_type::type; + auto ld = floor(tp); + detail::fields fds{year_month_day{ld}, time_of_day{tp-ld}}; + detail::to_stream(os, fmt, fds, abbrev, offset_sec); +} + template void to_stream(std::basic_ostream& os, const CharT* fmt, const sys_time& tp) { + using CT = typename std::common_type::type; const std::string abbrev("UTC"); CONSTDATA std::chrono::seconds offset{0}; - to_stream(os, fmt, local_time{tp.time_since_epoch()}, &abbrev, &offset); + auto sd = floor(tp); + detail::fields fds{year_month_day{sd}, time_of_day{tp-sd}}; + detail::to_stream(os, fmt, fds, &abbrev, &offset); } // format @@ -5381,378 +5366,10 @@ read(std::basic_istream& is, rld a0, Args&& ...args) read(is, std::forward(args)...); } -template -void -parse(std::basic_istream& is, range r, - std::chrono::duration& d) -{ - using namespace std; - using namespace std::chrono; - typename basic_istream::sentry ok{is}; - using Duration = std::chrono::duration; - if (ok) - { - auto& f = use_facet>(is.getloc()); - std::tm tm{}; - const CharT* command = nullptr; - auto modified = CharT{}; - auto width = -1; - CONSTDATA int not_a_hour_12_value = 0; - int I = not_a_hour_12_value; - hours h{}; - minutes min{}; - Duration s{}; - using detail::read; - using detail::rs; - using detail::ru; - using detail::rld; - for (auto fmt = r.beg; fmt < r.end && is.rdstate() == std::ios::goodbit; ++fmt) - { - if (isspace(*fmt)) - { - // space matches 0 or more white space characters - ws(is); - continue; - } - switch (*fmt) - { - case 'H': - if (command) - { - if (modified == CharT{}) - { - int H; - read(is, ru{H, 1, width == -1 ? 2u : width}); - if (!is.fail()) - h = hours{H}; - } - else if (modified == CharT{'O'}) - { - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - h = hours{tm.tm_hour}; - is.setstate(err); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'I': - if (command) - { - if (modified == CharT{}) - { - // reads in an hour into I, but most be in [1, 12] - read(is, rs{I, 1, width == -1 ? 2u : width}); - if (I != not_a_hour_12_value) - { - if (!(1 <= I && I <= 12)) - { - I = not_a_hour_12_value; - goto broken; - } - } - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'M': - if (command) - { - if (modified == CharT{}) - { - int M; - read(is, ru{M, 1, width == -1 ? 2u : width}); - if (!is.fail()) - min = minutes{M}; - } - else if (modified == CharT{'O'}) - { - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - min = minutes{tm.tm_min}; - is.setstate(err); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'n': - case 't': - if (command) - { - // %n and %t match 1 or more white space characters - // consecutive %n and %t count as one - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - if (!isspace(ic)) - { - is.setstate(ios::failbit); - break; - } - ws(is); - for (++fmt; *fmt == 'n' || *fmt == 't'; ++fmt) - ; - --fmt; - } - else - read(is, *fmt); - break; - case 'p': - // Error if haven't yet seen %I - if (command) - { - if (modified == CharT{}) - { - if (I == not_a_hour_12_value) - goto broken; - tm.tm_hour = I; - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - if (!(err & ios::failbit)) - { - h = hours{tm.tm_hour}; - I = not_a_hour_12_value; - } - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - - break; - case 'r': - if (command) - { - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - command = nullptr; - width = -1; - modified = CharT{}; - if ((err & ios::failbit) == 0) - { - h = hours{tm.tm_hour}; - min = minutes{tm.tm_min}; - s = duration_cast(seconds{tm.tm_sec}); - } - is.setstate(err); - } - else - read(is, *fmt); - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - int H, M; - read(is, ru{H, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, - ru{M, 1, 2}, CharT{'\0'}); - if (!is.fail()) - { - h = hours{H}; - min = minutes{M}; - } - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'S': - if (command) - { - if (modified == CharT{}) - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S; - read(is, rld{S, 1, width == -1 ? w : width}); - if (!is.fail()) - s = round(duration{S}); - } - else if (modified == CharT{'O'}) - { - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - s = duration_cast(seconds{tm.tm_sec}); - is.setstate(err); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int H; - int M; - long double S; - read(is, ru{H, 1, 2}, CharT{':'}, ru{M, 1, 2}, - CharT{':'}, rld{S, 1, w}); - if (!is.fail()) - { - h = hours{H}; - min = minutes{M}; - s = round(duration{S}); - } - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'X': - if (command) - { - ios_base::iostate err = ios_base::goodbit; - f.get(is, 0, is, err, &tm, command, fmt+1); - command = nullptr; - width = -1; - modified = CharT{}; - if ((err & ios::failbit) == 0) - { - h = hours{tm.tm_hour}; - min = minutes{tm.tm_min}; - s = duration_cast(seconds{tm.tm_sec}); - } - is.setstate(err); - } - else - read(is, *fmt); - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else - read(is, *fmt); - break; - case '%': - if (command) - { - if (modified == CharT{}) - read(is, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - command = fmt; - break; - default: - if (command) - { - if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') - { - width = static_cast(*fmt) - '0'; - while ('0' <= fmt[1] && fmt[1] <= '9') - width = 10*width + static_cast(*++fmt) - '0'; - } - else - { - if (modified == CharT{}) - read(is, CharT{'%'}, width, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else - read(is, *fmt); - break; - } - } - // is.rdstate() != ios::goodbit || *fmt == CharT{} - if (is.rdstate() == ios::goodbit && command) - { - if (modified == CharT{}) - read(is, CharT{'%'}, width); - else - read(is, CharT{'%'}, width, modified); - } - if (!is.fail()) - d = floor(h + min + s); - return; - } -broken: - is.setstate(ios_base::failbit); -} - -template -inline -void -parse(std::basic_istream& is, - const CharT* fmt, std::chrono::duration& d) -{ - using detail::range; - using Tr = std::char_traits; - parse(is, range{fmt, fmt + Tr::length(fmt)}, d); -} - template void -parse(std::basic_istream& is, - const CharT* fmt, local_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) +parse(std::basic_istream& is, const CharT* fmt, fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset) { using namespace std; using namespace std::chrono; @@ -5980,7 +5597,23 @@ parse(std::basic_istream& is, case 'H': if (command) { - parse(is, {command, fmt+1}, h); + if (modified == CharT{}) + { + int H; + read(is, ru{H, 1, width == -1 ? 2u : width}); + if (!is.fail()) + h = hours{H}; + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + h = hours{tm.tm_hour}; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; @@ -6030,7 +5663,23 @@ parse(std::basic_istream& is, case 'M': if (command) { - parse(is, {command, fmt+1}, min); + if (modified == CharT{}) + { + int M; + read(is, ru{M, 1, width == -1 ? 2u : width}); + if (!is.fail()) + min = minutes{M}; + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + min = minutes{tm.tm_min}; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; @@ -6134,14 +5783,19 @@ parse(std::basic_istream& is, case 'R': if (command) { - minutes temp; - parse(is, {command, fmt+1}, temp); - if (!is.fail()) + if (modified == CharT{}) { - h = floor(temp); - temp -= h; - min = temp; + int H, M; + read(is, ru{H, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, + ru{M, 1, 2}, CharT{'\0'}); + if (!is.fail()) + { + h = hours{H}; + min = minutes{M}; + } } + else + read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; @@ -6152,7 +5806,25 @@ parse(std::basic_istream& is, case 'S': if (command) { - parse(is, {command, fmt+1}, s); + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + read(is, rld{S, 1, width == -1 ? w : width}); + if (!is.fail()) + s = round(duration{S}); + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + s = duration_cast(seconds{tm.tm_sec}); + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; @@ -6163,16 +5835,24 @@ parse(std::basic_istream& is, case 'T': if (command) { - typename common_type::type temp; - parse(is, {command, fmt+1}, temp); - if (!is.fail()) + if (modified == CharT{}) { - h = floor(temp); - temp -= h; - min = floor(temp); - temp -= min; - s = duration_cast(temp); + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int H; + int M; + long double S; + read(is, ru{H, 1, 2}, CharT{':'}, ru{M, 1, 2}, + CharT{':'}, rld{S, 1, w}); + if (!is.fail()) + { + h = hours{H}; + min = minutes{M}; + s = round(duration{S}); + } } + else + read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; @@ -6564,19 +6244,18 @@ parse(std::basic_istream& is, auto ymd = year{Y}/m/d; if (!ymd.ok()) goto broken; - auto ld = local_days(ymd); - if (wd != not_a_weekday && - weekday{static_cast(wd)} != weekday{ld}) - goto broken; - tp = local_time{floor(ld + h + min + s)}; + if (wd != not_a_weekday) + { + if (weekday{static_cast(wd)} != weekday(ymd)) + goto broken; + } + fds.ymd = ymd; } else goto broken; } - else // did not parse a year - { - goto broken; - } + fds.tod = time_of_day(hours{h} + minutes{min}); + fds.tod.s_ = decimal_format_seconds{s}; if (abbrev != nullptr) *abbrev = std::move(temp_abbrev); if (offset != nullptr) @@ -6588,15 +6267,6 @@ broken: is.setstate(ios_base::failbit); } -template -inline -void -parse(std::basic_istream& is, const CharT* fmt, local_time& tp, - std::chrono::minutes* offset) -{ - parse(is, fmt, tp, static_cast*>(nullptr), offset); -} - template > struct parse_local_manip { @@ -6623,7 +6293,15 @@ std::basic_istream& operator>>(std::basic_istream& is, const parse_local_manip& x) { - parse(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + fields fds{}; + parse(is, x.format_.c_str(), fds, x.abbrev_, x.offset_); + if (!fds.ymd.ok()) + is.setstate(ios::failbit); + if (!is.fail()) + x.tp_ = local_days(fds.ymd) + duration_cast(fds.tod.to_duration()); return is; } @@ -6652,12 +6330,18 @@ std::basic_istream& operator>>(std::basic_istream& is, const parse_sys_manip& x) { - std::chrono::minutes offset{}; + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + minutes offset{}; auto offptr = x.offset_ ? x.offset_ : &offset; - local_time lt; - parse(is, x.format_.c_str(), lt, x.abbrev_, offptr); + fields fds{}; + parse(is, x.format_.c_str(), fds, x.abbrev_, offptr); + if (!fds.ymd.ok()) + is.setstate(ios::failbit); if (!is.fail()) - x.tp_ = sys_time{floor(lt - *offptr).time_since_epoch()}; + x.tp_ = sys_days(fds.ymd) + + duration_cast(fds.tod.to_duration() - *offptr); return is; } @@ -6681,7 +6365,15 @@ std::basic_istream& operator>>(std::basic_istream& is, const parse_duration& x) { - parse(is, x.format_.c_str(), x.d_); + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + fields fds{}; + parse(is, x.format_.c_str(), fds); + if (fds.ymd.ok()) + is.setstate(ios::failbit); + if (!is.fail()) + x.d_ = duration_cast(fds.tod.to_duration()); return is; } @@ -6784,6 +6476,8 @@ parse(const std::basic_string& format, return {format, d}; } +#if 0 // use in >> parse(format, x); instead + template inline void @@ -6899,6 +6593,8 @@ parse(std::basic_istream& is, detail::parse(is, format.c_str(), tp, &abbrev, &offset); } +#endif + // const CharT* formats template @@ -6995,6 +6691,8 @@ parse(const CharT* format, std::chrono::duration& d) return {format, d}; } +#if 0 // use in >> parse(format, x); instead + template inline void @@ -7103,6 +6801,8 @@ parse(std::basic_istream& is, const CharT* format, detail::parse(is, format, tp, &abbrev, &offset); } +#endif + } // namespace date #endif // DATE_H diff --git a/test/date_test/detail/decimal_format_seconds.pass.cpp b/test/date_test/detail/decimal_format_seconds.pass.cpp new file mode 100644 index 0000000..426ff0b --- /dev/null +++ b/test/date_test/detail/decimal_format_seconds.pass.cpp @@ -0,0 +1,145 @@ +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// template ::type::period::den>::value> +// class decimal_format_seconds +// { +// public: +// using precision = typename make_precision::type; +// static auto constexpr width = make_precision::width; +// +// private: +// std::chrono::seconds s_; +// precision sub_s_; +// +// public: +// constexpr explicit decimal_format_seconds(const Duration& d) noexcept; +// +// constexpr std::chrono::seconds& seconds() noexcept; +// constexpr std::chrono::seconds seconds() const noexcept; +// constexpr precision subseconds() const noexcept; +// constexpr precision to_duration() const noexcept; +// +// template +// friend +// std::basic_ostream& +// operator<<(std::basic_ostream& os, const decimal_format_seconds& x); +// }; + +#include "date.h" + +#include +#include +#include + +using fortnights = std::chrono::duration, + date::weeks::period>>; + +using microfortnights = std::chrono::duration>; + +int +main() +{ + using namespace date::detail; + using namespace std; + using namespace std::chrono; + + { + using D = decimal_format_seconds; + static_assert(is_same{}, ""); + static_assert(D::width == 0, ""); + D dfs{minutes{3}}; + assert(dfs.seconds() == seconds{180}); + assert(dfs.to_duration() == seconds{180}); + ostringstream out; + out << dfs; + assert(out.str() == "180"); + } + { + using D = decimal_format_seconds; + static_assert(is_same{}, ""); + static_assert(D::width == 0, ""); + D dfs{seconds{3}}; + assert(dfs.seconds() == seconds{3}); + assert(dfs.to_duration() == seconds{3}); + ostringstream out; + out << dfs; + assert(out.str() == "03"); + } + { + using D = decimal_format_seconds; + static_assert(D::width == 3, ""); + static_assert(is_same::type>{}, ""); + D dfs{seconds{3}}; + assert(dfs.seconds() == seconds{3}); + assert(dfs.to_duration() == seconds{3}); + assert(dfs.subseconds() == milliseconds{0}); + ostringstream out; + out << dfs; + assert(out.str() == "03.000"); + } + { + using D = decimal_format_seconds; + static_assert(D::width == 3, ""); + static_assert(is_same::type>{}, ""); + D dfs{milliseconds{3}}; + assert(dfs.seconds() == seconds{0}); + assert(dfs.to_duration() == milliseconds{3}); + assert(dfs.subseconds() == milliseconds{3}); + ostringstream out; + out << dfs; + assert(out.str() == "00.003"); + } + { + using D = decimal_format_seconds; + static_assert(D::width == 4, ""); + using S = make_precision::type; + static_assert(is_same{}, ""); + D dfs{microfortnights{3}}; + assert(dfs.seconds() == seconds{3}); + assert(dfs.to_duration() == S{36288}); + assert(dfs.subseconds() == S{6288}); + ostringstream out; + out << dfs; + assert(out.str() == "03.6288"); + } + { + using CT = common_type::type; + using D = decimal_format_seconds; + static_assert(D::width == 4, ""); + using S = make_precision::type; + static_assert(is_same{}, ""); + D dfs{microfortnights{3}}; + assert(dfs.seconds() == seconds{3}); + assert(dfs.to_duration() == S{36288}); + assert(dfs.subseconds() == S{6288}); + ostringstream out; + out << dfs; + assert(out.str() == "03.6288"); + } +} diff --git a/test/date_test/detail/make_precision.pass.cpp b/test/date_test/detail/make_precision.pass.cpp new file mode 100644 index 0000000..bac8609 --- /dev/null +++ b/test/date_test/detail/make_precision.pass.cpp @@ -0,0 +1,63 @@ +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// template +// struct make_precision +// { +// using type = std::chrono::duration::value>>; +// static constexpr unsigned width = w; +// }; + +#include "date.h" + +#include +#include + +int +main() +{ + using namespace date::detail; + using namespace std; + using namespace std::chrono; + + static_assert(make_precision<0>::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<2>::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<18>::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<20>::width == 6, ""); + static_assert(is_same::type, microseconds>{}, ""); +} diff --git a/test/date_test/detail/static_pow10.pass.cpp b/test/date_test/detail/static_pow10.pass.cpp new file mode 100644 index 0000000..fe44fa4 --- /dev/null +++ b/test/date_test/detail/static_pow10.pass.cpp @@ -0,0 +1,50 @@ +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// template +// struct static_pow10 +// { +// static constepxr std::uint64_t value = ...; +// }; + +#include "date.h" + +#include +#include +#include + +int +main() +{ + using namespace date::detail; + static_assert(static_pow10<0>::value == 1, ""); + static_assert(static_pow10<1>::value == 10, ""); + static_assert(static_pow10<2>::value == 100, ""); + static_assert(static_pow10<3>::value == 1000, ""); + static_assert(static_pow10<4>::value == 10000, ""); + static_assert(static_pow10<5>::value == 100000, ""); + static_assert(static_pow10<6>::value == 1000000, ""); + static_assert(static_pow10<7>::value == 10000000, ""); + static_assert(static_pow10<8>::value == 100000000, ""); + static_assert(static_pow10<9>::value == 1000000000, ""); + static_assert(static_pow10<10>::value == 10000000000, ""); +} diff --git a/test/date_test/detail/width.pass.cpp b/test/date_test/detail/width.pass.cpp new file mode 100644 index 0000000..6fbb7b9 --- /dev/null +++ b/test/date_test/detail/width.pass.cpp @@ -0,0 +1,64 @@ +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// width::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 +// +// struct width +// { +// static constexpr unsigned value = ...; +// }; + +#include "date.h" + +#include +#include +#include + +int +main() +{ + using namespace date::detail; + static_assert(width<0>::value == 0, ""); + static_assert(width<1>::value == 0, ""); + static_assert(width<2>::value == 1, ""); + static_assert(width<3>::value == 19, ""); + static_assert(width<4>::value == 2, ""); + static_assert(width<5>::value == 1, ""); + static_assert(width<6>::value == 19, ""); + static_assert(width<7>::value == 19, ""); + static_assert(width<8>::value == 3, ""); + static_assert(width<9>::value == 19, ""); + static_assert(width<10>::value == 1, ""); + static_assert(width<100>::value == 2, ""); + static_assert(width<1000>::value == 3, ""); + static_assert(width<10000>::value == 4, ""); + static_assert(width<625>::value == 4, ""); +} diff --git a/test/date_test/parse.pass.cpp b/test/date_test/parse.pass.cpp index 997cead..aa2be53 100644 --- a/test/date_test/parse.pass.cpp +++ b/test/date_test/parse.pass.cpp @@ -34,7 +34,7 @@ test_a() // correct abbreviation std::istringstream in{"Sun 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -44,7 +44,7 @@ test_a() // correct abbreviation std::istringstream in{"Sun 2016-12-11"}; sys_days tp; - parse(in, "%A %F", tp); + in >> parse("%A %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -54,7 +54,7 @@ test_a() // correct full name std::istringstream in{"Sunday 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -64,7 +64,7 @@ test_a() // correct full name std::istringstream in{"Sunday 2016-12-11"}; sys_days tp; - parse(in, "%A %F", tp); + in >> parse("%A %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -74,7 +74,7 @@ test_a() // not a valid name std::istringstream in{"Dec 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert( in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -84,7 +84,7 @@ test_a() // wrong name std::istringstream in{"Sat 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert( in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -94,7 +94,7 @@ test_a() // extra ws in input std::istringstream in{"Sun 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -104,7 +104,7 @@ test_a() // extra ws in format std::istringstream in{"Sun 2016-12-11"}; sys_days tp; - parse(in, "%a %F", tp); + in >> parse("%a %F", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -120,7 +120,7 @@ test_b() // correct abbreviation std::istringstream in{"Dec 11 2016"}; sys_days tp; - parse(in, "%b %d %Y", tp); + in >> parse("%b %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -130,7 +130,7 @@ test_b() // correct abbreviation std::istringstream in{"Dec 11 2016"}; sys_days tp; - parse(in, "%B %d %Y", tp); + in >> parse("%B %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -140,7 +140,7 @@ test_b() // correct abbreviation std::istringstream in{"Dec 11 2016"}; sys_days tp; - parse(in, "%h %d %Y", tp); + in >> parse("%h %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -150,7 +150,7 @@ test_b() // correct full name std::istringstream in{"December 11 2016"}; sys_days tp; - parse(in, "%b %d %Y", tp); + in >> parse("%b %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -160,7 +160,7 @@ test_b() // correct full name std::istringstream in{"December 11 2016"}; sys_days tp; - parse(in, "%B %d %Y", tp); + in >> parse("%B %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -170,7 +170,7 @@ test_b() // correct full name std::istringstream in{"December 11 2016"}; sys_days tp; - parse(in, "%h %d %Y", tp); + in >> parse("%h %d %Y", tp); assert(!in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -180,7 +180,7 @@ test_b() // incorrect abbreviation std::istringstream in{"Dece 11 2016"}; sys_days tp; - parse(in, "%b %d %Y", tp); + in >> parse("%b %d %Y", tp); assert( in.fail()); assert(!in.bad()); assert(!in.eof()); @@ -197,7 +197,7 @@ test_c() // correct abbreviation std::istringstream in{"Sun Dec 11 14:02:43 2016"}; sys_seconds tp; - parse(in, "%c", tp); + in >> parse("%c", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{14} + minutes{2} + seconds{43}); @@ -213,7 +213,7 @@ test_x() // correct abbreviation std::istringstream in{"12/11/16"}; sys_seconds tp; - parse(in, "%x", tp); + in >> parse("%x", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11}); @@ -229,7 +229,7 @@ test_X() // correct abbreviation std::istringstream in{"2016-12-11 14:02:43"}; sys_seconds tp; - parse(in, "%F %X", tp); + in >> parse("%F %X", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{14} + minutes{2} + seconds{43}); @@ -244,7 +244,7 @@ test_C() { std::istringstream in{"20 16 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/11); @@ -252,7 +252,7 @@ test_C() { std::istringstream in{"-2 1 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == -101_y/12/11); @@ -260,7 +260,7 @@ test_C() { std::istringstream in{"-1 0 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == -100_y/12/11); @@ -268,7 +268,7 @@ test_C() { std::istringstream in{"-1 99 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == -99_y/12/11); @@ -276,7 +276,7 @@ test_C() { std::istringstream in{"-1 1 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == -1_y/12/11); @@ -284,7 +284,7 @@ test_C() { std::istringstream in{"0 0 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 0_y/12/11); @@ -292,7 +292,7 @@ test_C() { std::istringstream in{"0 1 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 1_y/12/11); @@ -300,7 +300,7 @@ test_C() { std::istringstream in{"0 99 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 99_y/12/11); @@ -308,7 +308,7 @@ test_C() { std::istringstream in{"1 0 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 100_y/12/11); @@ -316,7 +316,7 @@ test_C() { std::istringstream in{"1 1 12 11"}; sys_days tp; - parse(in, "%C %y %m %d", tp); + in >> parse("%C %y %m %d", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 101_y/12/11); @@ -331,7 +331,7 @@ test_d() { std::istringstream in{"2016 09 12"}; sys_days tp; - parse(in, "%Y %d %m", tp); + in >> parse("%Y %d %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/9); @@ -339,7 +339,7 @@ test_d() { std::istringstream in{"2016 09 12"}; sys_days tp; - parse(in, "%Y %e %m", tp); + in >> parse("%Y %e %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/9); @@ -347,7 +347,7 @@ test_d() { std::istringstream in{"2016 9 12"}; sys_days tp; - parse(in, "%Y %d %m", tp); + in >> parse("%Y %d %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/9); @@ -355,7 +355,7 @@ test_d() { std::istringstream in{"2016 9 12"}; sys_days tp; - parse(in, "%Y %e %m", tp); + in >> parse("%Y %e %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/9); @@ -370,7 +370,7 @@ test_D() { std::istringstream in{"12/11/16"}; sys_days tp; - parse(in, "%D", tp); + in >> parse("%D", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/11); @@ -385,7 +385,7 @@ test_F() { std::istringstream in{"2016-12-13"}; sys_days tp; - parse(in, "%F", tp); + in >> parse("%F", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/12/13); @@ -400,7 +400,7 @@ test_H() { std::istringstream in{"2016-12-11 15"}; sys_time tp; - parse(in, "%F %H", tp); + in >> parse("%F %H", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{15}); @@ -415,7 +415,7 @@ test_Ip() { std::istringstream in{"2016-12-11 1 pm"}; sys_time tp; - parse(in, "%F %I %p", tp); + in >> parse("%F %I %p", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{13}); @@ -423,7 +423,7 @@ test_Ip() { std::istringstream in{"2016-12-11 1 am"}; sys_time tp; - parse(in, "%F %I %p", tp); + in >> parse("%F %I %p", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{1}); @@ -438,7 +438,7 @@ test_j() { std::istringstream in{"2016 361"}; sys_days tp; - parse(in, "%Y %j", tp); + in >> parse("%Y %j", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -453,7 +453,7 @@ test_m() { std::istringstream in{"2016 12 09"}; sys_days tp; - parse(in, "%Y %d %m", tp); + in >> parse("%Y %d %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/9/12); @@ -461,7 +461,7 @@ test_m() { std::istringstream in{"2016 12 9"}; sys_days tp; - parse(in, "%Y %d %m", tp); + in >> parse("%Y %d %m", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == 2016_y/9/12); @@ -476,7 +476,7 @@ test_M() { std::istringstream in{"2016-12-11 15"}; sys_time tp; - parse(in, "%F %M", tp); + in >> parse("%F %M", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + minutes{15}); @@ -491,7 +491,7 @@ test_S() { std::istringstream in{"2016-12-11 15"}; sys_seconds tp; - parse(in, "%F %S", tp); + in >> parse("%F %S", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + seconds{15}); @@ -499,7 +499,7 @@ test_S() { std::istringstream in{"2016-12-11 15.001"}; sys_time tp; - parse(in, "%F %S", tp); + in >> parse("%F %S", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + seconds{15} + milliseconds{1}); @@ -514,7 +514,7 @@ test_T() { std::istringstream in{"2016-12-11 15:43:22"}; sys_seconds tp; - parse(in, "%F %T", tp); + in >> parse("%F %T", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{15} + minutes{43} + seconds{22}); @@ -522,7 +522,7 @@ test_T() { std::istringstream in{"2016-12-11 15:43:22.001"}; sys_time tp; - parse(in, "%F %T", tp); + in >> parse("%F %T", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{15} + minutes{43} + seconds{22} + @@ -531,11 +531,19 @@ test_T() { std::istringstream in{"2016-12-11 15:43:22"}; sys_time tp; - parse(in, "%F %T", tp); + in >> parse("%F %T", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{15} + minutes{43} + seconds{22}); } + { + std::istringstream in{"15:43:22.001"}; + milliseconds d; + in >> parse("%T", d); + assert(!in.fail()); + assert(!in.bad()); + assert(d == hours{15} + minutes{43} + seconds{22} + milliseconds{1}); + } } void @@ -546,7 +554,7 @@ test_p() { std::istringstream in{"2016-12-11 11pm"}; sys_time tp; - parse(in, "%F %I%p", tp); + in >> parse("%F %I%p", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/11} + hours{23}); @@ -561,7 +569,7 @@ test_r() { std::istringstream in{"2016-12-26 1:36:57 pm"}; sys_seconds tp; - parse(in, "%F %r", tp); + in >> parse("%F %r", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26} + hours{13} + minutes{36} + seconds{57}); @@ -576,7 +584,7 @@ test_R() { std::istringstream in{"2016-12-26 13:36"}; sys_seconds tp; - parse(in, "%F %R", tp); + in >> parse("%F %R", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26} + hours{13} + minutes{36}); @@ -591,7 +599,7 @@ test_U() { std::istringstream in{"2016-52-1"}; sys_days tp; - parse(in, "%Y-%U-%w", tp); + in >> parse("%Y-%U-%w", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -606,7 +614,7 @@ test_W() { std::istringstream in{"2016-52-1"}; sys_days tp; - parse(in, "%Y-%W-%w", tp); + in >> parse("%Y-%W-%w", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -621,7 +629,7 @@ test_GV() { std::istringstream in{"2016-52-1"}; sys_days tp; - parse(in, "%G-%V-%w", tp); + in >> parse("%G-%V-%w", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -629,7 +637,7 @@ test_GV() { std::istringstream in{"2016-52-1"}; sys_days tp; - parse(in, "%G-%V-%w", tp); + in >> parse("%G-%V-%w", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -637,7 +645,7 @@ test_GV() { std::istringstream in{"20 16-52-1"}; sys_days tp; - parse(in, "%C %g-%V-%w", tp); + in >> parse("%C %g-%V-%w", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -645,7 +653,7 @@ test_GV() { std::istringstream in{"20 16-52-1"}; sys_days tp; - parse(in, "%C %g-%V-%u", tp); + in >> parse("%C %g-%V-%u", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26}); @@ -660,7 +668,7 @@ test_z() { std::istringstream in{"2016-12-26 15:53:22 -0500"}; sys_seconds tp; - parse(in, "%F %T %z", tp); + in >> parse("%F %T %z", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26} + hours{20} + minutes{53} + seconds{22}); @@ -668,7 +676,7 @@ test_z() { std::istringstream in{"2016-12-26 15:53:22 -0500"}; local_seconds tp; - parse(in, "%F %T %z", tp); + in >> parse("%F %T %z", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == local_days{2016_y/12/26} + hours{15} + minutes{53} + seconds{22}); @@ -676,7 +684,7 @@ test_z() { std::istringstream in{"2016-12-26 15:53:22 -05:00"}; sys_seconds tp; - parse(in, "%F %T %Ez", tp); + in >> parse("%F %T %Ez", tp); assert(!in.fail()); assert(!in.bad()); assert(tp == sys_days{2016_y/12/26} + hours{20} + minutes{53} + seconds{22}); @@ -692,7 +700,7 @@ test_Z() std::string a; std::istringstream in{"2016-12-26 15:53:22 word"}; local_seconds tp; - parse(in, "%F %T %Z", tp, a); + in >> parse("%F %T %Z", tp, a); assert(!in.fail()); assert(!in.bad()); assert(tp == local_days{2016_y/12/26} + hours{15} + minutes{53} + seconds{22}); diff --git a/test/date_test/time_of_day_hours.pass.cpp b/test/date_test/time_of_day_hours.pass.cpp index 7c1f8e1..aed0e04 100644 --- a/test/date_test/time_of_day_hours.pass.cpp +++ b/test/date_test/time_of_day_hours.pass.cpp @@ -60,7 +60,7 @@ main() static_assert(is_same{}, ""); static_assert( is_trivially_destructible{}, ""); - static_assert(!is_default_constructible{}, ""); + static_assert( is_default_constructible{}, ""); static_assert( is_trivially_copy_constructible{}, ""); static_assert( is_trivially_copy_assignable{}, ""); static_assert( is_trivially_move_constructible{}, ""); diff --git a/test/date_test/time_of_day_microfortnights.pass.cpp b/test/date_test/time_of_day_microfortnights.pass.cpp new file mode 100644 index 0000000..ce48da4 --- /dev/null +++ b/test/date_test/time_of_day_microfortnights.pass.cpp @@ -0,0 +1,119 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// enum {am = 1, pm}; + +// template +// class time_of_day> +// { +// public: +// using precision = std::chrono::std::chrono::duration; +// +// constexpr explicit time_of_day(precision since_midnight) noexcept; +// constexpr time_of_day(std::chrono::hours h, std::chrono::minutes m, +// std::chrono::seconds s, precision sub_s, +// unsigned md) noexcept; +// +// constexpr std::chrono::hours hours() const noexcept; +// constexpr std::chrono::minutes minutes() const noexcept; +// constexpr std::chrono::seconds seconds() const noexcept; +// constexpr precision subseconds() const noexcept; +// constexpr unsigned mode() const noexcept; +// +// constexpr explicit operator precision() const noexcept; +// constexpr precision to_duration() const noexcept; +// +// void make24() noexcept; +// void make12() noexcept; +// }; + +// template +// std::ostream& +// operator<<(std::ostream& os, const time_of_day>& t); + +#include "date.h" + +#include +#include +#include + +using fortnights = std::chrono::duration, + date::weeks::period>>; + +using microfortnights = std::chrono::duration>; + +int +main() +{ + using namespace date; + using namespace std; + using namespace std::chrono; + + using tod = time_of_day::type>; + + static_assert(is_same>{}, ""); + + static_assert( is_trivially_destructible{}, ""); + static_assert( is_default_constructible{}, ""); + static_assert( is_trivially_copy_constructible{}, ""); + static_assert( is_trivially_copy_assignable{}, ""); + static_assert( is_trivially_move_constructible{}, ""); + static_assert( is_trivially_move_assignable{}, ""); + + static_assert(is_constructible{}, ""); + static_assert(!is_convertible{}, ""); + + static_assert(is_nothrow_constructible{}, ""); + static_assert(!is_convertible{}, ""); + + constexpr tod t1 = tod{hours{13} + minutes{7} + microfortnights{5}}; + static_assert(t1.hours() == hours{13}, ""); + static_assert(t1.minutes() == minutes{7}, ""); + static_assert(t1.seconds() == seconds{6}, ""); + static_assert(t1.subseconds() == tod::precision{480}, ""); +#if __cplusplus >= 201402 + static_assert(static_cast(t1) == hours{13} + minutes{7} + + microfortnights{5}, ""); + static_assert(t1.to_duration() == hours{13} + minutes{7} + microfortnights{5}, ""); +#endif + + auto t2 = t1; + assert(t2.hours() == t1.hours()); + assert(t2.minutes() == t1.minutes()); + assert(t2.seconds() == t1.seconds()); + assert(t2.subseconds() == t1.subseconds()); + assert(t2.to_duration() == t1.to_duration()); + ostringstream os; + os << t2; + assert(os.str() == "13:07:06.0480"); + t2.make12(); + os.str(""); + os << t2; + assert(os.str() == "1:07:06.0480pm"); + t2.make24(); + os.str(""); + os << t2; + assert(os.str() == "13:07:06.0480"); +} diff --git a/test/date_test/time_of_day_milliseconds.pass.cpp b/test/date_test/time_of_day_milliseconds.pass.cpp index 1e024b8..fb32929 100644 --- a/test/date_test/time_of_day_milliseconds.pass.cpp +++ b/test/date_test/time_of_day_milliseconds.pass.cpp @@ -68,7 +68,7 @@ main() static_assert(is_same{}, ""); static_assert( is_trivially_destructible{}, ""); - static_assert(!is_default_constructible{}, ""); + static_assert( is_default_constructible{}, ""); static_assert( is_trivially_copy_constructible{}, ""); static_assert( is_trivially_copy_assignable{}, ""); static_assert( is_trivially_move_constructible{}, ""); diff --git a/test/date_test/time_of_day_minutes.pass.cpp b/test/date_test/time_of_day_minutes.pass.cpp index 846eb37..5b2c63e 100644 --- a/test/date_test/time_of_day_minutes.pass.cpp +++ b/test/date_test/time_of_day_minutes.pass.cpp @@ -62,7 +62,7 @@ main() static_assert(is_same{}, ""); static_assert( is_trivially_destructible{}, ""); - static_assert(!is_default_constructible{}, ""); + static_assert( is_default_constructible{}, ""); static_assert( is_trivially_copy_constructible{}, ""); static_assert( is_trivially_copy_assignable{}, ""); static_assert( is_trivially_move_constructible{}, ""); diff --git a/test/date_test/time_of_day_nanoseconds.pass.cpp b/test/date_test/time_of_day_nanoseconds.pass.cpp index bb6a715..3c6da21 100644 --- a/test/date_test/time_of_day_nanoseconds.pass.cpp +++ b/test/date_test/time_of_day_nanoseconds.pass.cpp @@ -68,7 +68,7 @@ main() static_assert(is_same{}, ""); static_assert( is_trivially_destructible{}, ""); - static_assert(!is_default_constructible{}, ""); + static_assert( is_default_constructible{}, ""); static_assert( is_trivially_copy_constructible{}, ""); static_assert( is_trivially_copy_assignable{}, ""); static_assert( is_trivially_move_constructible{}, ""); diff --git a/test/date_test/time_of_day_seconds.pass.cpp b/test/date_test/time_of_day_seconds.pass.cpp index b766143..a6d7814 100644 --- a/test/date_test/time_of_day_seconds.pass.cpp +++ b/test/date_test/time_of_day_seconds.pass.cpp @@ -63,7 +63,7 @@ main() static_assert(is_same{}, ""); static_assert( is_trivially_destructible{}, ""); - static_assert(!is_default_constructible{}, ""); + static_assert( is_default_constructible{}, ""); static_assert( is_trivially_copy_constructible{}, ""); static_assert( is_trivially_copy_assignable{}, ""); static_assert( is_trivially_move_constructible{}, ""); diff --git a/tz.h b/tz.h index 84c0cb8..c4ac1c7 100644 --- a/tz.h +++ b/tz.h @@ -1108,13 +1108,20 @@ utc_clock::now() } template -std::basic_ostream& -operator<<(std::basic_ostream& os, const utc_time& t) +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const utc_time& t) { + using namespace std; using namespace std::chrono; - using duration = typename std::common_type::type; + using CT = typename common_type::type; + const string abbrev("UTC"); + CONSTDATA seconds offset{0}; auto const& leaps = get_tzdb().leaps; - auto tp = sys_time{t.time_since_epoch()}; + auto tp = sys_time{t.time_since_epoch()}; + year_month_day ymd; + time_of_day time; + seconds ls{0}; if (tp >= leaps.front()) { auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); @@ -1122,17 +1129,205 @@ operator<<(std::basic_ostream& os, const utc_time& t) if (tp < lt[-1]) { if (tp >= lt[-1].date() - seconds{1}) - { - auto const dp = floor(tp); - auto time = make_time(tp-dp); - time.seconds() += seconds{1}; - return os << year_month_day(dp) << ' ' << time; - } + ls = seconds{1}; else tp += seconds{1}; } } - return os << tp; + auto const sd = floor(tp); + ymd = sd; + time = make_time(tp - sd); + time.seconds() += ls; + detail::fields fds{ymd, time}; + detail::to_stream(os, fmt, fds, &abbrev, &offset); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const utc_time& t) +{ + to_stream(os, "%F %T", t); + return os; +} + +template +std::basic_string +format(const std::locale& loc, const CharT* fmt, const utc_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template +std::basic_string +format(const CharT* fmt, const utc_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt, tp); + return os.str(); +} + +template +std::basic_string +format(const std::locale& loc, const std::basic_string& fmt, + const utc_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +std::basic_string +format(const std::basic_string& fmt, const utc_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +namespace detail +{ + +template > +struct parse_utc_manip +{ + const std::basic_string format_; + utc_time& tp_; + std::basic_string* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_utc_manip(std::basic_string format, + utc_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template +std::basic_istream& +operator>>(std::basic_istream& is, + const parse_utc_manip& x) +{ + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + minutes offset{}; + auto offptr = x.offset_ ? x.offset_ : &offset; + fields fds{}; + parse(is, x.format_.c_str(), fds, x.abbrev_, offptr); + if (!fds.ymd.ok()) + is.setstate(ios::failbit); + if (!is.fail()) + { + bool is_leap_second = fds.tod.seconds() == seconds{60}; + if (is_leap_second) + fds.tod.seconds() -= seconds{1}; + x.tp_ = to_utc_time(sys_days(fds.ymd) + + duration_cast(fds.tod.to_duration() - *offptr)); + if (is_leap_second) + x.tp_ += seconds{1}; + } + return is; +} + +} // namespace detail + +template +inline +detail::parse_utc_manip +parse(const std::basic_string& format, utc_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_utc_manip +parse(const std::basic_string& format, utc_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_utc_manip +parse(const std::basic_string& format, utc_time& tp, + std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_utc_manip +parse(const std::basic_string& format, utc_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_utc_manip +parse(const std::basic_string& format, utc_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; +} + +// const CharT* formats + +template +inline +detail::parse_utc_manip +parse(const CharT* format, utc_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_utc_manip +parse(const CharT* format, utc_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_utc_manip +parse(const CharT* format, utc_time& tp, std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_utc_manip +parse(const CharT* format, utc_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_utc_manip +parse(const CharT* format, utc_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; } // tai_clock