gettimeofday() - Why use both seconds and microseconds?

2.7k Views Asked by At

I need to use the function gettimeofday for a home assignment, and after reading the man page and looking some online examples, I can't see why people sometimes use both the tv_sec member of the struct and tv_usec member of the struct. The man page states:

  The tv argument is a struct timeval (as specified in <sys/time.h>):

       struct timeval {
           time_t      tv_sec;     /* seconds */
           suseconds_t tv_usec;    /* microseconds */
       };

So, assuming I need to return time in nanoseconds and I have created two timeval structs, start & end and I logged their time with gettimeofday(). Naturally my first thought was to calculate the difference in microseconds, i.e. start.tv_usec-end.tv_usec and then multiply it by 1000 to convert it into nanoseconds.

However, many online examples and tutorials calculate both the differences of the seconds and microseconds, convert them and then add them. (e.g.)

I read through the entire man page and couldn't find out why, would appreciate an explanation, if there is any.

3

There are 3 best solutions below

0
On BEST ANSWER

The "microseconds" field doesn't have enough space to store all the time that passed. The "seconds" field is not precise enough. Together, they provide the most information.

0
On

The tv_sec holds the whole number of seconds since the Unix Epoch — 1970-01-01 00:00:00 +00:00 — and the tv_usec (or tv_nsec when working with the more modern struct timespec) holds the fractional part — values [0..999,999] microseconds for struct timeval and [0..999,999,999] nanoseconds for struct timespec.

In the days before 64-bit integers were widespread, time_t was a 32-bit signed type, and that 'runs out' in January 2038 — there will have been more 2 billion seconds since the epoch. There was no room to store sub-second units in a single integer.

-2147483647 = 1901-12-13 20:45:53 +00:00
+2147483647 = 2038-01-19 03:14:07 +00:00

Even now, if dealing with nanoseconds, you need 30 bits to represent up to 999,999,999 nanoseconds (and 20 bits to represent up to 999,999 microseconds). The requirements for nanoseconds means that it is not sensible to try scaling a 64-bit number to represent seconds and nanoseconds — the Y2K38 problem would be deferred by something like 280 years, whereas the two-part solution puts it off until after the universe ends, which should be long enough for everyone. (I plan to start working on the Y10K problem on 5000-01-02, if I'm still around. )

So, although not ideal, the two part structure makes it easy to use the regular formatting functions that take a time_t value — there are lots of them, and the ones that take a struct tm derived from a time_t value. And the arithmetic on them isn't hard.

I note that, in some respects, both struct timeval and struct timespec are under-specified by POSIX. What is the sign of the tv_usec or tv_nsec portion of a struct timeval or struct timespec if the tv_sec component is negative (see Seconds Since the EpochIf the year is <1970 or the value is negative, the relationship is undefined). POSIX leaves the behaviour of negative values of tv_sec undefined, which is probably sensible, but does make life a bit difficult. Of course, there are also the issues of "when did which part of which country switch between the Julian and Gregorian calendars?" and "was there a year zero?" that would also have to be dealt with. When time_t was a 32-bit number, it reached back as far as December 1901, which largely avoids the problems (but Russia didn't switch from Julian to Gregorian until after the October Revolution, which occurred in November in the Gregorian calendar). Now that time_t is usually a 64-bit number, years BC (or BCE) also become representable.

Be careful when printing the tv_usec or tv_nsec part. Remember to add enough leading zeros by using %.6d for microseconds, %.9ld for nanoseconds, where the necessary conversion specifier (d vs ld vs …) for tv_usec is not clearly defined in POSIX — but d is usually correct. (The tv_nsec member is a long; that's easy and reliable.)

0
On

The time is represented by the whole number of seconds, PLUS the microseconds in the range 0 - 999999.