Marcus Kammarfelt
Marcus Kammarfelt

Reputation: 39

Date and time from Siemens

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

Siemens manual, page 107

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

Answers (2)

Alan Birtles
Alan Birtles

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

Hajo Kirchhoff
Hajo Kirchhoff

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

Related Questions