4

I am using the following code to get the current time in C++.

std::time_t t = std::time(nullptr);
std::time(&t);
std::cout << std::put_time(std::localtime(&t), "%X,");

However, this gives me time in HH::MM::SS. But for my application I also want to include time in milliseconds. Is there anyway to get something like HH::MM::SS::msecs using std::put_time?

Or what are the alternatives to get the system time with milliseconds precision inside a C++ program?

pankycodes
  • 61
  • 2
  • 6
  • Does this answer your question? [How to get current timestamp in milliseconds since 1970 just the way Java gets](https://stackoverflow.com/questions/19555121/how-to-get-current-timestamp-in-milliseconds-since-1970-just-the-way-java-gets) – Rietty Nov 14 '19 at 20:24
  • 2
    See this answer: https://stackoverflow.com/a/32874098/11547576 ... You have to calculate the milliseconds by a rather involved subtraction of total seconds from total milliseconds. – neutrino_logic Nov 14 '19 at 21:12

2 Answers2

6

Here's one example using some C++11 <chrono> features. If you can use C++20, check out the new <chrono> features for more goodies or take a look at Howard Hinnants Date library.

#include <chrono>
#include <cstdint>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>

// A C++11 constexpr function template for counting decimals needed for
// selected precision.
template<std::size_t V, std::size_t C = 0,
         typename std::enable_if<(V < 10), int>::type = 0>
constexpr std::size_t log10ish() {
    return C;
}

template<std::size_t V, std::size_t C = 0,
         typename std::enable_if<(V >= 10), int>::type = 0>
constexpr std::size_t log10ish() {
    return log10ish<V / 10, C + 1>();
}

// A class to support using different precisions, chrono clocks and formats
template<class Precision = std::chrono::seconds,
         class Clock = std::chrono::system_clock>
class log_watch {
public:
    // some convenience typedefs and "decimal_width" for sub second precisions
    using precision_type = Precision;
    using ratio_type = typename precision_type::period;
    using clock_type = Clock;
    static constexpr auto decimal_width = log10ish<ratio_type{}.den>();

    static_assert(ratio_type{}.num <= ratio_type{}.den,
                  "Only second or sub second precision supported");
    static_assert(ratio_type{}.num == 1, "Unsupported precision parameter");

    // default format: "%Y-%m-%dT%H:%M:%S"
    log_watch(const std::string& format = "%FT%T") : m_format(format) {}

    template<class P, class C>
    friend std::ostream& operator<<(std::ostream&, const log_watch<P, C>&);

private:
    std::string m_format;
};

template<class Precision, class Clock>
std::ostream& operator<<(std::ostream& os, const log_watch<Precision, Clock>& lw) {
    // get current system clock
    auto time_point = Clock::now();

    // extract std::time_t from time_point
    std::time_t t = Clock::to_time_t(time_point);

    // output the part supported by std::tm
    os << std::put_time(std::localtime(&t), lw.m_format.c_str());

    // only involve chrono duration calc for displaying sub second precisions
    if(lw.decimal_width) { // if constexpr( ... in C++17
        // get duration since epoch
        auto dur = time_point.time_since_epoch();

        // extract the sub second part from the duration since epoch
        auto ss =
            std::chrono::duration_cast<Precision>(dur) % std::chrono::seconds{1};

        // output the sub second part
        os << std::setfill('0') << std::setw(lw.decimal_width) << ss.count();
    }

    return os;
}

int main() {
    // default precision, clock and format
    log_watch<> def_cp; // <= C++14
    // log_watch def;   // >= C++17

    // alt. precision using alternative formats
    log_watch<std::chrono::milliseconds> milli("%X,");
    log_watch<std::chrono::microseconds> micro("%FT%T.");
    // alt. precision and clock - only supported if the clock is an alias for
    // system_clock
    log_watch<std::chrono::nanoseconds,
              std::chrono::high_resolution_clock> nano("%FT%T.");

    std::cout << "def_cp: " << def_cp << "\n";
    std::cout << "milli : " << milli << "\n";
    std::cout << "micro : " << micro << "\n";
    std::cout << "nano  : " << nano << "\n";
}

Example output:

def_cp: 2019-11-21T13:44:07
milli : 13:44:07,871
micro : 2019-11-21T13:44:07.871939
nano  : 2019-11-21T13:44:07.871986585
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    Hi @tedlyngmo. You are right. I changed it back to empty template so that it uses the default clock format. But the variable needs an empty template argument for it to compile. – pankycodes Nov 15 '19 at 13:42
  • 1
    @pankycodes Ah, of course, you are correct. I've made a change highlighting that! – Ted Lyngmo Nov 15 '19 at 13:47
  • Thanks @tedlyngmo for the solution with a nice class wrapper around it. But may I ask you why specifically you chose a friend function to overload your '<<' operator? It is a general question and was just curious about your choice :). – pankycodes Nov 15 '19 at 17:33
  • @pankycodes You are welcome! It was just to let it get direct access to `m_format`. It could have been done via a normal getter method too or by redirecting the `operator<<` to a member function with access. – Ted Lyngmo Nov 15 '19 at 17:39
  • 1
    Great answer, but I would advise to not escape the type-system until the last minute. When you instantiate `auto ms` it would be pertinent to not call `.count()` and let `ms` become a `std::chrono::milliseconds`. Inside the `duration_cast` would be a great time to get the remainder by doing `dur % std::chrono::seconds{1}`. This saves on the `ms % 1000` which, while correct, is easy to get wrong, or miss when the types change. Save the `.count()` for where it is absolutely unavoidable, in this case, when streaming out to `os`. – David Brown Nov 21 '19 at 08:36
  • @BigDaveDev Thanks for the suggestions. I've had some afterthought after I wrote this answer and think I'll rework it a little when time allows. I'll take your suggestions in consideration when I do. – Ted Lyngmo Nov 21 '19 at 09:25
  • 1
    @BigDaveDev I took your advice and it became much nicer indeed. – Ted Lyngmo Nov 21 '19 at 12:46
2

The accepted answer is a good one, but I would like to demonstrate doing this with an open-source, third-party, date handling library:

auto now = std::chrono::system_clock::now();
std::cout << date::format("%T", std::chrono::floor<std::chrono::milliseconds>(now));

This just output, for me: 10:01:46.654.

The fractional seconds separator is locale specific. In Sweden, where I reside, they use the comma as a separator. date::format allows supplying a std::locale, so we can force the use of the Swedish locale, for example, on my machine:

auto now = std::chrono::system_clock::now();
std::cout << date::format(std::locale("sv-SE"), "%T", std::chrono::floor<std::chrono::milliseconds>(now));

Now the ouput is: 10:02:32,169.

This formatting has been accepted in C++20, so you will get this when the vendors have implemented it :)

Of course, if the "%X" formatting is really want you want, then you cannot have decimal seconds and need to append them yourself:

auto now = std::chrono::system_clock::now();
auto ms  = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch() % std::chrono::seconds{1});
std::cout << date::format("%X", std::chrono::floor<std::chrono::milliseconds>(now)) << "," << ms.count();

Note that I am using ms.count() because in C++20 streaming a duration will append the units as well, meaning that << ms would output something like 123ms.

David Brown
  • 522
  • 2
  • 12