# [LEAPSECS] BBC radio Crowd Science

Clive D.W. Feather clive at davros.org
Sat Feb 4 10:37:18 EST 2017

```Warner Losh said:
> It's understanding what the weird math is that I'm having trouble with
> for people that say it is after the leap second that the delta
> changes.

[Not picking on Warner; this was just a convenient hook.]

I've been looking at this topic for work purposes and ended up doing a fair
bit of analysis on it. At the end, I concluded that IERS are correct and
the TAI-UTC delta *does* change after the leap second, at the start of the
new minute/day/month.

[Thanks to Steve Summit for reviewing a first draft of this.]

In the following, I'm going to represent a "broken-down" time in the form
D:H:M:S, where D is the number of days since some epoch date and H, M, and S
are hours, minutes, and seconds. 0 <= S < 61 and is a real number, whereas
D, H, and M are integers. D is, of course, a constant offset from the
integer part of the MJD, where the constant depends on the epoch date chosen.
The choice of epoch date is irrelevant to this discussion.

(I use D rather than year, month, and day to avoid the irrelevant (to this)
complexities of the Gregorian calendar. Inserting them back in just makes
the calculations longer but doesn't affect my point.)

Any time can be represented by two different broken-down times, one in TAI
and one in UTC. So if today had D=123456, then right now is:
123456:15:28:47 TAI
123456:15:28:10 UTC

Now define the "linearization" of a broken-down time as:

L = D*86400 + H*3600 + M*60 + S

In TAI, L is the number of seconds since 00:00:00 on the epoch date.

In UTC L is *NOT* the number of seconds since anything useful, but is rather
just a number that turns out to be useful. With an epoch date of 1970-01-01,
L gives you the Posix time_t value, but that is not relevant here. It *is*
important to note that the calculation of L in UTC is lossy in two different
ways:
* at a positive leap second, the same L value represents two different times;
* at a negative leap seconds, some L values don't represent any possible
time.
As we will see, this lossiness turns out not to matter.

A reasonable meaning of "TAI-UTC" is "L(D:H:M:S TAI) - L(D:H:M:S UTC)". This
certainly has the expected meaning for times that are not in the
neighbourhood of a leap second. So let's see what happens when we reach a
leap second. (In the following I use 0.5 second times to prevent arguments

Case A: a positive leap second at the end of D=99 (UTC) from TAI-UTC = 42
to TAI-UTC = 43:

UTC                          TAI
99:23:59:56.5 L=8639996.5   100:00:00:38.5 L=8640038.5    TAI-UTC = 42
99:23:59:57.5 L=8639997.5   100:00:00:39.5 L=8640039.5    TAI-UTC = 42
99:23:59:58.5 L=8639998.5   100:00:00:40.5 L=8640040.5    TAI-UTC = 42
99:23:59:59.5 L=8639999.5   100:00:00:41.5 L=8640041.5    TAI-UTC = 42
99:23:59:60.5 L=8640000.5   100:00:00:42.5 L=8640042.5    TAI-UTC = 42
100:00:00:00.5 L=8640000.5   100:00:00:43.5 L=8640043.5    TAI-UTC = 43
100:00:00:01.5 L=8640001.5   100:00:00:44.5 L=8640044.5    TAI-UTC = 43
100:00:00:02.5 L=8640002.5   100:00:00:45.5 L=8640045.5    TAI-UTC = 43

(Note that the UTC linearization is lossy because 8640000.5 is ambiguous.)

Case B: a negative leap second at the end of D=99 (UTC) from TAI-UTC = 42
to TAI-UTC = 41:

UTC                          TAI
99:23:59:56.5 L=8639996.5   100:00:00:38.5 L=8640038.5    TAI-UTC = 42
99:23:59:57.5 L=8639997.5   100:00:00:39.5 L=8640039.5    TAI-UTC = 42
99:23:59:58.5 L=8639998.5   100:00:00:40.5 L=8640040.5    TAI-UTC = 42
100:00:00:00.5 L=8640000.5   100:00:00:41.5 L=8640041.5    TAI-UTC = 41
100:00:00:01.5 L=8640001.5   100:00:00:42.5 L=8640042.5    TAI-UTC = 41
100:00:00:02.5 L=8640002.5   100:00:00:43.5 L=8640043.5    TAI-UTC = 41

(Note that the UTC linearization is lossy because 8639999.5 does not
represent any time.)

>From this, it becomes very clear that we have a consistent system of
calculation *and* the value of TAI-UTC changes at the boundary between UTC
days.

--------

However, to make this really useful, we need to know how to convert between
TAI and UTC in their various forms.

To do this, we require some kind of leap second table. I'm not going to
worry about the exact form of this, but instead represent it as a pair of
functions:
Delta_at_TAI (t) is defined as the value of TAI-UTC at the TAI time t,
which can be a TAI broken-down time or linearization
since the mapping between them is a bijection.
Delta_at_UTC (t) is defined as the value of TAI-UTC at the UTC time t,
which must be a UTC broken-down time.
Creating these functions from other representations is left as an exercise
for the reader. Note that, for the current IERS rules, Delta_at_UTC only
depends on the year and month, not the day-of-month or time, but this is
irrelevant to what follows; all that matters is that positive leap seconds
add a 61st second (numbered 60) to some minute.

Conversion from UTC to TAI is the easier of the two: to convert UTC time t
to TAI:
E = Delta_at_UTC (t)
L = linearize (t) + E
D = int(L / 86400)
H = int((L - D * 86400) / 3600)
M = int((L - D * 86400 - H * 3600) / 60)
S = L - D * 86400 - H * 3600 - M * 60

(The last four lines, obviously, are the inverse of the linearization
process.)

We might think that conversion from TAI to UTC is done in exactly the same
way: to convert TAI time t to UTC:
E = Delta_at_TAI (t)
L = linearize (t) - E
D = int(L / 86400)
H = int((L - D * 86400) / 3600)
M = int((L - D * 86400 - H * 3600) / 60)
S = L - D * 86400 - H * 3600 - M * 60

In fact, this works *nearly* all the time, because nearly all the time
there is exactly one D:H:M:S value that linearizes to L and a simple check
shows it gives the right answer. For example, looking at the first line of
both cases A and B above:
t = 99:23:59:56.5
E = 42
L = 8640038.5
D = 100
H = 0
M = 0
S = 38.5
Result: 100:00:00:38.5 TAI.

If you track through cases A and B, you'll find that this always gives the
right answer with a single exception in case A. Here, both 100:00:00:42.5 TAI
and 100:00:00:43.5 TAI will generate the same value of L (8640000.5), which
then converts to 100:00:00:00.5 UTC; this is wrong for the first of the two.
However, it turns out to be very easy to both detect and correct for this
case.

The correct algorithm for converting from TAI to UTC is:
E = Delta_at_TAI (t)
F = 1    if Delta_at_TAI (t + 1) >  E
0    if Delta_at_TAI (t + 1) <= E
L = linearize (t) - E - F
D = int(L / 86400)
H = int((L - D * 86400) / 3600)
M = int((L - D * 86400 - H * 3600) / 60)
S = L - D * 86400 - H * 3600 - M * 60 + F

(F is 1 if and only if the UTC time is XX:XX:XX:60.X)

--
Clive D.W. Feather          | If you lie to the compiler,
Email: clive at davros.org     | it will get its revenge.
Web: http://www.davros.org  |   - Henry Spencer
Mobile: +44 7973 377646
```