Time and Time Agagin
Pop quiz: what does the UNIX
time(2)
API return?
If you're like most programmers, you probably answered "the number of
seconds since the Epoch." The UNIX epoch began on January 1, 1970.
time(2) simply returns the number of seconds since then. What could be
simpler?
Unfortunately, it's not quite that simple. What time(2) returns is not
the number of seconds since the Epoch. It's a representation of the
UTC time since the start of the Epoch. That may sound like a
distinction without a difference. Are we arguing about the number of
angels that can dance on the head of a pin?
The Earth's day is not exactly 24 hours long. It's slightly shorter
than that. To make up the difference,
leap seconds are
periodically injected into UTC time. The next leap second is scheduled
to be injected on June 30th, 2012.
Unix time is pegged to UTC.
Wikipedia explains
how:
The Unix time number is zero at the Unix epoch, and increases by
exactly 86400 per day since the epoch...
When a leap second occurs, so that the UTC day is not exactly 86400 s
long, a discontinuity occurs in the Unix time number. The Unix time
number increases by exactly 86400 each day, regardless of how long the
day is. When a leap second is deleted (which has never occurred as of
2010), the Unix time number jumps up by 1 where the leap second was
deleted, which is the end of the day. When a leap second is inserted
(which has occurred on average once every year and a half), the Unix
time number increases continuously during the leap second, during which
time it is more than 86 400 s since the start of the current day, and
then jumps down by 1 at the end of the leap second, which is the start
of the next day.
The article goes on to note that on strictly conforming POSIX.1
systems, the time_t value 915148800 repeated twice in a row at the end
of 1998. The same oddity happens after every leap second is injected.
time_t repeats itself, like a broken record. For two seconds in a row,
time() returns the same value!
The implications are clear. time_t isn't an interval. You can't
subtract two time_t values and get the difference in time between them.
You can't wait until time() + 5 and be sure that you are waiting
exactly 5 seconds.
The Importance of being Punctual
The problems surrounding leap seconds may seem relatively
insignificant. After all, most applications are hardly concerned if
they waited 5 seconds or 6. The real question you have to ask yourself
is, am I feeling lucky? (Well, are you, punk?) However, there are
other, bigger problems with the way many programmers use time.
What time(2) returns is often referred to by programmers as
wall-clock time. Wall clock time is a representation of what
the rest of the world thinks the time is. For example, 6:00pm is a
wall clock time.
The problem with wall-clock time is that it can jump all over the
place. If the user opens up the system control panel and changes the
time, the wall clock time experienced by your program will change
abruptly and without warning. If you are running a time
synchronization daemon like ntpd, it will also modify the wall-clock
time periodically.
To see why this could be a problem, imagine the following sequence of
events:
- Program Foo decides to wait 5 seconds. The programmer tells the
computer to sleep until 6:05pm
- The user changes the time to 5:00pm
- Program Foo ends up waiting more than an hour, when in fact all it
really wanted to do was wait for 5 seconds.
To avoid these and similar problems, you should use
monotonic
time whenever possible. Unlike wall-clock time, monotonic time is
simply the number of seconds since some arbitrary starting point.
There are no leap seconds or timezone games, and it never decreases.
The POSIX clock_gettime interface, when used with CLOCK_MONOTONIC, will
give you the monotonic time. You can sleep until a time in the future
using clock_nanosleep. Condition variables can be configured to use
the monotonic time using pthread_cond_attr_setclock.
However, as far as I am aware, there is no way to make
pthread_mutex_timedlock or sem_timedwait use the monotonic clock.
Most of the time, programmers aren't interested in sleeping until a
specific wall-clock time; they simply want to wait for a certain
interval to elapse. Unless you're writing a calendar program, you
probably don't want your condition variable timeouts to change when
somebody changes the system time. However, with the APIs we have
today, that's often the behavior you get.
The future
Hopefully in the future programmers will be more aware of the need for
monotonic time, and POSIX will give us more opportunities to use it.
The International Telecommunications Union is considering
eliminating
leap seconds. If this happens, it will mean an end to the time_t
anomaly.