D. J. Bernstein

UTC, TAI, and UNIX time

What is TAI?

TAI, Temps Atomique International (French for International Atomic Time), measures real time. One second of TAI time is a constant duration defined by cesium radiation. TAI has been measured continuously since 1955 and is the foundation of all civil time standards.

TAI times are identified by year, month, day, hour, minute, and second. There are exactly 86400 TAI seconds in every TAI day. TAI days are labelled by the Gregorian calendar.

What is UTC?

One day of Earth's rotation isn't exactly 86400 seconds. It's closer to 86400.002 seconds, wobbling slightly from day to day.

UTC, Coordinated Universal Time, is based on TAI, and very similar to it, except that UTC has leap seconds every year or two. For example, here's how UTC and TAI handled the end of June 1997:

1997-06-30 23:59:59 UTC = 1997-07-01 00:00:29 TAI
1997-06-30 23:59:60 UTC = 1997-07-01 00:00:30 TAI
1997-07-01 00:00:00 UTC = 1997-07-01 00:00:31 TAI

Notice the 23:59:60 in UTC. That's a leap second. It extended 1997-06-30 UTC to 86401 seconds. Before the leap second, the TAI-UTC difference was 30 seconds; after the leap second, the TAI-UTC difference was 31 seconds.

By inserting occasional leap seconds into UTC, astronomers slow down UTC's progression to match Earth's rotation. That way the Sun will always be overhead at 12:00:00 UTC in England. (It's conceivable, but unlikely, that someday Earth's rotation will speed up past 1/86400 Hz. In that case astronomers will create negative leap seconds: UTC will skip from 23:59:58 to 00:00:00.)

Other time zones are based on UTC---e.g., UTC minus 5 hours---so noon has a predictable relationship to the Sun in every time zone.

The leap-second system was introduced at the beginning of 1972. At that point UTC was TAI minus 10 seconds.

What is UNIX time?

UNIX time counts the number of seconds since an ``epoch.'' This is very convenient for programs that work with time intervals: the difference between two UNIX time values is a real-time difference measured in seconds, within the accuracy of the local clock. Thousands of programmers rely on this fact.

What is the epoch? Originally it was defined as the beginning of 1970 GMT. GMT, Greenwich Mean Time, is a traditional term for the time zone in England. Unfortunately, it is ambiguous; it can refer to a variety of astronomical time scales.

Arthur David Olson's popular time library uses an epoch of 1970-01-01 00:00:10 TAI.

What's the problem?

For many years, the UNIX localtime() time-display routine didn't support leap seconds. In effect it treated TAI as UTC. Its displays slipped 1 second away from the correct local time as each leap second passed. Nobody cared; clocks weren't set that accurately anyway.

Unfortunately, xntpd, a program that synchronizes clocks using the Network Time Protocol, pandered to those broken localtime() libraries, at the expense of reliability. Watch how the xntpd time scale increases as a leap second occurs:

1997-06-30 23:59:59.7 UTC -> 867715199.7 xntpd
1997-06-30 23:59:59.8 UTC -> 867715199.8 xntpd
1997-06-30 23:59:59.9 UTC -> 867715199.9 xntpd
1997-06-30 23:59:60.0 UTC -> 867715200.0 xntpd
1997-06-30 23:59:60.1 UTC -> 867715200.1 xntpd
1997-06-30 23:59:60.2 UTC -> 867715200.2 xntpd
1997-06-30 23:59:60.3 UTC -> 867715200.3 xntpd
1997-06-30 23:59:60.4 UTC -> 867715200.4 xntpd
1997-06-30 23:59:60.5 UTC -> 867715200.5 xntpd
1997-06-30 23:59:60.6 UTC -> 867715200.6 xntpd
1997-06-30 23:59:60.7 UTC -> 867715200.7 xntpd
1997-06-30 23:59:60.8 UTC -> 867715200.8 xntpd
1997-06-30 23:59:60.9 UTC -> 867715200.9 xntpd
1997-07-01 00:00:00.0 UTC -> 867715200.0 xntpd
1997-07-01 00:00:00.1 UTC -> 867715200.1 xntpd
1997-07-01 00:00:00.2 UTC -> 867715200.2 xntpd

The xntpd time scale repeats itself! It cannot be reliably converted to UTC.

By resetting the clock at each leap second, xntpd extracts a correct UTC display (except, of course, during leap seconds) from the broken localtime() libraries. Meanwhile, it produces incorrect results for applications that add and subtract real times.

Why not fix it?

It's easy enough to fix xntpd. It's also easy to fix localtime() to handle leap seconds. In fact, some vendors have already adopted Olson's time library.

The main obstacle is POSIX. POSIX is a ``standard'' designed by a vendor consortium several years ago to eliminate progress and protect the installed base. The behavior of the broken localtime() libraries was documented and turned into a POSIX requirement.

Fortunately, the POSIX rules are so outrageously dumb---for example, they require that 2100 be a leap year, contradicting the Gregorian calendar---that no self-respecting engineer would obey them.


The NIST Time and Frequency Division Home Page is a good starting point for programmers who want to learn about time measurement.

The Olson library is available from ftp://elsie.nci.nih.gov/pub/. The above argument against the xntpd time scale is shamelessly stolen from one of Olson's manual pages.

In preparation for the Y2036 and Y2038 disasters, I've put together some 64-bit time manipulation code, including very fast UTC-to-TAI conversion. My library supports the same TAI epoch as the Olson library.

I've also put together a very simple clock-synchronization package, including a Network Time Protocol client that handles leap seconds correctly.