Reputation: 103
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
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