the djb way


configuring leapseconds

So you're thinking you are all set: thanks to clockspeed your clock is now tuned with atomic precision, you're connected on your high speed DSL, you're on eBay bidding on that cool MP3 player. Your mouse pointer is poised, ready to click, heart pounding, watching the seconds tick down on xclock -update 1, tick, tick, tick...

Just 3 seconds left: Click! You're currently the high bidder!

But wait: the auction still isn't over! It seems like it keeps running and running for an eternity, at least 20 seconds. In fact, three other bids come in after yours, cruelly smashing your anticipation. With too little time to counter now, it's too late.

You've been out-sniped!

Wha' happen'?

What happened is that clockspeed assumes your system understands leap seconds, whereas your system is actually ignoring them. The difference (as of January 1, 2004) is 22 seconds. (Your computer will appear to be running ahead.)

Solution? Configure your system to understand leap seconds. This is done by setting up the right time zone file.

Linux and OpenBSD

The time zone files on Linux and OpenBSD are located in /usr/share/zoneinfo. Configuring the time zone simply involves making a symbolic link from /etc/localtime to the correct time zone for your locale.

The default Linux and OpenBSD configurations are to assume POSIX rules, which ignore leap seconds. For example, the default time zone setup on my system here in Kampala, Uganda is:

/etc/localtime -> /usr/share/zoneinfo/Africa/Kampala

To configure for leap seconds, just link /etc/localtime to the corresponding time zone file in /usr/share/zoneinfo/right. For example:

# ln -s /usr/share/zoneinfo/right/Africa/Kampala /etc/localtime

That's it. See also ctime(3).


FreeBSD is a little different, in that the installation doesn't include a ./right subdirectory within /usr/share/zoneinfo. Instead, the procedure is to recompile the time zone files from source to include leap seconds.

To do this requires that the source tree is installed. Then perform the following steps:

# cd /usr/src/share/zoneinfo
# make clean
# make -DLEAPSECONDS all install 
# make clean

This rebuilds all the time zone files with the leap second information specified in /usr/src/share/zoneinfo/leapseconds, then reinstalls them into /usr/share/zoneinfo.

Now reset your timezone configuration with the sysinstall tool. This will copy the time zone file for your locale from /usr/share/zoneinfo to /etc/localtime. Alternatively, just make a symlink:

# ln -s /usr/share/zoneinfo/Africa/Kampala /etc/localtime

See also ctime(3) and zdump(8).

Note: the procedure described here was obtained from the clockspeed port instructions: see /usr/ports/sysutils/clockspeed/pkg-message. If you choose to install clockspeed itself from the ports system, please be aware that the clockspeed files will be installed in different locations than those described in the djb way.

about leap seconds...

Inside the heart of your computer beats a time(3) clock, ticking away the absolute number of seconds that have elapsed since the clock struck midnight on the morning of January 1, 1970, UTC.

This time, 01-Jan-1970 00:00:00 UTC, is referred to in Unix time parlance as the Epoch.

Various functions in the Unix standard library are then used to convert the integral number of seconds elapsed since the Epoch into the familiar, human-readable output we get from utilities such as date(1).

UTC, meanwhile, refers to Coordinated Universal Time. It is a relative, astronomical basis of time: based on synchronizing all clocks to 12:00 noon when the sun is observed directly overhead on the prime meridian (0 degrees longitude) at the Old Royal Observatory in Greenwich, England.

UTC time is what is reported by the all the time servers used for setting up clockspeed, such as those listed at

But the UTC clock ticks themselves are not directly synchronized to any solar phenomena. Rather, they are synchronized to the atomic time known as TAI.

TAI refers to Temps Atomique International, International Atomic Time. TAI is an absolute measure of time, based on one second equal to 9,192,631,770 cycles of resonance in cesium 133. The primary timeservers around the world are directly reporting time measured by such atomic clocks.

TAI time begins on 01-Jan-1958 00:00:00 GMT. That is, the TAI clock was originally started in synch with the traditional astronomical basis of time.

Thereafter, though, TAI and the astronomical basis of time begin to drift apart. This is because the Earth's speed of rotation is not constant, while the atomic measure of time is extremely precise.

TAI is based on one day being exactly 86,400 seconds. The Earth's rotation, meanwhile, is a bit wobbly and has been getting a little slower lately. (This was once blamed on the effect of something called tidal drag. Yeah, right. But scientists have now determined that the slowing is actually due to human obesity caused by the exponential expansion of fast food franchises since the Eisenhower administration. How would you like to be planet Earth, trying to spin straight with a bunch of fatties clinging to your skin...)

