../

Summary

I had a library I wrote in the past that performed certain functions, and as part of the feature set requested by the original customer, the library also measured the length of time it took to complete each task. The time measurements were provided to the caller as time_t variables, and the start and end timestamps of each "event" was obtained using the standard and very common time(3) system call. That gave it 1-second resolution, which at the time was acceptable since the tasks it measured would often take minutes or hours to complete.

But computers have gotten faster, network speeds have also gotten faster, and the library I wrote is now being used in a slightly different way than I originally anticipated. 1-second resultion was no longer fast enough. I needed to update the library to have sub-second resolution. Here is how I used boost::date_time and replaced all my uses of time_t with something better.

Types

The C type time_t suffers from some of the classic problems one can expect to see when working with parts of the original C language dating back to the early 1970s:

The boost::date_time library solves these problems. For example, the library provides a boost::posix_time::ptime type as well as a boost::posix_time::time_duration type, and both of these can be initialized with the special value boost::date_time::not_a_date_time. This way, variables cannot be confused. If your ptime variable is set to zero, you know for certain it means epoch, and not a timestamp that hasn't yet been initialized.

Timestamps

Timestamps are the most common use. Often obtained with a call time time(3), timestamps represent the number of seconds since 1970-01-01 00:00:00 (aka UNIX epoch). Code that uses time_t variables to record such a timestamp might look like this:

#include <time.h> ... time_t now = 0; ... now = time( NULL );

When using boost::date_time, the same code would look like this:

#include <boost/date_time.hpp> ... boost::posix_time::ptime now = boost::date_time::not_a_date_time; ... now = boost::posix_time::microsec_clock::universal_time();
The default constructor for ptime is not_a_date_time, so there isn't a need to explicitely initialize the variable as I did in this example.

The ptime variables can be initialized many different ways. Several examples:

Durations

If you subtract two time_t variables to determine the length of time between them, you end up with a third time_t variable, and it is up to the developer to ensure the right logic is in place so that 3rd time_t isn't accidentally used as a timestamp. It represents a duration, not a timestamp.

With boost::date_time, the difference between two ptime variables is a boost::posix_time::time_duration. This ensures the result isn't accidentally used or interpreted as a point in time. For example:

#include <boost/date_time.hpp> ... boost::posix_time::ptime start = boost::posix_time::microsec_clock::universal_time(); performLongTask(); // time how long this takes to run boost::posix_time::ptime end = boost::posix_time::microsec_clock::universal_time(); boost::posix_time::time_duration d = end - start;

As expected, time durations can be added or subtracted from timestamps. Past and future events can be described as follows:

boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); boost::posix_time::time_duration fiveSecs = boost::posix_time::seconds( 5 ); boost::posix_time::ptime future = now + fiveSecs; boost::posix_time::ptime past = now - boost::posix_time::hours( 48 );

String Conversions

Objects from the boost::date_time library can be easily formatted as strings. Two of the simpler helper functions to call are:

For example:

const boost::posix_time::ptime now = ... std::string t = boost::posix_time::to_iso_extended_string( now );

Additional reading

Last modified: 2014-08-29
Stéphane Charette, stephanecharette@gmail.com
../