4.16. TimeVal Class

TimeVal is a wrapper around UNIX struct timeval structure, enhanced with arithmetic operations, logic operations, and string formatting. You can use it as any other built-in data type.


class TimeVal : public timeval
    enum { gmt, loc };

    TimeVal ();
    TimeVal (long sec_, long msec_);
    TimeVal (double d_);
    TimeVal (const timeval& tv_);

    operator double () const;

    void sec (long sec_);
    long sec (void) const;

    void msec (long msec_);
    long msec (void) const;

    void tz (int tz_);
    int tz (void) const;

    TimeVal& operator+= (const TimeVal& rhs_);
    TimeVal& operator-= (const TimeVal& rhs_);

    friend TimeVal operator+(const TimeVal& lhs_, const TimeVal& rhs_);
    friend TimeVal operator-(const TimeVal& lhs_, const TimeVal& rhs_);

    bool    operator< (const TimeVal& rhs_) const;
    bool    operator==(const TimeVal& rhs_) const;

    friend bool operator> (const TimeVal& lhs_, const TimeVal& rhs_);
    friend bool operator!=(const TimeVal& lhs_, const TimeVal& rhs_);
    friend bool operator<=(const TimeVal& lhs_, const TimeVal& rhs_);
    friend bool operator>=(const TimeVal& lhs_, const TimeVal& rhs_);

    string fmtString (const char* fmt_ = NULL) const;

    static TimeVal zeroTime ();
    static TimeVal gettimeofday ();

4.16.2. USAGE

A few words about UNIX time are in order. A millisecond is the next smallest value to a second. A millisecond is 1/1,000 of a second (1 millisecond = 0.001 second). Conversely, one microsecond is 1/1,000 of a millisecond (1 microsecond = 0.001 millisecond). In summary,

1 millisecond = 0.001 second

1 microsecond = 0.000001 second
1 microsecond = 0.001 millisecond 

Standard UNIX timer resolution is close to 10 milliseconds or 0.01 second. This is a minimal granularity of your PC clock and it is called a "clock tick". It is meaningless to wait on select(3) or schedule a timer with Reactor::registerTimerHandler() for the time interval less then 0.01 second.

UNIX structure timeval is defined in terms of seconds and microseconds:

struct timeval {
    int tv_sec;    // seconds 
    int tv_usec;   // microseconds!

More often then not, I'm interested in a delta time between two TimeVal values. To get an ASCII representation of the delta time, consider this example:

void foo ()
    TimeVal start (TimeVal::gettimeofday ());

    // Do some time-intensive data processing ...

    TimeVal end (TimeVal::gettimeofday ());

    end -= start;

    // Printing time in HH:MM:SS.MLS format

    printf ("Delta time: %s.%03ld", 
             end.fmtString ("%T").c_str (),
            (end.msec () % 1000000)/1000);

    // Or printing seconds/milliseconds only: SS.MLS

    printf ("Delta time: %s.%03ld",
            end.fmtString("%S").c_str (),
            (end.msec () % 1000000)/1000);

The output produced will look something along these lines:

Delta time: HH:MM:SS.MLS
Delta time: SS.MLS

There is one very important point that needs discussion - the true sense of present time as the control flows throught your program. This problem pops up its ugly head now and then, taking considerable amount of time to track down. The typical symptom is that your program works just fine until you introduce unexpected delays in your program (such as turning the debugging on). That's when troubles start.

Consider this innocent-looking piece of code:

001 void foo (TimeVal& delay_, TimeValue& projected_)
002 {
003    TimeVal start = ASSA::TimeVal::gettimeofday ();
005    // Use delay_ in some way ...
007    ziigie (start, delay_);
009    // Do some other seemingly quick stuff here ....
011    projected_ = TimeVal::gettimeofday () + delay;
012 }

It seems that this code should bring not surprises when running. I tend to think that when compared, the value of start in line 3 and whatever TimeVal::gettimeofday() returns in line 11 in calculating the value of projected_ is the same, because the call to ziigie() was not suppose to be time-consuming. The truth is that you never know. It might happen that some of the ziigie() code, or some other code ziigie() calls directly or indirectly triggers some really expensive operation such as file I/O (opening a new file). This might be a slow system call that would introduce an unexpected delay. There is nothing wrong with that except for this delay messes up our sense of time back in foo(). Two calls to TimeVal::gettimeofday() standing a couple of lines apart might return vastly different results! This is something to keep in mind.

The way to fix this sort of problems is to store the current time in an automatic variable and use it everywhere else through the body of the function. The example above thus migth be re-written such as:

void foo (TimeVal& delay_, TimeValue& projected_)
    TimeVal now = ASSA::TimeVal::gettimeofday ();
    TimeVal start = now;

    // Use delay_ in some way ...

    ziigie (start, delay_);

    // Do some other seemingly quick stuff here ....

    projected_ = now + delay;