The UTC clock formally came into being on 01-Jan-1972 00:00:10 TAI. That is, by New Year's day 1972, the Earth's rotation--and therefore its relative mark of time with respect to the Sun--had already lost 10 seconds with respect TAI.

(By convention, the Unix Epoch is now usually defined to include the first 10 leap seconds in UTC--that is, 01-Jan-1970 00:00:10 TAI--even though UTC wasn't formally around then.)

Keeping the UTC clock aligned with Our Mister Sun is a tricky business. Every so often another second has to be added to the TAI reference mark, to keep the Sun overhead when our watches point straight up 12:00 noon.

In fact, as of January 1, 2004, a total of 22 leap seconds have been added to the UTC clock since the Unix Epoch. Since the Unix Epoch is defined into being to include the first 10 leap seconds implied in UTC, these 10 seconds aren't in the leap second table. So even though:

TAI - UTC = 32 leap seconds total (as of 01-Jan-2004)

on Unix systems we only need to worry about the 22 seconds since the Epoch.

UTC leap second adjustments may be applied midnight at either of December 31 or June 30. The last leap second adjustment to UTC was midnight December 31, 1998. (Whoa, 5 years now since the last adjustment!) Adjustments for leap seconds are not predictable.

And how are leap seconds added to UTC, anyway? Just as a day is added to the calendar during leap years, a leap second is added into the UTC clock.

For example, a normal year end would look like this:

31-Dec-1999 23:59:58
31-Dec-1999 23:59:59
01-Jan-2000 00:00:00

The leap second added into the end of 1998 looked like this:

31-Dec-1998 23:59:58
31-Dec-1998 23:59:59
31-Dec-1998 23:59:60 leap second added!
01-Jan-1999 00:00:00

Recall that the Unix Epoch was 01-Jan-1970 00:00:00 UTC, equivalent to 01-Jan-1970 00:00:10 TAI. Since then, if you compared the number of elapsed seconds between the Epoch and, say, Y2K, a UTC-clock calculation would appear to be 22 seconds less than a TAI-based calculation, --that is, if you failed to make any leap second adjustment. In order to get the correct measure of elapsed time from a UTC clock, you need to add back in the leap seconds.

It is relatively trivial for time libraries to refer to a leap second table in order to make this adjustment. It is just a matter of maintaining the leap second table current with any new leap seconds added by the Time Pharaohs, a.k.a. the International Earth Rotation Service (IERS).

Time libraries on our reference operating systems, such as FreeBSD, OpenBSD and Linux, are fully capable of using leap second tables and making adjustments accordingly. Astonishingly, however, the POSIX.1 standard requires that leap seconds be ignored. Thus we read in the man page for ctime(3) on our FreeBSD system, for example:

The asctime(), ctime(), difftime(), gmtime(), localtime(), and mktime() functions conform to ISO/IEC 9899:1990 (``ISO C89''), and conform to ISO/IEC 9945-1:1996 (``POSIX.1'') provided the selected local timezone does not contain a leap-second table... [emphasis added]

When the local timezone does include a leap second table, however, these functions still work just fine. In fact, they are right. They just can't be considered POSIX.1 compatible anymore.

The default for systems like OpenBSD and FreeBSD is to follow POSIX. That's why we find them installed using timezone files without leap second tables. Bernstein, on the other hand, thinks this is nuts, and assumes that the time functions on our host systems will be enabled to handle leap seconds properly.

So sntpclock goes out to your nearest friendly NTP time server, snagging a timing mark in current UTC time; then clockadd computes the actual elapsed seconds from the Epoch, adjusted for leap seconds, and sets the clock tick counter accordingly. Clockspeed (and the libtai library) install their own leap second table in /etc/leapsecs.dat to make the proper calculations.

By installing the right timezone files--those that include leap second tables--the ctime(3) functions will then synch with the timing marks obtained by clockspeed.

And you will never lose another eBay auction again.

how not to do it...

The clever reader may figure out another way to beat the leap second problem. Simply poke clockspeed in the eye, so it won't see leap seconds anymore:

# cp /dev/null /etc/leapsecs.dat

Now clockspeed will be blind to leap seconds, just like POSIX and the default zoneinfo files.

This is not the djb way.

Copyright © 2002, 2003, 2004, Wayne Marshall.
All rights reserved.

Last edit 2004.10.04, wcm.