I'm looking for a way to print out milliseconds in this format using C++:
cout << hours << " Hours : " << minutes << " Minutes : " << seconds << " Seconds : " << milliseconds << " Milliseconds" << endl;
I know there are a ton of duplicate questions about this. But none of them really handle how to get the remainder in milliseconds. There are a few that do this using Java, but I want a solution in C++.
Edit:
I wanted to clarify the question. I'm looking to take a time value that I get for the time it takes a program to run and print out that time in a legible format for the user. Getting the standard hr:min:sec was straight forward. But including any remaining milliseconds was tripping me up.
std::string format_duration( std::chrono::milliseconds ms ) {
using namespace std::chrono;
auto secs = duration_cast<seconds>(ms);
ms -= duration_cast<milliseconds>(secs);
auto mins = duration_cast<minutes>(secs);
secs -= duration_cast<seconds>(mins);
auto hour = duration_cast<hours>(mins);
mins -= duration_cast<minutes>(hour);
std::stringstream ss;
ss << hour.count() << " Hours : " << mins.count() << " Minutes : " << secs.count() << " Seconds : " << ms.count() << " Milliseconds";
return ss.str();
}
live example.
Extending this to days/years/etc should be easy (there isn't a predefined std::chrono duration type for days/years/etc prior to c++20 however).
But I can do better.
template<class Duration>
struct split_duration {
Duration d;
std::chrono::milliseconds leftover;
split_duration( std::chrono::milliseconds ms ):
d( std::chrono::duration_cast<Duration>(ms) ),
leftover( ms - std::chrono::duration_cast<std::chrono::milliseconds>(d) )
{}
};
template<class...Durations>
std::tuple<Durations...> durations( std::chrono::milliseconds ms ) {
std::tuple<std::optional<split_duration<Durations>>...> tmp;
( (void)(
(void)std::get<std::optional<split_duration<Durations>>>(tmp).emplace( ms ),
ms = std::get<std::optional<split_duration<Durations>>>(tmp)->leftover
), ...
);
return std::make_tuple( std::get<std::optional<split_duration<Durations>>>( tmp )->d... );
}
template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag = {};
inline std::string duration_name( tag_t<std::chrono::milliseconds> ) { return "ms"; }
inline std::string duration_name( tag_t<std::chrono::seconds> ) { return "Seconds"; }
inline std::string duration_name( tag_t<std::chrono::minutes> ) { return "Minutes"; }
inline std::string duration_name( tag_t<std::chrono::hours> ) { return "Hours"; }
// inline std::string duration_name( tag_t<std::chrono::days> ) { return "Days"; }
// inline std::string duration_name( tag_t<std::chrono::years> ) { return "Years"; }
template<class...Durations>
std::string format_duration( std::chrono::milliseconds ms ) {
auto split = durations<Durations...>(ms);
std::stringstream ss;
(
(void)( ss << duration_name(tag<Durations>) << ": " << std::get<Durations>(split).count() << " " ), ...
);
return ss.str();
}
Days/Years requires c++20, everything else is c++17.
You just call format_durations<Durations...>( some_ms )
and out comes a formatted string based off the Durations...
. You do have to do it from most-to-least significant.
durations<Durations...>
gives you a tuple breakdown of the time that has to be most-to-least; you could then reorder that before formatting if you chose.
Duplicate duration types leads to compile time errors, as std::get
dies a horrible ambiguous death.
Live example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With