Преобразовать std:: chrono:: system_clock:: time_point в struct timeval и обратно
Я пишу код на С++, который должен получить доступ к старой библиотеке C, которая использует timeval в качестве представления текущего времени.
В старом пакете для получения текущей даты/времени мы использовали:
struct timeval dateTime;
gettimeofday(&dateTime, NULL);
function(dateTime); // The function will do its task
Теперь мне нужно использовать С++ chrono, что-то вроде:
system_clock::time_point now = system_clock::now();
struct timeval dateTime;
dateTime.tv_sec = ???? // Help appreaciated here
dateTime.tv_usec = ???? // Help appreaciated here
function(dateTime);
Позже в коде мне нужно вернуться назад, построив переменную time_point
из возвращаемого struct timeval
:
struct timeval dateTime;
function(&dateTime);
system_clock::time_point returnedDateTime = ?? // Help appreacited
Я использую С++ 11.
Ответы
Ответ 1
[Отредактировано использование time_val
вместо бесплатных vars]
Предполагая, что вы доверяете system_clock
с точностью до миллисекунды, вы можете сделать следующее:
struct timeval dest;
auto now=std::chrono::system_clock::now();
auto millisecs=
std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()
);;
dest.tv_sec=millisecs.count()/1000;
dest.tv_usec=(millisecs.count()%1000)*1000;
std::cout << "s:" << dest.tv_sec << " usec:" << dest.tv_usec << std::endl;
Используйте std::chrono::microseconds
в duration_cast
и соответствующим образом настройте свой код (div/mod) для более высокой точности - будьте осторожны, насколько вы доверяете точности полученных вами значений.
Обратный возврат:
timeval src;
// again, trusting the value with only milliseconds accuracy
using dest_timepoint_type=std::chrono::time_point<
std::chrono::system_clock, std::chrono::milliseconds
>;
dest_timepoint_type converted{
std::chrono::milliseconds{
src.tv_sec*1000+src.tv_usec/1000
}
};
// this is to make sure the converted timepoint is indistinguishable by one
// issued by the system_clock
std::chrono::system_clock::time_point recovered =
std::chrono::time_point_cast<std::chrono::system_clock::duration>(converted)
;
Ответ 2
См. std::chrono::system_clock::to_time_t()
, который преобразует time_point
в time_t
, который становится вашим tv_sec
. Вы не получаете tv_usec
, вы можете установить его 0; или вы могли бы поиграть с несколькими другими вещами, включая duration_cast
, чтобы извлечь доли секунды из вашего time_point
.
from_time_t
() делает обратное.
Ответ 3
Вот как сделать преобразование без использования ручных коэффициентов преобразования или в зависимости от неуказанного режима округления time_t
:
timeval
to_timeval(std::chrono::system_clock::time_point tp)
{
using namespace std::chrono;
auto s = time_point_cast<seconds>(tp);
if (s > tp)
s = s - seconds{1};
auto us = duration_cast<microseconds>(tp - s);
timeval tv;
tv.tv_sec = s.time_since_epoch().count();
tv.tv_usec = us.count();
return tv;
}
std::chrono::system_clock::time_point
to_time_point(timeval tv)
{
using namespace std::chrono;
return system_clock::time_point{seconds{tv.tv_sec} + microseconds{tv.tv_usec}};
}
to_timeval
заботится о том, чтобы округлить tp
вниз (в случае, если он отрицательный). Спецификация POSIX немного расплывчата в этом, но я предполагаю, что timeval
представляет моменты времени до эпохи с отрицательными значениями tv_sec
, а затем положительные значения tv_usec
. Тогда это простая операция, чтобы найти microseconds
с последнего second
.
Если я ошибаюсь в своем предположении (и можно найти более точную спецификацию POSIX), <chrono>
имеет право моделировать все, что он делает.
Обратное преобразование, предполагая вышеприведенные соглашения, невероятно читаемо. Это не требует комментариев.
Все это можно протестировать следующим образом:
timeval
make_timeval(time_t s, long us)
{
timeval tv;
tv.tv_sec = s;
tv.tv_usec = us;
return tv;
}
bool
operator==(timeval x, timeval y)
{
return x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec;
}
int
main()
{
using namespace std::chrono;
assert(make_timeval(0, 0) == to_timeval(system_clock::time_point{}));
assert(make_timeval(1, 0) == to_timeval(system_clock::time_point{seconds{1}}));
assert(make_timeval(1, 400000) == to_timeval(system_clock::time_point{seconds{1} + microseconds{400000}}));
assert(make_timeval(-1, 400000) == to_timeval(system_clock::time_point{seconds{-1} + microseconds{400000}}));
assert(to_time_point(make_timeval(0, 0)) == system_clock::time_point{});
assert(to_time_point(make_timeval(1, 0)) == system_clock::time_point{seconds{1}});
assert(to_time_point(make_timeval(1, 400000)) == system_clock::time_point{seconds{1} + microseconds{400000}});
assert(to_time_point(make_timeval(-1, 400000)) == system_clock::time_point{seconds{-1} + microseconds{400000}});
}
Все это основывается на предположении, что эпоха для timeval
и system_clock
идентична. Это не указано, но верно для всех существующих реализаций. В любом случае, мы можем стандартизировать эту существующую практику в ближайшем будущем.
Помните, что в POSIX timeval
используется как time_point
, так и duration
. Таким образом, to_time_point
может привести к ошибке времени выполнения, если timeval
в настоящее время представляет продолжительность времени. И to_timeval
может привести к ошибке времени выполнения, если клиент интерпретирует результат как продолжительность времени.