data one
data one

Reputation: 103

How to Convert "struct tm" (VTYPE_TM) to DATE(double) - Native API?

I am writing a small DLL in native C++ (Visual Studio 2013 version) using Native API technology.

From an external program inside my DLL, I get the Date (no Time: only day, month, year) as a variable of type VTYPE_TM (this is "struct tm").

if (paParams[0].vt == VTYPE_TM) 
 double dateFrom = (double)paParams[0].tmVal ; // convert error - but how ?

Next, I need to convert this date, which is contained inside the tm structure, to the DATE(double) type.

More information about DATE(double) can be found here - in the online calculator https://planetcalc.com/7027/.

Now the actual question and my problem: how do I convert "struct tm" to DATE(double) ?

As I understand it, we need to write a function that will convert each field value from the tm structure to the required double. But isn't there a ready-made solution? Perhaps I need to use some intermediate type, but I don't know which one.

Can you please tell me how to implement this?

A question for the hardcore C ++ programming gurus!

Upvotes: 2

Views: 191

Answers (1)

Howard Hinnant
Howard Hinnant

Reputation: 219508

This can easily be done with C++20 <chrono>. Unfortunately this part of C++20 isn't shipping yet, but vendors are busy implementing it.

Fortunately there exists a free, open-source, header-only preview of this part of C++20 which works with C++11/14/17. Additionally, the preview has an examples page with this very problem already worked out as an example.

Here is the example translated to just convert back and forth with type double, including all of the test cases from the documentation page you link to.

#include "date/date.h"
#include <ctime>
#include <iostream>

template <class D>
date::sys_time<D>
to_sys_time(double dt)
{
    using namespace date;
    using namespace std::chrono;
    using fdays = duration<double, days::period>;
    using ftime = time_point<system_clock, fdays>;
    auto ft = ftime{fdays{dt}};
    if (ft < ftime{})
    {
        auto d = time_point_cast<days>(ft);
        auto t = d - ft;
        ft = d + t;
    }
    ft -= sys_days{1970_y/January/1} - sys_days{1899_y/December/30};
    return round<D>(ft);
}

template <class D>
double
to_double(date::sys_time<D> tp)
{
    using namespace date;
    using namespace std::chrono;
    using fdays = duration<double, days::period>;
    using ftime = time_point<system_clock, fdays>;
    auto ft = ftime{tp} + (sys_days{1970_y/January/1} - sys_days{1899_y/December/30});
    if (ft >= ftime{})
        return ft.time_since_epoch().count();
    auto d = floor<days>(ft);
    auto t = d - ft;
    return (d + t).time_since_epoch().count();
}

void
test(double d)
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;

    auto t = to_sys_time<seconds>(d);
    auto d2 = to_double(t);
    cout << d << " is " << t << " which is " << d2 << '\n';
}

int
main()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    test(-1.25);
    test(-1.3);
    test(-0.5);
    test(0.5);

    year_month_day today = floor<days>(system_clock::now());

    std::tm tm{};
    tm.tm_year = int{today.year()} - 1900;
    tm.tm_mon = unsigned{today.month()} - 1;
    tm.tm_mday = unsigned{today.day()};

    sys_days sd = year{tm.tm_year + 1900}/(tm.tm_mon + 1)/tm.tm_mday;
    cout << "Today " << today << " is " << to_double(sd) << '\n';
}

This currently outputs:

-1.25 is 1899-12-29 06:00:00 which is -1.25
-1.3 is 1899-12-29 07:12:00 which is -1.3
-0.5 is 1899-12-30 12:00:00 which is 0.5
0.5 is 1899-12-30 12:00:00 which is 0.5
Today 2021-01-30 is 44226

The first four lines simply confirm that the conversion functions result in the correct values according to your documentation.

Finally the current date (UTC) is obtained from system_clock::now(), converted to a std::tm, and then converted back to a days-precision chrono::time_point (based on system_clock). This time_point is then fed into to_double to convert it to a double, which currently results in 44226.

These conversion functions are needlessly complicated to take into account the bizarre history of this format described in the last paragraph of your documentation.

Upvotes: 2

Related Questions