[comp.bugs.4bsd] Leap seconds

mo@uunet.UU.NET (Mike O'Dell) (01/13/88)

I just had a long conversation with Dave Mills about time ticking
and he is now of the opinion that jiggering the "real clock", i.e.,
the gicky the ticks away at some fairly regular rate is wrong.
The correct solution for synchronizing is to maintain an offset
between the "local real clock" and "global absolute time".  Note
that for high precision, this requires maintaining not only the
last known offset, but the 1st and 2nd derivatives so the drift
can be interpolated (to quite astonishing accuracies, it turns out).
In this scenario then, the offset reflects any leaping, not the
basal clock.  The "local apparent time" is formed by adding the
last known offset plus the interpolated drift term to the current
value of the real clock.  (Note that the interpolated value is a
function of the difference between the local real clock at the
last offset synchronization and its current value. This makes the
calculations a bit tricky if you want high precision.)

Therefore, the right place, I would argue, to put leaping is in
ctime().  I prefer to think of ctime() as a mapping between
external absolute time and the system's internal relative representation.
If you take this view, then it is clear that is where it goes.

Besides, I dunno how to set the clock WITHOUT leapseconds! If you
listen to WWV on a cheap plastic short-wave radio to set the time,
you get leap seconds!  So if you  ain't supposed to put them in,
you gotta manually subtract them!

Oh yes, I just realized it - the problem stems from using an epoc-relative
representation.  Absolute representations don't have the problem, or
at least push them so far into the past as to not matter.  For example,
	0400 UTC December 9, 1987 
doesn't suffer the bias - it's built-in!!

	-Mike

dmr@alice.UUCP (01/14/88)

It seems to me that the POSIX wording ("ignoring leap seconds")
is essentially correct, though the specs could be made more precise.

One postulate (not related to time as such) is that it would be rather
impolite to make everyone change their ctime function to get reasonable
conformance.

A second postulate is that the successive numbers returned by time(2) should
increase by 1 each second, and not skip or pause.

A third is that time errors in the distant past are considerably less
important than ones near the present.

Ctime operates by dividing the value of time(2) by 86400 to get days,
converting this to YY/MM/DD, and converting the remainder to HH:MM:SS.

Because of leap seconds, there are not always 86400 sec/day; thus ctime ignores
leap seconds (as specified by POSIX).  But the time "now" is correct.

So where did the leap seconds go?  One way to think of the situation is
that the epoch (and in fact all times before the leap second) moves
whenever we have a leap second.  Practically always, all this means is
that doing ctime() on a old date will be wrong by some number of
seconds.  Most people don't care; those who do, can install a nicer
ctime like the posted one.

It is true that if you execute "while sleep(1); do date; done" across
a leap second, you will not ever see the time "23:59:60" as you should;
and moreover, your ctime will be off by 1 sec until you do something
(reboot, jam the clock, skew your clock by listening to WWV, whatever).
I claim that for the vast majority of people, this witching period
(where an "old date" is in fact recent) is of little consequence; if
it is, they can fix their ctime to know about the leap second.

What would be wrong is to change the meaning of time(2) as
interpreted now, for any value of now.  For example, we run a WWVB
synchronized time server that puts out Unix Standard Time on a
network, and probably some hundreds of machines use it directly or
indirectly.  We would consider it unfriendly to jump the number it puts
out, and force everyone to change their simplistic ctime routines that
work well almost always.

	Dennis Ritchie

trt@rti.UUCP (Thomas Truscott) (01/16/88)

Dozens of UN*X people have known of the leap second bug in ctime
(and in the inverse routines) for well over a decade.
Whoever wrote ctime (was it dmr?!) did a great job,
but it has suffered bit rot as the years have gone by.
It is a shame that people have been too lazy to fix the leap second bug.
Ctime has worse problems that have not been fixed either.
Ignoring these problems will just delay the inevitable,
and documenting them as a standard will just make things worse.

It is NOT too late to fix these problems, nor is it too late
simply to postpone the issue for future consideration.

In article <7622@alice.UUCP>, dmr@alice.UUCP writes:
> One postulate (not related to time as such) is that it would be rather
> impolite to make everyone change their ctime function to get reasonable
> conformance.  ...
> A third is that time errors in the distant past are considerably less
> important than ones near the present.

Fine, let's redefine EPOCH to be 14 seconds before 0000 Jan 1 1970 GMT.
That is a lot cleaner than "ignoring leap seconds."

Of course if ctime (and its inverse routines) persist in their
ignorance of leap seconds (as the new standard demands)
then we will have periodic glitches in time.
Do people want this from here to eternity?

And then there are the ctime problems with daylight savings time.
There are areas in the US today where ctime gives the wrong answer.
Is that reasonable conformance?
And there will be big problems when the US Congress (or a state legislature)
changes the rules about daylight savings time.
I consider it "impolite" of ctime to give me wrong answers.

The number one flaw in ctime is that there is no mechanism
by which it (and its inverse routines) can cope with future,
unpredictable, changes in the time reporting system.
Once this problem is addressed, the problems of daylight savings time,
etc. *and leap seconds* will magically disappear.

May the root have mercy on my impertinence,
	Tom Truscott

guy@gorodish.Sun.COM (Guy Harris) (01/16/88)

> So where did the leap seconds go?  One way to think of the situation is
> that the epoch (and in fact all times before the leap second) moves
> whenever we have a leap second.

Unfortunately, this is inconsistent with the wording in POSIX Draft 12, wherein
it states that "The Epoch refers to the time at 0 hours, 0 minutes, 0 seconds,
Coordinated Universal Time on January 1, 1970."  There is only one such
instant; it can't move.

If the POSIX spec *meant* to say that, when converting "time_t" to "struct tm",
the conversion code should act *as if* leap seconds weren't counted in the
result of "time()" (even though they are, unless you stop your clock during
leap seconds), it should say so - which means this should be described in
Chapter 8, the section where additional information about C language library
routines is presented.

I still think that all efforts to work hard at interpreting the POSIX document
in an unusual manner so as to permit existing systems to conform are doomed to
failure; the spec is rather unambiguous, in that 1) it refers to a particular
instant of time that cannot move, and 2) it specifically says that leap seconds
are not counted.  The only possible interpretation of this that I can see is
that, if a total of 569,022,442 seconds have elapsed since the Epoch (which was
true at one particular instant a few days ago), as defined by some standard
outside of POSIX (e.g., a counter driven by a cesium-beam clock and set to zero
at that instant by the BIH or whoever standardizes these things), "time()" must
return 569,022,428, as it is not supposed to count leap seconds.

