How Does Your Program Read the Current Time?
Your program cannot read the hardware clock directly. It asks the Linux kernel through a system call. Linux provides two system calls for reading the current calendar time: time() (seconds precision) and gettimeofday() (microsecond precision). This post explains both, when to use each, and the structures involved.
Keywords in This Post
gettimeofday() fills a struct timeval with the current time broken into seconds and microseconds since the Epoch. Include <sys/time.h> to use it.
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
/* Returns 0 on success, -1 on error */
struct timeval {
time_t tv_sec; /* Seconds since the Epoch */
suseconds_t tv_usec; /* Additional microseconds (0 to 999999) */
};
| Field | Type | What it holds | Example value |
|---|---|---|---|
| tv_sec | time_t | Whole seconds since Epoch | 1747267215 |
| tv_usec | suseconds_t | Additional microseconds (fractional part) | 715616 |
What does microsecond precision mean in practice?
If you are benchmarking a sorting algorithm that runs in 2.4 milliseconds, you need gettimeofday() — time() with its 1-second precision would show both “start” and “end” as the same second and give you a duration of 0.
The second argument tz (of type struct timezone) is a historical leftover from older UNIX. It was meant to carry timezone information, but the design was flawed. On Linux, it is completely ignored. Always pass NULL.
#include <sys/time.h>
#include <stdio.h>
int main(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) == -1) {
perror("gettimeofday");
return 1;
}
printf("Seconds since Epoch : %ld\n", (long)tv.tv_sec);
printf("Additional microsecs: %ld\n", (long)tv.tv_usec);
/* Convert to a floating-point timestamp */
double now = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
printf("Combined timestamp : %.6f\n", now);
return 0;
}
Sample output:
Seconds since Epoch : 1747267215
Additional microsecs: 715616
Combined timestamp : 1747267215.715616
A common use case is measuring how long a section of code takes to run. Take two timestamps — one before and one after — then compute the difference.
#include <sys/time.h>
#include <stdio.h>
/* Returns elapsed time in milliseconds between two timevals */
double elapsed_ms(struct timeval *start, struct timeval *end)
{
long sec_diff = end->tv_sec - start->tv_sec;
long usec_diff = end->tv_usec - start->tv_usec;
return (double)(sec_diff * 1000) + (double)(usec_diff / 1000.0);
}
int main(void)
{
struct timeval t_start, t_end;
gettimeofday(&t_start, NULL);
/* ---- code to benchmark ---- */
volatile long sum = 0;
for (long i = 0; i < 10000000L; i++)
sum += i;
/* ---- end of timed section -- */
gettimeofday(&t_end, NULL);
printf("Loop took %.3f ms\n", elapsed_ms(&t_start, &t_end));
return 0;
}
time() is the older, simpler sibling of gettimeofday(). It returns the current calendar time as a plain time_t (seconds since Epoch). No microseconds, no structure — just one integer.
#include <time.h>
time_t time(time_t *timep);
/* Returns seconds since Epoch, or (time_t)-1 on error */
There are two ways to use it — both are equivalent:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t t1, t2;
/* Method 1: pass a pointer, result stored in both return value AND t1 */
time(&t1);
/* Method 2: pass NULL — cleaner, result only in return value */
t2 = time(NULL);
printf("t1 = %ld\n", (long)t1);
printf("t2 = %ld\n", (long)t2);
/* t1 and t2 will be the same (or 1 second apart at most) */
return 0;
}
Why does time() exist when gettimeofday() is strictly better?
Historical reasons — time() appeared in early UNIX; gettimeofday() was added later in BSD. Today, time() is implemented as a thin wrapper over gettimeofday() on Linux. Use time(NULL) when you only need whole seconds (e.g., timestamping a log entry, seeding srand()). Use gettimeofday() when sub-second precision matters.
| Feature | time() | gettimeofday() |
|---|---|---|
| Header | <time.h> | <sys/time.h> |
| Precision | Seconds only | Microseconds |
| Return type | time_t | int (fills struct timeval) |
| Use case | Log timestamps, srand() seed | Benchmarking, network RTT |
| Portability | POSIX standard | Widely available (not strictly POSIX) |
Up Next in This Series
Part 4 covers time conversion functions — how to go from a raw time_t value to human-readable output using ctime(), gmtime(), localtime(), and mktime().
