From: julian@uhunix.uhcc.Hawaii.Edu (Julian Cowley)
Subject: Summary: How write the (ANSI C) library time functions
Followup-To: comp.unix.programmer
Organization: University of Hawaii at Manoa
Date: Fri, 17 Apr 1992 07:29:38 GMT

[Followups to comp.unix.programmer this time.]

Two weeks ago I posted a query to the net asking how specific one
needs to be to write the ANSI C functions gmtime(), localtime(),
and mktime(), specifically in regards to leap seconds, and what
kind of time time() returns (UTC, GMT, etc.).  As expected, I got
myself into a can of worms, but I think I can express the main
points.

Basically, ANSI C ignores the question about leap seconds, but it
does make provision for them nonetheless (by saying that the range
of tm_sec is 0--61) (*).  With regards to time(), all ANSI says is
that the return value should be an arithmetic type which represents
the current time.  That's it.  No mention of how it is represented.

Well, that's fine, but I was really looking for a practical answer
for use on Unix.  POSIX addresses the questions more closely.  With
regards to leap seconds, it says to just forget about 'em, in order
to be consistent.  Their definition of consistency leaves a lot to be
desired, however.  See below.  For time(), POSIX says that its return
value increases monotonically with each second, and is based on the
number of seconds since an Epoch (their capitalization) of 01 Jan
1970 00:00:00 UTC.  Note that it doesn't specify whether time() is
based on atomic time, mean solar time, or whatever.  It also
specifies that the conversion of a return value from time() to a date
and time should be the same on all POSIX systems.

So they decided to ignore leap seconds.  Why did they do this?  I
think for two reasons:  (1) To avoid bugs, since it simplifies
things considerably.  gmtime() and localtime() with leap second
handling are not very difficult to write.  The big problem is
mktime(), which can be very difficult to write correctly if it is
to be the exact inverse of localtime() for every value of time_t
(**).  It also requires the system administrator to keep track of
how many leap seconds there have been and to add new ones when they
are announced.  (2) To avoid making a binding decision on something
which fundamentally exists for theoretical physics and cosmology
rather than everyday people (how many people even know leap seconds
exist?).  Also, leap seconds are a fairly new technique (they
started in 1972), and since physicists seem to change their mind
about time every few years, the committees wanted to avoid becoming
antiquated by the possibility that leap seconds would be superseded
by a newer technique.

HOWEVER, and this is a big "however", if time() is based on an
atomic clock such as WWVH (***), then time() will be based on UTC. 
UTC uses leap seconds, and, aye, there is the Contradiction (my
capitalization).  POSIX wants for there to be a formula for any
struct tm, a formula that doesn't use leap seconds.  Yet it also
wants that number to be the number of seconds since the Epoch. 
Well, the answer depends on what you mean by seconds.  Since POSIX
says that the Epoch is fixed at a specific time, essentially this
means that a POSIX second is not an atomic second, since it gets
longer and longer as more leap seconds are added to UTC. 
Therefore, time() is *not* based on UTC after all.  This, combined
with the adjtime() system call, makes for a very loose definition
of what a second is on a POSIX system.  Many people on the net seem
to take exception to this, and I don't blame them.  Programmers and
operating systems are big boys now, and POSIX seems to be treating
them as adolescents.

Well, that's my summary, and I'm sure that it's full of holes. 
Feel free to sink it.  I want to thank all the people on the net
who answered my query, especially Arthur David Olson (author of the
free time routines available on uunet) and Guy Harris (who was
tireless in his zeal to give me a good answer).

Footnotes (ugh):

(*) The fact that ANSI C allows for leap seconds does *not*, in my
mind, mean that they require them.  They're just making provisions
for the people who do want to use them, whereas people who don't
can just use tm_sec modulo 60.

(**) That is, mktime(localtime(&t)) == t for all t (excluding any
errors from localtime).

(***) Based on, not necessarily attached to -- it doesn't matter
whether how far off it is, really, as long as it is reset every
once in a while to coincide with what UTC says the time is.  Even
your wristwatch is based on atomic time if you adjust it by calling
time (assuming your phone company's clock is based on UTC).

---
Julian Cowley, University of Hawaii
julian@uhunix.uhcc.hawaii.edu
Dept. of Perennial Students

"You know, daylight savings time is a funny thing.  It's just a
game we play.  We say, `At two o'clock, it will be three o'clock.'
Why not, just for the fun of it, on Monday morning say it's Friday
night?" -- paraphrased from a comedian on the Comedy Channel
Newsgroups: comp.unix.programmer