Search code examples
c++c++-chrono

Getting current time with millisecond precision using put_time in C++


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?


Solution

  • 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