Создание объекта boost::posix_time::ptime из 64-битного целого числа секунд

У меня есть 32-битная система Linux, в которой я должен записывать данные с отметкой времени со смещением секунды UINT32 от эпохи 1901-01-01 00:00:00.

Вычисление метки времени меня устраивает, поскольку я могу использовать 64-битный счетчик ticks() и функции ticks_per_second() для генерации секунд с начала эпохи следующим образом (мне требуется только разрешение второго уровня)

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;

Это работает для меня, так как я знаю, что в качестве целого числа без знака количество секунд не превысит максимальное значение UINT32 (ну, во всяком случае, не в течение многих лет).

Моя проблема заключается в том, что мое приложение может получать сообщение Modbus, содержащее значение UINT32, для которого я должен установить аппаратные и системные часы с помощью вызова ioctl с использованием RTC_SET_TIME. Этот UINT32 снова является смещением в секундах с моей эпохи 1901-01-01 00:00:00.

Теперь моя проблема заключается в том, что у меня нет возможности создать объект ptime, используя 64-битные целые числа - часть ticks объектов time_duration является частной, и я ограничен использованием long, который в моей 32-битной системе просто 4-байтовое целое число со знаком, недостаточно большое для хранения смещения секунд от моей эпохи.

У меня нет контроля над значением эпохи, и поэтому я действительно озадачен тем, как я могу создать требуемый объект boost::posix_time::ptime из имеющихся у меня данных. Я, вероятно, могу получить грязное решение, вычислив количество секунд в определенные интервалы времени и используя дополнительную эпоху, чтобы создать мост, позволяющий это сделать, но мне было интересно, есть ли что-то в коде boost, что позволит мне решить проблему полностью, используя библиотека boost datetime. Я прочитал всю документацию, которую смог найти, но не вижу очевидного способа сделать это.

РЕДАКТИРОВАТЬ: я нашел этот связанный вопрос Convert int64_t to time_duration, но принятый ответ там НЕ работает для моей эпохи


person mathematician1975    schedule 15.10.2013    source источник


Ответы (2)


Вы можете применить time_durations с максимально допустимым шагом (то есть std::numeric_limits<long>::max()), поскольку поле total_seconds ограничено long (со знаком).

Примечание. Я обозначил его как int32_t ниже, чтобы он по-прежнему работал корректно при компиляции на 64-битной платформе.

Вот небольшая демонстрация:

#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";
}

Отпечатки:

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

Смотрите Прямой эфир на Coliru

person sehe    schedule 15.10.2013
comment
Спасибо за ответ. Я сам делал нечто подобное, я просто надеялся на решение с повышением, а не обходным путем. Я думаю, что отсутствие ответов здесь говорит о том, что их нет. Я приму ваш ответ, если через пару дней больше ничего не появится. - person mathematician1975; 19.10.2013
comment
Удивительно, что это нужно сделать в октябре 2014 года на C++ (даже с Boost). Почему эти типы давно не были переведены на 64-битные? - person Dan Nissenbaum; 06.10.2014
comment
@DanNissenbaum Я проверил ваш ответ, и он действительно намного элегантнее! Coliru - person sehe; 06.10.2014

Хотя boost::posix_time::seconds нельзя использовать, если секунды представляют число больше 32 бит (по состоянию на октябрь 2014 г.), оказывается, что boost::posix_time::milliseconds можно легко использовать (без обходных путей), следующее:

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);
}

Итак, просто конвертируйте 64-битные секунды в (64-битные) миллисекунды, и все готово!


Примечание Будьте /очень/ осведомлены о поведении, зависящем от компилятора, с возможностью создания целочисленных типов:

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

будет работать на GCC или Clang, но просто переполнит вычисления в MSVC2013. Вам нужно будет явно принудить вычисление к 64 битам:

uint64_t offset = uint64_t(113ul)*365*24*60*60*1000;
person Dan Nissenbaum    schedule 06.10.2014