Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a boost::posix_time::ptime object from a 64 bit integer second count

I have a 32 bit Linux system in which I have to record data that is timestamped with a UINT32 second offset from an epoch of 1901-01-01 00:00:00.

Calculating the timestamp is ok for me as I can use the 64 bit ticks() counter and ticks_per_second() functions to generate the seconds since epoch as follows (I only require second level resolution)

const ptime ptime_origin(time_from_string("1901-01-01 00:00:00"));
time_duration my_utc = microsec_clock::universal_time() - ptime_origin;
boost::int64_t tick_per_sec = my_utc.ticks_per_second();
boost::int64_t tick_count = my_utc.ticks();
boost::int64_t sec_since_epoch = tick_count/tick_per_sec;

This works for me since I know that as an unsigned integer, the seconds count will not exceed the maximum UINT32 value (well not for many years anyway).

The problem I have is that my application can receive a modbus message containing a UINT32 value for which I have to set the hardware and system clock with an ioctl call using RTC_SET_TIME. This UINT32 is again the offset in seconds since my epoch 1901-01-01 00:00:00.

My problem now is that I have no way to create a ptime object using 64 bit integers - the ticks part of the time_duration objects is private and I am restricted to using long which on my 32 bit system is just a 4-byte signed integer not large enough to store the seconds offset from my epoch.

I have no control over the value of the epoch and so I am really stumped as to how I can create my required boost::posix_time::ptime object from the data I have. I can probably obtain a dirty solution by calculating hard second counts to particular time intervals and using an additional epoch to make a bridge to allow this but I was wondering if there is something in the boost code that will allow me to solve the problem entirely using the boost datetime library. I have read all the documentation I can find but I cannot see any obvious way to do this.

EDIT: I found this related question Convert int64_t to time_duration but the accepted answer there does NOT work for my epoch

like image 460
mathematician1975 Avatar asked Oct 15 '13 10:10

mathematician1975


2 Answers

Although boost::posix_time::seconds cannot be used if the seconds represent a number greater than 32 bits (as of Oct 2014), it turns out that boost::posix_time::milliseconds can easily be used (without workarounds), as follows:

inline std::string convertMsSinceEpochToString(std::int64_t const ms)
{
    boost::posix_time::ptime time_epoch(boost::gregorian::date(1970, 1, 1));
    boost::posix_time::ptime t = time_epoch + boost::posix_time::milliseconds(ms);
    return boost::posix_time::to_simple_string(t);
}

So, just convert your 64-bit seconds to (64-bit) milliseconds, and you're good to go!


Note Be /very/ aware of compiler dependent behaviour with the capacity of builting integral types:

uint64_t offset = 113ul*365ul*24ul*60ul*60ul*1000ul; // 113 years give or take some leap seconds/days etc.?

would work on GCC or Clang, but it would simply overflow the calculations in MSVC2013. You'd need to explicitly coerce the calulation to 64 bits:

uint64_t offset = uint64_t(113ul)*365*24*60*60*1000;
like image 189
Dan Nissenbaum Avatar answered Sep 21 '22 00:09

Dan Nissenbaum


You could apply time_durations in the maximum allowable increments (which is std::numeric_limits<long>::max()) since the total_seconds field is limited to long (signed).

Note: I worded it as int32_t below so that it will still work correctly if compiled on a 64-bit platform.

Here's a small demonstration:

#include "boost/date_time.hpp"
#include <iostream>

using namespace boost::gregorian; 
using namespace boost::posix_time;

int main()
{
    uint64_t offset = 113ul*365ul*24ul*60ul*60ul; // 113 years give or take some leap seconds/days etc.?

    static const ptime time_t_epoch(date(1901,1,1)); 
    static const uint32_t max_long = std::numeric_limits<int32_t>::max();
    std::cout << "epoch: " << time_t_epoch << "\n";

    ptime accum = time_t_epoch;
    while (offset > max_long)
    {
        accum  += seconds(max_long);
        offset -= max_long;
        std::cout << "accumulating: " << accum << "\n";
    }

    accum += seconds(offset);
    std::cout << "final: " << accum << "\n";
}

Prints:

epoch: 1901-Jan-01 00:00:00
accumulating: 1969-Jan-19 03:14:07
final: 2013-Dec-04 00:00:00

See it Live on Coliru

like image 37
sehe Avatar answered Sep 21 '22 00:09

sehe