Ultimately, the leap seconds problem can be finessed if the POSIX spec is also
fixed so as not to mandate that systems *do* sync up with e.g. WWV; the
wording in the POSIX spec would render my system non-conforming, since 1) my
machine's clock drifts and 2) I don't call WWV every morning to set the time,
nor do I have it attached to a WWV clock.  Were the standard to permit a system
to make a reasonable effort to sync up with UTC, but allow it to be off by a
couple of seconds or so (or perhaps even a couple of minutes), a system that
doesn't take leap seconds into account would have a "ctime" that gave times
that were off by a couple of seconds, but that wouldn't cause it not to
conform.  If the user cares about this, they can buy a system that has a
"ctime" that undestands leap seconds (and also sync it up with a WWV radio or
somesuch); if they don't care about this, they're not obliged to buy such a
system.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

rbutterworth@watmath.waterloo.edu (Ray Butterworth) (01/18/88)

In article <1980@rti.UUCP>, trt@rti.UUCP (Thomas Truscott) writes:
> And then there are the ctime problems with daylight savings time.
> There are areas in the US today where ctime gives the wrong answer.
> Is that reasonable conformance?
> And there will be big problems when the US Congress (or a state legislature)
> changes the rules about daylight savings time.
> I consider it "impolite" of ctime to give me wrong answers.
> 
> The number one flaw in ctime is that there is no mechanism
> by which it (and its inverse routines) can cope with future,
> unpredictable, changes in the time reporting system.
> Once this problem is addressed, the problems of daylight savings time,
> etc. *and leap seconds* will magically disappear.

One of the main problems is that time_t tries to be too many things
for too many people and never quite manages to do everything that is
expected of it.

time_t works fairly well as a time stamp for file changes and such
system related things.  It is when it comes to a human interface
that its deficiencies become more apparent.

A library (portable, not-necessarily-unix, etc.) that I've been working
on provides a complementary solution (definitely not a replacement).

The idea is that when a human enters a time he is refering to the time
on his watch or on the wall clock.  If he says "2 days from today", or
"6 months from now", he would expect the date to change but not the
clock.  It shouldn't matter that 2 days from now is actually 1 second
earlier because of a leap-second, or that 6 months from now is actually
1 hour earlier because of daylight saving time, or 4 hours later because
he's moved to a different time zone.
Similarly, asking for the difference in time between "10:15 Aug 17" and
"10:15 Jan 12" should give an answer that is an exact number of days.

i.e. time is kept track of ignoring time-zones, leap-seconds, DST,
or any other complications that might arise in the future (short of
a major change to the calendar).

I haven't yet needed it, but it wouldn't be difficult to add another
function that could give the real difference in time between two
of these times taking into account leap-seconds, DST, time-zones,
and currently known acts of government.

I realize that this idea is hardly original, and is in fact a giant
step backwards.  But for many applications it works much better than
the more precise (and therefore more often incorrect) values given by
the size_t functionality.

guy@gorodish.Sun.COM (Guy Harris) (01/19/88)

> time_t works fairly well as a time stamp for file changes and such
> system related things.  It is when it comes to a human interface
> that its deficiencies become more apparent.

It was never intended to be used as something for humans to deal with directly.
That's what "struct tm"s are for.

If you want to know the number of seconds that will elapse between two events
(*counting* leap seconds, of course; we all get one second older during leap
seconds just as we do during regular seconds), you should be able to get the
time stamps of those two events and subtract them.  (This is one reason why the
times returned by "time()" should NOT exclude leap seconds.)

If you want to know how much time separates the two events, with the proviso
that e.g. 11:30 tomorrow is to be considered precisely one day after 11:30
today, daylight savings time, leap seconds, etc.  nonwithstanding, you get the
two events dates/times as "struct tm"s and subtract them component by
component.  Similarly, if you want the date/time for "two days from now", you
get "now" as a "struct tm", add two to the current day-of-week value and do the
appropriate "carry propagation" (carefully, taking into account time zone
changes, etc.)

Unfortunately, while UNIX systems have - and ANSI C implementations, barring
surprising changes in the standard, will have - a routine to convert from
"time_t" values to "struct tm" values, most UNIX systems have no routine to go
the other way.  The current ANSI C draft does include a routine that goes from
a "struct tm" to a "time_t"; the spec says that routine has to do the
aforementioned carry propagation.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com