Search code examples
c++epochc++-chrono

Convert a double to time point using std chrono library


I have a double value that represents an epoch time but added the accuracy of a micro second. So a number like this:

double time_us=1628517578.547;
std::chrono::time_point time(time_us);

The above code doesn't;t work as I am getting the following error:

 no instance of constructor "time_point" matches the argument list  

I need to do this conversion to get the millisecond of the day (number of Milliseconds that passed from the last night).

I plan to use the following code to get the required millisecond:

double sysTOH=time.hour*3600+time.min*60+time.sec+time.usec*1e-6;

What is the best way to achieve this?


Solution

  • std::chrono:: is a lot to write everywhere, so I'm going to assume:

    using namespace std::chrono;
    

    time_point is not a concrete type, it is a class template:

    template<class Clock, class Duration = typename Clock::duration> class time_point;
    

    This means that you have to supply at least the first template parameter, and in your case, it is best to supply the second as well.

    Your input, time_ms, has type double, and represents a count of seconds. So start by creating a type that matches that description:

    using ds = duration<double>;
    

    ds is a duration with a rep of double and a period of ratio<1>.

    Now it is handy to use a little of C++20 <chrono>. Don't worry, if you don't have C++20, there is a free, open-source, header-only preview of it that works with C++11/14/17.

    sys_time<ds> time{ds{time_ms}};
    

    sys_time is a type alias supplied by "date/date.h" for the type:

    time_point<system_clock, duration<double>>
    

    I.e. a time_point based on system_clock using your custom duration type ds (double-based seconds).

    One first converts the raw double to double-based seconds, and then to a time_point based on those seconds.

    Next, it is best to convert to an integral-based time_point to find the time since midnight. Your questions uses microseconds and milliseconds somewhat interchangeably. So I am going to assume milliseconds for everything. Change to microseconds if you need to.

    auto tp = round<milliseconds>(time);
    

    This takes the double-based time_point and converts it to an integral-based time_point that counts milliseconds. round is used to avoid round-off error associated with double-based representations. round is part of C++17 and later, but "date/date.h" will supply it for you in C++11/14.

    The type of tp is time_point<system_clock, milliseconds>.

    Next it is convenient to truncate tp to a precision of days:

    auto td = floor<days>(tp);
    

    floor is part of C++17 and later, but "date/date.h" will supply it for you in C++11/14. days is a day-precision duration. td is simply a count of days since the Unix epoch, and has type time_point<system_clock, days>.

    One can also think of td as a time point to the beginning of the day. So one can subtract it from tp to get the "time of day", or "time since midnight" UTC:

    auto tod = tp - td;
    

    tod has type milliseconds is the value is the number of milliseconds since midnight UTC. If you need midnight defined by some time zone, then there's a little more work to do to take the UTC offset into account. Your question is vague on this point.

    Putting it all together:

    #include "date/date.h"
    #include <chrono>
    #include <iostream>
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono;
    
        double time_ms=1628517578.547;
        using ds = duration<double>;
        sys_time<ds> time{ds{time_ms}};
        auto tp = round<milliseconds>(time);
        auto td = floor<days>(tp);
        auto tod = tp - td;
        std::cout << "tod = " << tod << '\n';
    }
    

    Output:

    tod = 50378547ms