Search code examples
cintervalstimeval

Add or subtract an interval from a timeval


I've seen a number of questions / answers for subtracting two datetimes here but not one for adding or subtracting an interval to / from a timevalue.

Add milliseconds to timeval C++ This asnwer does not result in a valid timeval structure.

void addms2timeval(int microseconds, timeval tv); //prototype (doesn't exist yet)
...
    struct timeval later; 
    gettimeofday(&later, NULL);
    addms2timeval(1000, later);
    //later is now 1000 us in the future. 

Also doesn't seem to be part of the standard C libraries unless I missed it. https://pubs.opengroup.org/onlinepubs/007908775/xsh/systime.h.html https://www.gnu.org/software/libc/manual/html_node/Date-and-Time.html

Perhaps it's trivial, but it seems like one of those things that you can think you coded correctly but which then fails randomly every so often due to over / under flows.

Has anyone written and tested this?


Solution

  • I have a pair of files, timeval_math.c and timeval_math.h that probably show you how to do what you want.

    timeval_math.h

    #ifndef JLSS_ID_TIMEVAL_MATH_H
    #define JLSS_ID_TIMEVAL_MATH_H
    
    #include <sys/time.h>
    
    extern void sub_timeval(struct timeval t1, struct timeval t2, struct timeval *td);
    extern void add_timeval(struct timeval t1, struct timeval t2, struct timeval *td);
    extern int  cmp_timeval(struct timeval t1, struct timeval t2);
    
    #endif /* JLSS_ID_TIMEVAL_MATH_H */
    

    timeval_math.c

    #include "timeval_math.h"
    
    enum { US_PER_SECOND = 1000000 };
    
    void sub_timeval(struct timeval t1, struct timeval t2, struct timeval *td)
    {
        td->tv_usec = t2.tv_usec - t1.tv_usec;
        td->tv_sec  = t2.tv_sec - t1.tv_sec;
        if (td->tv_sec > 0 && td->tv_usec < 0)
        {
            td->tv_usec += US_PER_SECOND;
            td->tv_sec--;
        }
        else if (td->tv_sec < 0 && td->tv_usec > 0)
        {
            td->tv_usec -= US_PER_SECOND;
            td->tv_sec++;
        }
    }
    
    void add_timeval(struct timeval t1, struct timeval t2, struct timeval *td)
    {
        td->tv_usec = t2.tv_usec + t1.tv_usec;
        td->tv_sec  = t2.tv_sec + t1.tv_sec;
        if (td->tv_usec >= US_PER_SECOND)
        {
            td->tv_usec -= US_PER_SECOND;
            td->tv_sec++;
        }
        else if (td->tv_usec <= -US_PER_SECOND)
        {
            td->tv_usec += US_PER_SECOND;
            td->tv_sec--;
        }
    }
    
    int cmp_timeval(struct timeval t1, struct timeval t2)
    {
        if (t1.tv_sec < t2.tv_sec)
            return -1;
        else if (t1.tv_sec > t2.tv_sec)
            return +1;
        else if (t1.tv_usec < t2.tv_usec)
            return -1;
        else if (t1.tv_usec > t2.tv_usec)
            return +1;
        else
            return 0;
    }
    

    addms2timeval()

    Clearly, you can write an addms2timeval() function, but it isn't clear to me how your interface (void addms2timeval(int microseconds, timeval tv); returns the value. You should probably use one of these two interfaces:

    struct timeval addms2timeval(int microseconds, struct timeval tv);
    void addms2timeval(int microseconds, struct timeval *tv);
    

    Assuming the latter, you can write:

    void addms2timeval(int microseconds, struct timeval *tv)
    {
         add_timeval(*tv, (struct timeval){.tv_sec = 0, .tv_usec = microseconds}, tv);
    }
    

    The second argument to add_timeval() is a compound literal, a feature added to C99.

    This code may be used for any purpose. There is no warranty.