From b5d025ea2f03cebad25a9e293ebe3781b08dc113 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Tue, 19 Mar 2019 19:50:30 -0400 Subject: [PATCH] Align time_of_day with hh_mm_ss Per committee review --- include/date/date.h | 207 ++++++++---------- include/date/tz.h | 4 +- test/date_test/time_of_day_hours.pass.cpp | 14 +- .../time_of_day_microfortnights.pass.cpp | 8 - .../time_of_day_milliseconds.pass.cpp | 18 -- test/date_test/time_of_day_minutes.pass.cpp | 14 -- .../time_of_day_nanoseconds.pass.cpp | 18 -- test/date_test/time_of_day_seconds.pass.cpp | 16 -- 8 files changed, 99 insertions(+), 200 deletions(-) diff --git a/include/date/date.h b/include/date/date.h index 92cc773..318dfca 100644 --- a/include/date/date.h +++ b/include/date/date.h @@ -3650,7 +3650,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, fields& fds, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr); -// time_of_day +// hh_mm_ss namespace detail { @@ -3808,29 +3808,29 @@ abs(std::chrono::duration d) } // namespace detail template -class time_of_day +class hh_mm_ss { using dfs = detail::decimal_format_seconds::type>; - enum state {is_24, am, pm}; - std::chrono::hours h_; std::chrono::minutes m_; dfs s_; bool neg_; - state mode_ = is_24; public: + static unsigned CONSTDATA fractional_width = dfs::width; using precision = typename dfs::precision; - CONSTCD11 time_of_day() = default; + CONSTCD11 hh_mm_ss() NOEXCEPT + : hh_mm_ss(Duration::zero()) + {} - CONSTCD11 explicit time_of_day(Duration d) NOEXCEPT + CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT : h_(std::chrono::duration_cast(detail::abs(d))) , m_(std::chrono::duration_cast(detail::abs(d)) - h_) , s_(detail::abs(d) - h_ - m_) - , neg_(d < precision{}) + , neg_(d < Duration::zero()) {} CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} @@ -3839,17 +3839,11 @@ public: CONSTCD14 std::chrono::seconds& seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} + CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} CONSTCD11 precision to_duration() const NOEXCEPT - {return (make_24(mode_, h_) + m_ + s_.to_duration()) * (1-2*neg_);} - - CONSTCD14 void make24() NOEXCEPT - { - h_ = make_24(mode_, h_); - mode_ = is_24; - } - CONSTCD14 void make12() NOEXCEPT {make_12(mode_, h_);} + {return (h_ + m_ + s_.to_duration()) * (1-2*neg_);} CONSTCD11 bool in_conventional_range() const NOEXCEPT { @@ -3860,89 +3854,21 @@ public: private: - CONSTCD14 - static - std::chrono::hours - make_24(state mode, std::chrono::hours h) NOEXCEPT - { - using namespace std; - if (mode != is_24) - { - if (mode == am) - { - if (h == chrono::hours{12}) - h = chrono::hours{0}; - } - else - { - if (h != chrono::hours{12}) - h += chrono::hours{12}; - } - } - return h; - } - - CONSTCD14 - static - void - make_12(state& mode, std::chrono::hours& h) NOEXCEPT - { - using namespace std; - if (mode == is_24) - { - if (h < chrono::hours{12}) - { - if (h == chrono::hours{0}) - h = chrono::hours{12}; - mode = am; - } - else - { - if (h != chrono::hours{12}) - h -= chrono::hours{12}; - mode = pm; - } - } - } - template friend std::basic_ostream& - operator<<(std::basic_ostream& os, time_of_day const& tod) + operator<<(std::basic_ostream& os, hh_mm_ss const& tod) { using namespace detail; using namespace std; - if (tod.neg_) + if (tod.is_negative()) os << '-'; - if (tod.mode_ == is_24) - { - if (tod.h_ < chrono::hours{10}) - os << '0'; - os << tod.h_.count() << ':'; - if (tod.m_ < chrono::minutes{10}) - os << '0'; - os << tod.m_.count() << ':' << tod.s_; - } - else - { - os << tod.h_.count() << ':'; - if (tod.m_ < chrono::minutes{10}) - os << '0'; - os << tod.m_.count() << ':' << tod.s_; -#if !ONLY_C_LOCALE - std::tm tm{}; - tm.tm_hour = tod.make_24(tod.mode_, tod.h_).count(); - auto& facet = use_facet>(os.getloc()); - const charT f[] = {'%', 'p'}; - facet.put(os, os, os.fill(), &tm, f, f+2); -#else - if (tod.mode_ == am) - os << 'A'; - else - os << 'P'; - os << 'M'; -#endif - } + if (tod.h_ < chrono::hours{10}) + os << '0'; + os << tod.h_.count() << ':'; + if (tod.m_ < chrono::minutes{10}) + os << '0'; + os << tod.m_.count() << ':' << tod.s_; return os; } @@ -3961,15 +3887,71 @@ private: std::basic_string* abbrev, std::chrono::minutes* offset); }; +inline +CONSTCD14 +bool +is_am(std::chrono::hours const& h) NOEXCEPT +{ + using namespace std::chrono; + return hours{0} <= h && h < hours{12}; +} + +inline +CONSTCD14 +bool +is_pm(std::chrono::hours const& h) NOEXCEPT +{ + using namespace std::chrono; + return hours{12} <= h && h < hours{24}; +} + +inline +CONSTCD14 +std::chrono::hours +make12(std::chrono::hours h) NOEXCEPT +{ + using namespace std::chrono; + if (h < hours{12}) + { + if (h == hours{0}) + h = hours{12}; + } + else + { + if (h != hours{12}) + h -= hours{12}; + } + return h; +} + +inline +CONSTCD14 +std::chrono::hours +make24(std::chrono::hours h, bool is_pm) NOEXCEPT +{ + using namespace std::chrono; + if (is_pm) + { + if (h != hours{12}) + h += hours{12}; + } + else if (h == hours{12}) + h = hours{0}; + return h; +} + +template +using time_of_day = hh_mm_ss; + template ::value>::type> CONSTCD11 inline -time_of_day> +hh_mm_ss> make_time(const std::chrono::duration& d) { - return time_of_day>(d); + return hh_mm_ss>(d); } template @@ -4555,22 +4537,22 @@ struct fields { year_month_day ymd{nanyear/0/0}; weekday wd{8u}; - time_of_day tod{}; + hh_mm_ss tod{}; bool has_tod = false; fields() = default; fields(year_month_day ymd_) : ymd(ymd_) {} fields(weekday wd_) : wd(wd_) {} - fields(time_of_day tod_) : tod(tod_), has_tod(true) {} + fields(hh_mm_ss tod_) : tod(tod_), has_tod(true) {} fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} - fields(year_month_day ymd_, time_of_day tod_) : ymd(ymd_), tod(tod_), - has_tod(true) {} + fields(year_month_day ymd_, hh_mm_ss tod_) : ymd(ymd_), tod(tod_), + has_tod(true) {} - fields(weekday wd_, time_of_day tod_) : wd(wd_), tod(tod_), has_tod(true) {} + fields(weekday wd_, hh_mm_ss tod_) : wd(wd_), tod(tod_), has_tod(true) {} - fields(year_month_day ymd_, weekday wd_, time_of_day tod_) + fields(year_month_day ymd_, weekday wd_, hh_mm_ss tod_) : ymd(ymd_) , wd(wd_) , tod(tod_) @@ -5146,11 +5128,10 @@ to_stream(std::basic_ostream& os, const CharT* fmt, if (modified == CharT{}) #endif { - if (*fmt == CharT{'I'}) - hms.make12(); - if (hms.hours() < hours{10}) + auto h = *fmt == CharT{'I'} ? make12(hms.hours()) : hms.hours(); + if (h < hours{10}) os << CharT{'0'}; - os << hms.hours().count(); + os << h.count(); } #if !ONLY_C_LOCALE else if (modified == CharT{'O'}) @@ -5303,7 +5284,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, tm.tm_hour = static_cast(fds.tod.hours().count()); facet.put(os, os, os.fill(), &tm, begin(f), end(f)); #else - if (fds.tod.hours() < hours{12}) + if (is_am(fds.tod.hours())) os << ampm_names().first[0]; else os << ampm_names().first[1]; @@ -5357,18 +5338,16 @@ to_stream(std::basic_ostream& os, const CharT* fmt, tm.tm_sec = static_cast(fds.tod.seconds().count()); facet.put(os, os, os.fill(), &tm, begin(f), end(f)); #else - time_of_day tod(duration_cast(fds.tod.to_duration())); - tod.make12(); + hh_mm_ss tod(duration_cast(fds.tod.to_duration())); save_ostream _(os); os.fill('0'); os.width(2); - os << tod.hours().count() << CharT{':'}; + os << make12(tod.hours()).count() << CharT{':'}; os.width(2); os << tod.minutes().count() << CharT{':'}; os.width(2); os << tod.seconds().count() << CharT{' '}; - tod.make24(); - if (tod.hours() < hours{12}) + if (is_am(tod.hours())) os << ampm_names().first[0]; else os << ampm_names().first[1]; @@ -5959,7 +5938,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { using Duration = std::chrono::duration; using CT = typename std::common_type::type; - fields fds{time_of_day{d}}; + fields fds{hh_mm_ss{d}}; return to_stream(os, fmt, fds); } @@ -5971,7 +5950,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, { using CT = typename std::common_type::type; auto ld = floor(tp); - fields fds{year_month_day{ld}, time_of_day{tp-local_seconds{ld}}}; + fields fds{year_month_day{ld}, hh_mm_ss{tp-local_seconds{ld}}}; return to_stream(os, fmt, fds, abbrev, offset_sec); } @@ -5985,7 +5964,7 @@ to_stream(std::basic_ostream& os, const CharT* fmt, const std::string abbrev("UTC"); CONSTDATA seconds offset{0}; auto sd = floor(tp); - fields fds{year_month_day{sd}, time_of_day{tp-sys_seconds{sd}}}; + fields fds{year_month_day{sd}, hh_mm_ss{tp-sys_seconds{sd}}}; return to_stream(os, fmt, fds, &abbrev, &offset); } @@ -7590,7 +7569,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, if (H != not_a_hour) { fds.has_tod = true; - fds.tod = time_of_day{hours{H}}; + fds.tod = hh_mm_ss{hours{H}}; } if (M != not_a_minute) { diff --git a/include/date/tz.h b/include/date/tz.h index b2dd89a..0d8699e 100644 --- a/include/date/tz.h +++ b/include/date/tz.h @@ -1922,14 +1922,14 @@ is_leap_second(date::utc_time const& ut) return {ls, ds}; } -struct get_leap_second_info_t +struct leap_second_info { bool is_leap_second; std::chrono::seconds elapsed; }; template -get_leap_second_info_t +leap_second_info get_leap_second_info(date::utc_time const& ut) { auto p = is_leap_second(ut); diff --git a/test/date_test/time_of_day_hours.pass.cpp b/test/date_test/time_of_day_hours.pass.cpp index 0768468..5b12429 100644 --- a/test/date_test/time_of_day_hours.pass.cpp +++ b/test/date_test/time_of_day_hours.pass.cpp @@ -85,16 +85,10 @@ main() ostringstream os; os << t2; assert(os.str() == "13:00:00"); - t2.make12(); + auto h = make12(t2.hours()); os.str(""); - assert(t2.hours() == hours{1}); + assert(h == hours{1}); assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "1:00:00PM"); - t2.make24(); - os.str(""); - assert(t2.hours() == hours{13}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "13:00:00"); + assert(!is_am(t2.hours())); + assert(is_pm(t2.hours())); } diff --git a/test/date_test/time_of_day_microfortnights.pass.cpp b/test/date_test/time_of_day_microfortnights.pass.cpp index 6267f0c..2a16e44 100644 --- a/test/date_test/time_of_day_microfortnights.pass.cpp +++ b/test/date_test/time_of_day_microfortnights.pass.cpp @@ -108,12 +108,4 @@ main() 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 7d7ba69..c13fe4c 100644 --- a/test/date_test/time_of_day_milliseconds.pass.cpp +++ b/test/date_test/time_of_day_milliseconds.pass.cpp @@ -101,22 +101,4 @@ main() ostringstream os; os << t2; assert(os.str() == "13:07:05.022"); - t2.make12(); - os.str(""); - assert(t2.hours() == hours{1}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.subseconds() == milliseconds{22}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "1:07:05.022PM"); - t2.make24(); - os.str(""); - assert(t2.hours() == hours{13}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.subseconds() == milliseconds{22}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "13:07:05.022"); } diff --git a/test/date_test/time_of_day_minutes.pass.cpp b/test/date_test/time_of_day_minutes.pass.cpp index 2102a47..47cdafc 100644 --- a/test/date_test/time_of_day_minutes.pass.cpp +++ b/test/date_test/time_of_day_minutes.pass.cpp @@ -89,18 +89,4 @@ main() ostringstream os; os << t2; assert(os.str() == "13:07:00"); - t2.make12(); - os.str(""); - assert(t2.hours() == hours{1}); - assert(t2.minutes() == minutes{7}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "1:07:00PM"); - t2.make24(); - os.str(""); - assert(t2.hours() == hours{13}); - assert(t2.minutes() == minutes{7}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "13:07:00"); } diff --git a/test/date_test/time_of_day_nanoseconds.pass.cpp b/test/date_test/time_of_day_nanoseconds.pass.cpp index 73748bf..71a7fe8 100644 --- a/test/date_test/time_of_day_nanoseconds.pass.cpp +++ b/test/date_test/time_of_day_nanoseconds.pass.cpp @@ -101,22 +101,4 @@ main() ostringstream os; os << t2; assert(os.str() == "13:07:05.000000022"); - t2.make12(); - os.str(""); - assert(t2.hours() == hours{1}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.subseconds() == nanoseconds{22}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "1:07:05.000000022PM"); - t2.make24(); - os.str(""); - assert(t2.hours() == hours{13}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.subseconds() == nanoseconds{22}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "13:07:05.000000022"); } diff --git a/test/date_test/time_of_day_seconds.pass.cpp b/test/date_test/time_of_day_seconds.pass.cpp index a6e84b9..eaf4fb9 100644 --- a/test/date_test/time_of_day_seconds.pass.cpp +++ b/test/date_test/time_of_day_seconds.pass.cpp @@ -93,20 +93,4 @@ main() ostringstream os; os << t2; assert(os.str() == "13:07:05"); - t2.make12(); - os.str(""); - assert(t2.hours() == hours{1}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "1:07:05PM"); - t2.make24(); - os.str(""); - assert(t2.hours() == hours{13}); - assert(t2.minutes() == minutes{7}); - assert(t2.seconds() == seconds{5}); - assert(t2.to_duration() == t1.to_duration()); - os << t2; - assert(os.str() == "13:07:05"); }