time() and gettimeofday() return different seconds

Josh Kelley picture Josh Kelley · Apr 7, 2014 · Viewed 12.2k times · Source

On the two systems I've tested (a 32-bit Ubuntu 12.04 server and a 64-bit Ubuntu 13.10 VM), the seconds since the epoch given by time() may differ from gettimeofday()'s.

Specifically, though I call time() after calling gettimeofday(), the value returned by time() is sometimes less than the tv_sec value returned by gettimeofday().

This apparently occurs just after the clock rolls over to a new second.

This caused bugs in some of my code that expected time()'s and gettimeofday()'s seconds to be interchangeable.

Sample code demonstrating this problem:

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main()
{
  time_t start = time(NULL);
  int same = 0;
  int different = 0;
  int max_usec = 0;
  while (1) {
    time_t t;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    t = time(NULL);
    if (t < tv.tv_sec) {
      different++;
      if (tv.tv_usec > max_usec) {
        max_usec = tv.tv_usec;
      }
    } else {
      same++;
    }
    if (t > start + 5) {
      break;
    }
  }
  printf("Same:      %i\n", same);
  printf("Different: %i\n", different);
  printf("Largest difference seen at %i\n", max_usec);
}

Note that I'm calling time() second and only complaining if its value is less than gettimeofday()'s.

Sample output:

Same:      33282836
Different: 177076
Largest difference seen at 5844

I.e., the two values were the same 33 million times, they were different 177k times, and they were always different within 5844 microseconds of a new second.

Is this a known issue? What causes this?

Answer

akira picture akira · May 11, 2014

Both calls are implemented as kernel syscalls. Both functions end up reading a struct timekeeper, both refer to the very same instance. But they differ in what they do with it:

time():

uses the get_seconds() function, which is a shortcut to this:

struct timekeeper *tk = &timekeeper;
return tk->xtime_sec;

it just returns xktime_sec.

gettimeofday():

gettimeofday() on the other hand uses do_gettimeofday() (via getnstimeofday) which reads both fields xktime_sec as well as xktime_nsec (via timekeeping_get_ns). Here it might happen that xktime_nsec holds more nanoseconds than a second. This potential extra time is used to increase the tv_sec field by calling the function timespec_add_ns() which does this:

a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
a->tv_nsec = ns;

So, tv_sec might get bigger than the xktime_sec field was. And there you have it: a little difference in what time() gives you and what gettimeofday() gives you.

I fought against this issue in fluxbox today and until a better solution occurs I live with this:

uint64_t t_usec = gettimeofday_in_usecs(); // calcs usecs since epoch
time_t t = static_cast<time_t>(t_usec / 1000000L);