Converting between timespec & std::chrono

I was working with some POSIX APIs recently and needed to supply a timespec value. I primarily work with std::chrono types in C++ and was surprised that there were no (obvious) existing conversion methods. Below are a few utility functions that I came up with to handle common conversions.

Table of Contents

  1. timespec Refresher
  2. Conversion Functions
  3. Bonus: timeval conversions
  4. Further Reading

timespec Refresher

As a quick refresher, timespec is a type defined in the ctime header (aka time.h). The timespec type can be used to store either a time interval or absolute time. The type is a struct with two fields:

struct timespec {
   time_t   tv_sec;
   long     tv_nsec;
}

The tv_sec field represents either a general number of seconds, or seconds elapsed since 1970, and tv_nsec represents the count of nanoseconds.

Conversion Functions

A timespec can represent either an absolute time or time interval. With std::chrono, these are two separate concepts: std::chrono::duration represents an interval, while std::chrono::time_point represents an absolute time.

We need for four functions to convert between the two C++ time concepts and timespec:

  1. timespec to std::chrono::duration
  2. std::chrono::duration to timespec
  3. timespec to std::chrono::timepoint
  4. std::chrono::time_point to timespec

timespec to std::chrono::duration

Converting from a timespec to a std::chrono::duration (nanoseconds below) is straightforward: we convert tv_sec to std::chrono::seconds and tv_nsec to std::chrono::nanoseconds, and then cast the result to our target return type, std::chrono::nanoseconds.

using std::chrono; // for example brevity

constexpr nanoseconds timespecToDuration(timespec ts)
{
    auto duration = seconds{ts.tv_sec} 
        + nanoseconds{ts.tv_nsec};

    return duration_cast<nanoseconds>(duration);
}

std::chrono::duration to timespec

Converting from std::chrono::duration to timespec is a two step process. First we capture the portion of the duration which can be represented by a round number of seconds. We subtract this count from the total duration to get the remaining nanosecond count.

Once we have the two components, we can create our timespec value.

using std::chrono; // for example brevity

constexpr timespec durationToTimespec(nanoseconds dur)
{
    auto secs = duration_cast<seconds>(dur);
    dur -= secs;

    return timespec{secs.count(), dur.count()};
}

timespec to std::chrono::timepoint

For the std::chrono::time_point examples, I've used the system_clock as the reference clock.

To convert a timespec value to std::chrono::time_point, we first use our timespecToDuration() function to get a std::chrono::duration. We then use a duration_cast to convert std::chrono::duration to our reference clock duration (system_clock::duration).

We can then create a std::chrono::time_point value from our std::chrono::system_clock::duration.

using std::chrono; // for example brevity

constexpr time_point<system_clock, nanoseconds>
    timespecToTimePoint(timespec ts)
{
    return time_point<system_clock, nanoseconds>{
        duration_cast<system_clock::duration>(timespecToDuration(ts))};
}

std::chrono::time_point to timespec

To convert from a std::chrono::time_point to timespec, we take a similar approach to the std::chrono::duration conversion.

First we capture the portion of the duration which can be represented by a round number of seconds. We subtract this count from the total duration to get the remaining nanosecond count.

Once we have the two components, we can create our timespec value.

using std::chrono; // for example brevity

constexpr timespec timepointToTimespec(
    time_point<system_clock, nanoseconds> tp)
{
    auto secs = time_point_cast<seconds>(tp);
    auto ns = time_point_cast<nanoseconds>(tp) -
             time_point_cast<nanoseconds>(secs);

    return timespec{secs.time_since_epoch().count(), ns.count()};
}

Bonus: timeval conversions

Another common time structure with POSIX systems is timeval, which is defined in the sys/time.h. This type is very similar to timespec:

struct timeval
{
    time_t         tv_sec;
    suseconds_t    tv_usec;
}

We can convert between timeval and std::chrono types in the same manner shown above, except std::chrono::microseconds is used in place of std::chrono::nanoseconds.

using std::chrono; // for example brevity

constexpr microseconds timevalToDuration(timeval tv)
{
    auto duration = seconds{tv.tv_sec} + microseconds{tv.tv_usec};

    return duration_cast<microseconds>(duration);
}

Further Reading

Related Articles