How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

后端 未结 7 2062
生来不讨喜
生来不讨喜 2020-11-27 02:50

How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

For example:

\"10-10-2012 12:38:40.123456\"         


        
7条回答
  •  一整个雨季
    2020-11-27 03:18

    Self-explanatory code follows which first creates a std::tm corresponding to 10-10-2012 12:38:40, converts that to a std::chrono::system_clock::time_point, adds 0.123456 seconds, and then prints that out by converting back to a std::tm. How to handle the fractional seconds is in the very last step.

    #include 
    #include 
    #include 
    
    int main()
    {
        // Create 10-10-2012 12:38:40 UTC as a std::tm
        std::tm tm = {0};
        tm.tm_sec = 40;
        tm.tm_min = 38;
        tm.tm_hour = 12;
        tm.tm_mday = 10;
        tm.tm_mon = 9;
        tm.tm_year = 112;
        tm.tm_isdst = -1;
        // Convert std::tm to std::time_t (popular extension)
        std::time_t tt = timegm(&tm);
        // Convert std::time_t to std::chrono::system_clock::time_point
        std::chrono::system_clock::time_point tp = 
                                         std::chrono::system_clock::from_time_t(tt);
        // Add 0.123456 seconds
        // This will not compile if std::chrono::system_clock::time_point has
        //   courser resolution than microseconds
        tp += std::chrono::microseconds(123456);
        
        // Now output tp
    
        // Convert std::chrono::system_clock::time_point to std::time_t
        tt = std::chrono::system_clock::to_time_t(tp);
        // Convert std::time_t to std::tm (popular extension)
        tm = std::tm{0};
        gmtime_r(&tt, &tm);
        // Output month
        std::cout << tm.tm_mon + 1 << '-';
        // Output day
        std::cout << tm.tm_mday << '-';
        // Output year
        std::cout << tm.tm_year+1900 << ' ';
        // Output hour
        if (tm.tm_hour <= 9)
            std::cout << '0';
        std::cout << tm.tm_hour << ':';
        // Output minute
        if (tm.tm_min <= 9)
            std::cout << '0';
        std::cout << tm.tm_min << ':';
        // Output seconds with fraction
        //   This is the heart of the question/answer.
        //   First create a double-based second
        std::chrono::duration sec = tp - 
                                        std::chrono::system_clock::from_time_t(tt) +
                                        std::chrono::seconds(tm.tm_sec);
        //   Then print out that double using whatever format you prefer.
        if (sec.count() < 10)
            std::cout << '0';
        std::cout << std::fixed << sec.count() << '\n';
    }
    

    For me this outputs:

    10-10-2012 12:38:40.123456
    

    Your std::chrono::system_clock::time_point may or may not be precise enough to hold microseconds.

    Update

    An easier way is to just use this date library. The code simplifies down to (using C++14 duration literals):

    #include "date.h"
    #include 
    #include 
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono;
        auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
        static_assert(std::is_same>{}, "");
        std::cout << t << '\n';
    }
    

    which outputs:

    2012-10-10 12:38:40.123456
    

    You can skip the static_assert if you don't need to prove that the type of t is a std::chrono::time_point.

    If the output isn't to your liking, for example you would really like dd-mm-yyyy ordering, you could:

    #include "date.h"
    #include 
    #include 
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono;
        using namespace std;
        auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
        auto dp = floor(t);
        auto time = make_time(t-dp);
        auto ymd = year_month_day{dp};
        cout.fill('0');
        cout << ymd.day() << '-' << setw(2) << static_cast(ymd.month())
             << '-' << ymd.year() << ' ' << time << '\n';
    }
    

    which gives exactly the requested output:

    10-10-2012 12:38:40.123456
    

    Update

    Here is how to neatly format the current time UTC with milliseconds precision:

    #include "date.h"
    #include 
    
    int
    main()
    {
        using namespace std::chrono;
        std::cout << date::format("%F %T\n", time_point_cast(system_clock::now()));
    }
    

    which just output for me:

    2016-10-17 16:36:02.975
    

    C++17 will allow you to replace time_point_cast with floor. Until then date::floor is available in "date.h".

    std::cout << date::format("%F %T\n", date::floor(system_clock::now()));
    

    Update C++20

    In C++20 this is now simply:

    #include 
    #include 
    
    int
    main()
    {
        using namespace std::chrono;
        auto t = sys_days{10d/10/2012} + 12h + 38min + 40s + 123456us;
        std::cout << t << '\n';
    }
    

    Or just:

    std::cout << std::chrono::system_clock::now() << '\n';
    

    std::format will be available to customize the output.

提交回复
热议问题