Subject: bug#26632: TAI<->UTC conversion botches 1961 to 1971

From: Zefram <[email-masked]>
Date: Mon, 24 Apr 2017 02:02:18 +0100

The SRFI-19 library gets TAI<->UTC conversions badly wrong in the years
1961 to 1971 (inclusive).

This has to be examined somewhat indirectly, because SRFI-19 doesn't offer
any way to display a TAI time in its conventional form as a date-like
structure, nor to input a TAI time from such a structure.  SRFI-19's
date structure, as implemented, is always interpreted according to UTC.
The only operations supported on TAI time structures are conversions to
and from the various forms of UTC, conversions to and from the less-useful
`monotonic' time, and arithmetic operations.  Thus the erroneous TAI<->UTC
conversions only come out through arithmetic operations in TAI space.
One must also be careful to avoid unrelated bugs such as bug#21911.

First I'll consider an ordinary day in 1967:

scheme@(guile-user)> (use-modules (srfi srfi-19))
scheme@(guile-user)> (time-difference
... (time-utc->time-tai (date->time-utc (make-date 0 0 0 0 15 3 1967 0)))
... (time-utc->time-tai (date->time-utc (make-date 0 0 0 0 14 3 1967 0))))
$1 = #<time type: time-duration nanosecond: 0 second: 86400>

This takes the start and end of 1967-03-14, as judged by UTC, converts
both of these times to TAI, and asks for the duration of that TAI
interval.  It's asking how many TAI seconds long that UTC day was.
As described in <http://maia.usno.navy.mil/ser7/tai-utc.dat>, there
was no UTC leap on that day, but throughout 1967 UTC had a frequency
offset from TAI such that each UTC second lasted exactly 1.00000003 TAI
seconds.  The correct answer to the above question is therefore exactly
86400.002592 s.  The answer shown above, of 86400.000000 s, is incorrect.

If time-tai->time-utc is applied to the times in the above example,
it accurately inverts what time-utc->time-tai did.  It is good that the
conversions are mutually consistent, but in this case it means they are
both wrong.

Second, I'll consider a less ordinary day:

scheme@(guile-user)> (time-difference
... (time-utc->time-tai (date->time-utc (make-date 0 0 0 12 1 2 1968 0)))
... (time-utc->time-tai (date->time-utc (make-date 0 0 0 12 31 1 1968 0))))
$2 = #<time type: time-duration nanosecond: 0 second: 86400>

This time the period considered is from noon 1968-01-31 to noon
1968-02-01.  The same frequency offset described above applies throughout
this period.  The additional complication here is that at the end of
1968-01-31 there was a leap of -0.1 (TAI) seconds.  The true duration of
this day is therefore exactly 86399.902592 s.  The answer shown above,
of 86400.000000 s, is incorrect in two ways, accounting for neither the
frequency offset nor the leap.

Once again, time-tai->time-utc accurately inverts the incorrect
time-utc->time-tai.  The failure to handle UTC's leaps in this era is
not specific to the relatively unusual negative leaps: it's equally
clueless about the positive leaps.

The full extent of the conversion errors, integrated across the entire
"rubber seconds" era from 1961-01-01 to 1972-01-01, is a little over
8.5 seconds of TAI.

This bug influences bug#26164, regarding time arithmetic in UTC.  If one
were to ignore the rubber seconds era, an obvious way to correct UTC
time arithmetic would be to convert to TAI and the arithmetic there.
That handles UTC leaps correctly.  But with rubber seconds it would
still be wrong.  In the rubber seconds era the number of UTC seconds in
an interval differs from the number of TAI seconds.

-zefram