Search code examples
c++cdatetimetimevaltimespec

Better way to get absolute time?


Currently I'm trying to get absolute time to use with pthread_mutex_timedlock. I know I need to add the timeval from gettimeofday to the timespec and then add my arbitrary amount of time.

The below works but it might overflow when multiplying with such large numbers.

Is there a better way to do this (I'm given a time in milliseconds):

struct timespec ts;
struct timeval now;
gettimeofday(&now, nullptr);

ts.tv_sec = now.tv_sec + milliseconds / 1000;
ts.tv_nsec = now.tv_usec * 1000000000 * (milliseconds % 1000);

ts.tv_sec += ts.tv_nsec / (1000000000);
ts.tv_nsec %= (1000000000);

In the above, I add the time given with the current time to get an absolute time.

My alternative code is:

void timeval_to_timespec(struct timeval* tv, struct timespec* ts)
{
    ts->tv_sec = tv->tv_sec;
    ts->tv_nsec = tv->tv_usec * 1000;
}

struct timespec add_timespec(struct timespec* a, struct timespec* b)
{
    struct timespec result = {a->tv_sec + b->tv_sec, b->tv_nsec + b->tv_nsec};
    if(result.tv_nsec >= 1000000000)
    {
        result.tv_nsec -= 1000000000;
        ++result.tv_sec;
    }
    return result;
}

//Convert the milliseconds to timespec.
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds - (ts.tv_sec * 1000)) * 1000000;

//Convert the current time(timeval) to timespec.
timeval_to_timespec(&now, &spec_now);

ts = add_timespec(&ts, &spec_now); //add the milliseconds to the current time.

I'm wondering if there is a better way to do the above. I'd prefer to not use my alternative code but the previous code doesn't seem too safe and I'm not fond of the modulo.

Ideas?


Solution

  • Your first approach is actually reasonable, except that you've made some typos and errors with the constants.

    How about this approach:

    ts.tv_sec = now.tv_sec + milliseconds / 1000;
    ts.tv_nsec = now.tv_usec * 1000 // 1000 ns per us, not a million!
                 + (milliseconds % 1000) * 1000000 // a million ns per ms.
    ts.tv_sec += ts.tv_nsec / 1000000000;
    ts.tv_nsec %= 1000000000;
    

    There's no danger of the second addition overflowing a 32-bit int, because now.tv_usec * 1000 is no more than 999,999,000, and (milliseconds % 1000) * 1000000 is no more than 999,000,000, so the sum is at most 1,998,999,000 (and the number of seconds carried by the last two lines is always 0 or 1).