Search code examples
c++c++11timestampc++-chronotime-t

C++ custom time date struct to utc epoch


I use a library that uses the following struct to define a start timestamp as follows.

    struct SYSTEMTIME {
    /** year */
    WORD year;

    /** month */
    WORD month;

    /** day of week */
    WORD dayOfWeek;

    /** day */
    WORD day;

    /** hour */
    WORD hour;

    /** minute */
    WORD minute;

    /** second */
    WORD second;

    /** milliseconds */
    WORD milliseconds;
};

For every log entry after this time is specified in nanoseconds difference from this first timestamp.

Lets say its UTC 2017-12-19 14:44:00 And the first following log entries are 397000ns after this.

How do I create a chronos, time_t or unix time from epoch from the first SYSTEMTIME struct and then add the nanoseconds to it.

Printout should be for this first entry 2017-12-19 14:44:00.000397

Best regards


Solution

  • Updated

    I've slightly modified the code below to convert between SYSTEMTIME and date::sys_time<std::chrono::milliseconds>, instead of date::sys_time<std::chrono::nanoseconds>.

    Rationale: So that there is no implicit precision loss in to_SYSTEMTIME. Clients of to_SYSTEMTIME can explicitly truncate precision in any way that they desire (floor, round, ceil, etc.). And failure to truncate precision (if needed) won't be a silent run time error.

    The client code (in main) is not impacted by this change.


    You could use Howard Hinnant's free, open-source, header-only date/time library for this:

    #include "date/date.h"
    #include <iostream>
    
    using WORD = int;
    
    struct SYSTEMTIME {
        /** year */
        WORD year;
    
        /** month */
        WORD month;
    
        /** day of week */
        WORD dayOfWeek;
    
        /** day */
        WORD day;
    
        /** hour */
        WORD hour;
    
        /** minute */
        WORD minute;
    
        /** second */
        WORD second;
    
        /** milliseconds */
        WORD milliseconds;
    };
    
    date::sys_time<std::chrono::milliseconds>
    to_sys_time(SYSTEMTIME const& t)
    {
        using namespace std::chrono;
        using namespace date;
        return sys_days{year{t.year}/t.month/t.day} + hours{t.hour} +
               minutes{t.minute} + seconds{t.second} + milliseconds{t.milliseconds};
    }
    
    int
    main()
    {
        using namespace std::chrono;
        using namespace date;
        SYSTEMTIME st{2017, 12, 2, 19, 14, 44, 0, 0};
        auto t = to_sys_time(st) + 397000ns;
        std::cout << floor<microseconds>(t) << '\n';
    }
    

    Output:

    2017-12-19 14:44:00.000397
    

    This converts a SYSTEMTIME to a std::chrono::time_point<system_clock, milliseconds> (which has a type-alias named date::sys_time<milliseconds>) by collecting the different parts out of the SYSTEMTIME. It then simply adds nanoseconds to that time_point, truncates it to the desired precision of microseconds, and streams it out.

    If it would be helpful, here is how you could use the same library to do the opposite conversion:

    SYSTEMTIME
    to_SYSTEMTIME(date::sys_time<std::chrono::milliseconds> const& t)
    {
        using namespace std::chrono;
        using namespace date;
        auto sd = floor<days>(t);
        year_month_day ymd = sd;
        auto tod = make_time(t - sd);
        SYSTEMTIME x;
        x.year = int{ymd.year()};
        x.month = unsigned{ymd.month()};
        x.dayOfWeek = weekday{sd}.c_encoding();
        x.day = unsigned{ymd.day()};
        x.hour = tod.hours().count();
        x.minute = tod.minutes().count();
        x.second = tod.seconds().count();
        x.milliseconds = tod.subseconds().count();
        return x;
    }