Reputation: 39
Background: I have an old Siemens cpu that will send me a total of 8 bytes as the date_and_time format. This format is constructed so the 4 first byte contains the time and the last 4 bytes contains the date since 1 january 1992.
Problem: I started to extract the last 4 dates to calculate the day by using
long long int dt = 0x60592102FC29000;
char a, b, c, d;
a = (dt & 0xFF);
b = ((dt >> 8) & 0xFF);
c = ((dt >> 16) & 0xFF);
d = ((dt >> 24) & 0xFF);
long int date = int((unsigned char)a << 24 |
(unsigned char)b << 16 |
(unsigned char)c << 8 |
(unsigned char)d);
But after this part i got stuck because i cant figure out any nice way to calculate the day without looping trough all days.
Examples:
0x60592102FC29000 is equal to 2021-06-04 09:55:40.000
0x7A0DC6021C07000 is equal to 1996-12-24 12:55:34.010
References:
T#0d_0h_0m_0s_0ms to T#49d_17h_2m_47s_295ms Maximum of two digits for the values day, hour, minute, second and a maximum of three digits for milliseconds Initialization with T#0d_0h_0m_0s_0ms
D#1992-01-01 to D#2200-12-31 Leap years are taken into account, year has four digits, month and day are two digits each Initialization with D#0001-01-01
Link to siemens way of calculating
My final solutions inspired by posts below:
//1996-12-24 12:55:34.010
long long int dt = 0x7A0DC6021C070000;
const int dateOffset = 8034;
time_t date_t = 0;
int date =
((dt & 0xFF) << 24) |
(((dt >> 8) & 0xFF) << 16) |
(((dt >> 16) & 0xFF) << 8) |
(((dt >> 24) & 0xFF));
int time =
((dt >> 32 & 0xFF) << 24) |
(((dt >> 40) & 0xFF) << 16) |
(((dt >> 48) & 0xFF) << 8) |
(((dt >> 56) & 0xFF));
tm _dt = *localtime(&date_t);
_dt.tm_mday += dateOffset + date;
_dt.tm_sec += time/1000;
mktime(&_dt);
Upvotes: 2
Views: 302
Reputation: 36568
Using Howard Hinnant's date library or the c++20 equivalent will make this easier:
#include "date.h"
#include <chrono>
#include <iostream>
using namespace date;
using namespace date::literals;
int main()
{
date::sys_days epoch = 1992_y / 1 / 1;
uint64_t date_time = 0x60592102'FC290000;
date::days days(
(( date_time & 0xFF ) << 24) |
(( (date_time >> 8) & 0xFF) << 16) |
(( (date_time >> 16) & 0xFF) << 8) |
(( (date_time >> 24) & 0xFF)));
std::chrono::milliseconds time(
((date_time >> 32 & 0xFF) << 24) |
(((date_time >> 40) & 0xFF) << 16) |
(((date_time >> 48) & 0xFF) << 8) |
(((date_time >> 56) & 0xFF)));
std::chrono::system_clock::time_point result = epoch + days + time;
std::cout << result << "\n";
}
Note that your example values seem to be incorrect (missing a trailing 0
). This code prints:
2021-06-05 09:55:40.0000000
Which is one day later than your sample value, I'm not sure if your sample is wrong or if day 1 is the 1st January? The specification you've linked to is incredibly unclear. If the samples are correct then is is easy enough to fix by adding:
epoch -= date::days(1);
The same code using c++20:
#include <chrono>
#include <iostream>
#include <format>
using namespace std::chrono;
using namespace std::literals;
int main()
{
sys_days epoch = 1992y / 1 / 1;
uint64_t date_time = 0x60592102'FC290000;
days days(
((date_time & 0xFF) << 24) |
(((date_time >> 8) & 0xFF) << 16) |
(((date_time >> 16) & 0xFF) << 8) |
(((date_time >> 24) & 0xFF)));
milliseconds time(
((date_time >> 32 & 0xFF) << 24) |
(((date_time >> 40) & 0xFF) << 16) |
(((date_time >> 48) & 0xFF) << 8) |
(((date_time >> 56) & 0xFF)));
system_clock::time_point result = epoch + days + time;
std::cout << std::format("{:%F %T %Z}", result) << "\n";
}
Upvotes: 3
Reputation: 2085
I think you should do yourself a favor and use a chrono library. Date problems are notoriously difficult and error prone to deal with. Using a chrono library your problem boils down to something as simple as
date_time dt=date_time(1992, 1, 1) + days(value);
where value
is the value returned by your Siemens CPU.
boost::chrono or boost::date_time should be available on older cpus including embedded systems. The C++ standard has std::chrono, but the functionality you need is only available with C++20.
Here is an example using boost::date_time:
#include "boost/date_time/gregorian/gregorian.hpp"
date start(1992,Jan,1);
date yourdate=start + days(value);
Upvotes: 3