[comp.bugs.4bsd] ctime

bww@K.GP.CS.CMU.EDU (Bradley White) (01/08/88)

Index:	lib/libc/gen/ctime.c 4.3BSD

Description:
	The time routines in the C library know nothing about leap
	seconds.  This means that the correspondence between the
	number of seconds since the epoch and the actual time of
	day is broken.
Repeat-By:
	Count the actual number of seconds since January 1, 1970 and
	run the answer through ctime(3).  Notice that the answer is
	fourteen seconds in the future.
Fix:
	Apply the following patch and then check the output of the
	test program.
		main()
		{
			long t;

			t = 567993612;  printf("%s", ctime(&t));
			t = 567993613;  printf("%s", ctime(&t));
			t = 567993614;  printf("%s", ctime(&t));
		}

	It should look like this (in EST at least).
		Thu Dec 31 18:59:59 1987
		Thu Dec 31 18:59:60 1987
		Thu Dec 31 19:00:00 1987

RCS file: RCS/ctime.c,v
retrieving revision 1.1
diff -c -r1.1 ctime.c
*** /tmp/,RCSt1011358	Wed Jan  6 00:08:34 1988
--- ctime.c	Wed Jan  6 00:03:48 1988
***************
*** 321,326 ****
--- 321,362 ----
  	return tmp;
  }
  
+ /* THE LEAP SECOND TABLE SHOULD BE LOADED FROM A FILE */
+ 
+ static struct leapsec {
+ 	time_t	ls_when;
+ 	long	ls_corr;
+ } leapsecs[] = {
+ 	78796800,	1,	/* 30 Jun 1972 */
+ 	94694401,	2,	/* 31 Dec 1972 */
+ 	126230402,	3,	/* 31 Dec 1973 */
+ 	157766403,	4,	/* 31 Dec 1974 */
+ 	189302404,	5,	/* 31 Dec 1975 */
+ 	220924805,	6,	/* 31 Dec 1976 */
+ 	252460806,	7,	/* 31 Dec 1977 */
+ 	283996807,	8,	/* 31 Dec 1978 */
+ 	315532808,	9,	/* 31 Dec 1979 */
+ 	362793609,	10,	/* 30 Jun 1981 */
+ 	394329610,	11,	/* 30 Jun 1982 */
+ 	425865611,	12,	/* 30 Jun 1983 */
+ 	489024012,	13,	/* 30 Jun 1985 */
+ 	567993613,	14	/* 31 Dec 1987 */
+ };
+ 
+ #define	NLEAPSECS	(sizeof(leapsecs)/sizeof(leapsecs[0]))
+ 
+ struct leapsec *
+ lssearch(t)
+ 	time_t t;
+ {
+ 	register int i = NLEAPSECS;
+ 
+ 	while (--i >= 0)
+ 		if (t >= leapsecs[i].ls_when)
+ 			return &leapsecs[i];
+ 	return 0;
+ }
+ 
  static int	mon_lengths[2][MONS_PER_YEAR] = {
  	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
***************
*** 341,348 ****
--- 377,392 ----
  	register int		y;
  	register int		yleap;
  	register int *		ip;
+ 	int			ls_flag;
+ 	struct leapsec		*l;
  	static struct tm	tm;
  
+ 	ls_flag = 0;
+ 	if (l = lssearch(*clock)) {
+ 		if (*clock == l->ls_when)
+ 			ls_flag = 1;
+ 		*clock -= l->ls_corr;
+ 	}
  	tmp = &tm;
  	days = *clock / SECS_PER_DAY;
  	rem = *clock % SECS_PER_DAY;
***************
*** 359,364 ****
--- 403,415 ----
  	rem = rem % SECS_PER_HOUR;
  	tmp->tm_min = (int) (rem / SECS_PER_MIN);
  	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
+ 	if (ls_flag)
+ 		/*
+ 		 * How should a leap second be represented?
+ 		 * We choose to add one to tm_sec which will
+ 		 * usually result in "... ??:59:60".
+ 		 */
+ 		 tmp->tm_sec += 1;
  	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
  	if (tmp->tm_wday < 0)
  		tmp->tm_wday += DAYS_PER_WEEK;

-- 
--
Bradley White <bww@cs.cmu.edu>         +1-412-268-3060
CMU Computer Science Department  40 26'33"N 79 56'48"W

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/08/88)

In article <604@PT.CS.CMU.EDU> bww@K.GP.CS.CMU.EDU (Bradley White) writes:
>	The time routines in the C library know nothing about leap
>	seconds.  This means that the correspondence between the
>	number of seconds since the epoch and the actual time of
>	day is broken.

The C library time routines are not PERMITTED to know about leap
seconds, according to the current draft proposed ANSI C standard.
You should set the system clock so that ctime() reports the correct
time of day.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/08/88)

In article <6976@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>The C library time routines are not PERMITTED to know about leap
>seconds, according to the current draft proposed ANSI C standard.

Oops, excuse me -- it is IEEE Std 1003.1 (POSIX) Draft 12 that
says this; ANSI C does not dictate what the system time
representation is.

ado@elsie.UUCP (Arthur David Olson) (01/09/88)

> > The C library time routines are not PERMITTED to know about leap
> > seconds, according to the current draft proposed ANSI C standard.
> Oops, excuse me -- it is IEEE Std 1003.1 (POSIX) Draft 12 that
> says this; ANSI C does not dictate what the system time
> representation is.

There is one conflict between leap seconds and draft proposed ANSI C.
When the leap seconds occur, the time is "supposed" to be counted thus:
		...
		23:59:58
		23:59:59
		23:59:60
		00:00:00
		00:00:01
		00:00:02
		...
which means that one or two times a year (typically) the "tm_sec"
field of a "struct_tm" should have the value 60.  The November '87
draft, though, specifies that tm_sec is confined to the range 0..59.

Of course if you're willing to fudge and simply repeat the
		23:59:59
or the
		00:00:00
there's no problem.  (For those using the mod.sources table-based time
functions, lines such as

	# Rule NAME FROM TO   TYPE IN  ON AT        SAVE     LETTER/S
	Rule   US   1988 only -    Jan 1  00:00:00 -00:00:01 S

do the trick by repeating the 23:59:59; change the "AT" filed to "00:00:01"
to repeat the first second of the new year rather than the last of the old.)
-- 
Bugs is a trademark of Warner Brothers/Volkswagen.
Time is a trademark of Time-Life Incorporated.
-- 
ado@vax2.nlm.nih.gov		ADO, VAX, and NIH are Ampex and DEC trademarks

chongo@amdahl.amdahl.com (Landon Curt Noll) (01/09/88)

In article <6976@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
 >
 >The C library time routines are not PERMITTED to know about leap
 >seconds, according to the current draft proposed ANSI C standard.
 >You should set the system clock so that ctime() reports the correct
 >time of day.

I'm opening myself to flames here, but here I go anyway:

Yes, I was the one who proposed the following definition which is now
in the P1003.1 Draft 12 standard:

    Epoch:
	The Epoch refers to the time at 0 hours, 0 minutes 0 seconds, Coordinated
	Universal Time on January 1, 1970.  The value "seconds since the Epoch"
	refers to the difference in seconds between the referenced time and the
	Epoch, not counting leap seconds.

The function ctime() converts "seconds since the Epoch" into a date/time.
The fix from CMU violates the standard.

The reason why this was put into the standard was to avoid having to
put things like this fix into your code.  (most systems do not do this,
so the standard reflects this)

chongo <> /\oo/\
-- 
[views above shouldn't be viewed as Amdahl views, or as views from Amdahl, or
 as Amdahl views views, or as views by Mr. Amdahl, or as views from his house]

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

>     Epoch:
> 	The Epoch refers to the time at 0 hours, 0 minutes 0 seconds,
>	Coordinated Universal Time on January 1, 1970.  The value "seconds
>	since the Epoch" refers to the difference in seconds between the
>	referenced time and the Epoch, not counting leap seconds.
> 
> The reason why this was put into the standard was to avoid having to
> put things like this fix into your code.  (most systems do not do this,
> so the standard reflects this)

It is worth nothing at this point that most systems also do not provide
POSIX-conformant time, and are unlikely ever to do so; instead of providing
time in "seconds since the Epoch", they provide the number of seconds since 0
hours, 0 minutes, 0 seconds, SWT (Somebody's Watch Time) on January 1, 1970.

It is also worth noting that if a system decided to provide 100%
POSIX-conformant time, it would have to ensure that the clock did not advance
during a leap second.

Think of it this way: a machine's clock provides coordinates along a time axis,
starting with 0 at the Epoch and progressing nicely at the rate of one tick per
second.  A "time", either local or Coordinated Universal, can be thought of as
a label applied to a point on this axis.  For instance, the label
"00:00:01 1/1/70" is applied to the point one second from the Epoch.

The coordinates along this time axis correspond most naturally to "time_t"s.
"time"s, i.e., the labels, correspond most naturally to "struct tm"s.  What
most systems do not do is throw leap seconds into the conversion between
"time_t" and "struct tm", i.e. into "localtime" and "gmtime".

If an implementation wishes to take leap seconds into account, it can either
treat "time_t"s as labels (i.e., hold the system clock back on leap seconds) or
as coordinates (i.e., throw the leap second conversion into "localtime" and
"gmtime", perhaps by doing it the "correct" way and adding a 61st second to
some minutes).  Most implementations do not do *either* one; the current
wording in the standard technically requires implementations to do *something*,
namely stop the clock during leap seconds - it does not permit existing
implementations to conform down to the last dotted "i" and crossed "t".

I would say that what *really* needs to be done is to recognize that few, if
any implementations can possibly conform in the strictest sense, as few of them
e.g. listen to WWV to set their clocks (and, I suspect, few customers are
willing to pay the extra cost of having their system listen to WWV just so that
they can be guaranteed not to have their clocks off even by one second), and to
indicate that "seconds since the Epoch" merely indicates some reasonable effort
to come *close* to seconds since the Epoch.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

admin@wundt.psy.vu.nl (Wundt Administrator) (01/11/88)

In several articles precedding this a " bug " in ctime is discussed.

I really couldn't care much about 14 secs in 18 plus years.
My Watch Time (or SWT, if you prefer) is often off by more than
14 secs anyway. Further, the other U*IX sites we connect don't
aggree with MWT either. We differ by (oh gosh !!) minutes occainsionally.
Fortunately, noone has suffered (tremendously) from this.

Michael Felt

In article <38377@sun.uucp> guy@gorodish.Sun.COM (Guy Harris) writes:
 
>I would say that what *really* needs to be done is to recognize that few, if
>any implementations can possibly conform in the strictest sense, as few of them
>e.g. listen to WWV to set their clocks (and, I suspect, few customers are
>willing to pay the extra cost of having their system listen to WWV just so that
>they can be guaranteed not to have their clocks off even by one second), and to
>indicate that "seconds since the Epoch" merely indicates some reasonable effort
>to come *close* to seconds since the Epoch.

gwyn@brl-smoke.UUCP (01/12/88)

In article <1964@rti.UUCP> trt@rti.UUCP (Thomas Truscott) writes:
>I SUGGEST THAT POSIX
>1. Encourage fixes for the leap-second and other problems of ctime()
>    and related programs.
>2. Document that application of such fixes is recommended.

I agree with Tom's analysis and recommendations.

Note that the "epoch" in practice is really whatever is obtained
by the inverse of ctime() built into the set-date command, whenever
a system administrator looks at the wall clock to set the system
time.  It isn't really 00:00:00, Jan 1, 1970, although it will
usually be close to it (unless the administrator is purposely
establishing a fake date for some reason, which occasionally is
useful).  No amount of standardization will change this fact of
life, so the definitions should make as much sense as possible.

I could accept having the POSIX standard permit implementations to
maintain a "reasonably close" approximation to whatever is defined.
Only sites with extremely good connections to a controlled time
reference have much chance of being within a tick of the
theoretically "correct" time setting anyway.  That would clearly
be demanding too much precision.

The most useful thing for ctime() to report would be something
very close to the wall-clock time at which the corresponding
time() function was invoked.  Whether the number of seconds since
some artifical epoch is exact or not is of no consequence to most
non-astronomical applications.

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

> I really couldn't care much about 14 secs in 18 plus years.

Yes, 14 secs one way or another is no big deal.
So you won't care if someone fixes the leap-second bug in ctime, right?

AS WE ALL KNOW
UNIX (and other systems) internally maintains the seconds that have 
elapsed since <EPOCH>.  If at a given instant the elapsed time is 't',
then one second later the elapsed time will be 't+1'.
This is unaffected by time zones, daylight savings time,
leap days, leap seconds, or any other artifacts of man.
This is the fundamental representation of time in UNIX (and other systems):
other representations are derivatives and should be treated as such.

AND THEREFORE
The ctime() routine, which converts "EPOCH time" such as 
    568924849
into something more understandable by man such as
    Mon Jan 11 13:40:49 1988
must take into account the artifacts such as time zones, etc.
including *leap seconds* or else ctime() will produce the wrong answer!
Similarly for the inverse routines ("getdate", "timec", any standards yet?)
that convert a user-supplied date into EPOCH time.

DO NOT ATTEMPT TO FIX THE BUG BY DOCUMENTING IT!
We might document "ignore leap seconds" but that results in
small but very real errors that someday could cause real harm.
Should we document "ignore the difference between sidereal
and solar timekeeping systems"?
There is no end to this!

I SUGGEST THAT POSIX
1. Encourage fixes for the leap-second and other problems of ctime()
    and related programs.
2. Document that application of such fixes is recommended.
	Tom Truscott

chongo@amdahl.amdahl.com (Landon Curt Noll) (01/13/88)

In article <1964@rti.UUCP> trt@rti.UUCP (Thomas Truscott) writes:
 >Yes, 14 secs one way or another is no big deal.
 >So you won't care if someone fixes the leap-second bug in ctime, right?

And by the same logic, you won't care if we all don't fix it, right?

 >DO NOT ATTEMPT TO FIX THE BUG BY DOCUMENTING IT!
 >We might document "ignore leap seconds" but that results in
 >small but very real errors that someday could cause real harm.

But there exists software that would dislike to see: 23:59:60.  That is an
example of real harm NOW if we require a change or leave open the choice 
to vendors.

If you leave it up to the vendors to fix or not fix time based on leap
seconds, then you could be faced with synced machines on a localarea net
converting into or out of timestamps differently.  The 14 seconds
between synced machines could be a non-trivial difference.  Since the
Earth is unlikely to obtain a boost of rotational energy, this
difference is likely to increase in the future.  :-)

Or consider two machines from two different vendors:

	Vendor DOES			Vendor DOESNOT
	-----------			--------------
	keeps track of leap seconds	does not keep track of leap seconds

If I ask 'DOES' and 'DOESNOT' to give me the string produced by
asctime(gmttime(some_fixed_timestamp)), I could get different values!
If I ask them to convert the same ascii time string (assume it is GMT
based time) into a timestamp, I could get different values!

We have 3 main choices:

	1) leave the timestamp conversion alone  (>99% don't do leap seconds)
	2) force systems to do leap seconds (a change with doubtful value
	      transition problems and retroactive change problems)
	3) allow systems to select their option (problems with synced systems)

I vote for #1.

chongo <> /\oo/\
-- 
[views above shouldn't be viewed as Amdahl views, or as views from Amdahl, or
 as Amdahl views views, or as views by Mr. Amdahl, or as views from his house]

chongo@amdahl.amdahl.com (Landon Curt Noll) (01/13/88)

In article <7040@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
 >The most useful thing for ctime() to report would be something
 >very close to the wall-clock time at which the corresponding
 >time() function was invoked.  Whether the number of seconds since
 >some artifical epoch is exact or not is of no consequence to most
 >non-astronomical applications.

I think some people are mis-understanding the point:

POSIX does not say that a conforming machine MUST have the correct time!
What it does say is that given some specific time, it MUST produce the 
same timestamp.

chongo <> /\oo/\
-- 
[views above shouldn't be viewed as Amdahl views, or as views from Amdahl, or
 as Amdahl views views, or as views by Mr. Amdahl, or as views from his house]

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

> POSIX does not say that a conforming machine MUST have the correct time!

*Portable Operating System Interface for Computer Environments",
P1003.1/DRAFT 12, October 12, 1987, page 24:

	Epoch
	     The Epoch refers to the time at 0 hours, 0 minutes, 0 seconds,
	     Coordinated Universal Time on January 1, 1970.  The value "seconds
	     since the Epoch" refers to the difference in seconds between the
	     referenced time and the Epoch, not counting leap seconds.

Page 81:

	4.5.1 Get System Time
	Function: time()

	...

	4.5.1.2 Description
	The time() function returns the value of time in seconds since the
	Epoch (see Epoch (section)2.3).

Sorry, but POSIX DOES say that a conforming machine MUST have correct time,
or more precisely it must have the correct time minus the number of leap
seconds since 0 hours, 0 minutes, 0 seconds, Coordinated Universal Time on
January 1, 1970.

I.e, if at some time T it is currently 569,022,442 seconds since the point in
time labelled as 0 hours, 0 minutes, 0 seconds, Coordinated Universal Time on
January 1, 1970 (which is roughly what it is as I type this), the difference in
seconds between the referenced time and the Epoch is 569,022,442.  There have
been 14 leap seconds between that time and the Epoch; were some timekeeper not
to count those seconds, it would have counted only 569,022,428 seconds.

Thus, at that time T, a call to "time()" on a POSIX system returns 569,022,428.
Not 569,022,442; not 469,022,368; not 569,022,488.

> What it does say is that given some specific time, it MUST produce the 
> same timestamp.

What does "it MUST produce the same timestamp" mean?  If it means "all POSIX
systems must, if 'time()' is called at some time T, return the same value",
then it certainly says that - it also says what that value must be!
Furthermore, it says that this value must NOT include leap seconds, which means
that if the system e.g. keeps time by bumping a counter every second it must
avoid doing so for leap seconds.

That may not have been what you MEANT to have POSIX say, but it's what you DID
make POSIX say.  If that's not what you meant, you'll have to try again.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

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

> If you leave it up to the vendors to fix or not fix time based on leap
> seconds, then you could be faced with synced machines on a localarea net
> converting into or out of timestamps differently.

Please define what a "timestamp" is!  Is a "timestamp" a value as returned by
"time()", or a value as returned by "time()" and subsequently convered by
"localtime()" or "gmtime()"?  The time stamps that are applied to files by UNIX
systems tend to be the former, as do the fundamental notion of time in such
systems.  I infer from your other statements, though, that you use the latter
notion of "timestamp".

The problem you cite - "struct tm" values with "tm_sec" equal to 60 - has
NOTHING WHATSOEVER to do with the former ("time_t" time stamps), only the
latter.  Therefore, to fix this problem, the definition of "seconds since the
Epoch" need not be changed; the net result will be that a system that keeps
"perfect" time (i.e., the system clock, as returned by "time()", was correctly
set to the number of seconds since the Epoch - *counting* leap seconds, i.e.
ignoring the artifacts of human-developed timekeeping systems that attempt to
correlate time values to the behavior of heavenly bodies - is incremented by
one precisely every second) will generate "struct tm" values that slowly drift
with respect to what various time standards showing time values using the
aforementioned timekeeping systems say.

The only way to fix this drift would either be to 1) change "localtime" to take
leap seconds into account or 2) change the system so that the values returned
by "time" take leap seconds into account.  In short, you CAN'T fix it without
teaching systems about leap seconds if they do not already use clocks that know
about leap seconds.

> The 14 seconds between synced machines could be a non-trivial difference.

Yes, for programs that use "struct tm" values and expect them to match up.  No,
for programs that use "time_t" values and expect them to match up.

Therefore, the wording in the definition of "Epoch" in the front of P1003.1 is
completely wrong, as it requires implementations to keep track of leap seconds
anyway (if you don't believe me, please read my previous article and Tom
Truscott's article and refute the points stated therein).

If you believe that the conversions performed by "localtime" and "gmtime"
should not take leap seconds into account (a position that is subject to debate
- I'll let Tom Truscott take the view opposed to yours on this), please *fix*
the definition of "Epoch" and change the writeups on "localtime" and "gmtime"
instead.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/13/88)

In article <20700@amdahl.amdahl.com> chongo@amdahl.amdahl.com (Landon Curt Noll) writes:
>POSIX does not say that a conforming machine MUST have the correct time!
>What it does say is that given some specific time, it MUST produce the 
>same timestamp.

Quoting IEEE Std 1003.1 Draft 12:

	The Epoch refers to the time at 0 hours, 0 minutes, 0 seconds,
	Coordinated Universal Time on January 1, 1970.  The value "seconds
	since the Epoch" refers to the difference in seconds between the
	referenced time and the Epoch, not counting leap seconds.

	The time() function returns the value of time in seconds since the
	Epoch.

	[There are also specific extensions to the asctime() function that
	aren't relevant to the discussion.]

I believe it pretty clearly specifies what time() must return.  I would
call that "the correct time" (expressed in seconds since the Epoch, with
the wart of having to know how many leap seconds to leave out).  By the
way, It might be nice to specify that this is the intent of time(), without
insisting on exactness, since not only are most systems not synchronized
with standard time references, but also one never knows how long a system
call will actually take to complete.

ANSI X3.159-198x specifies that the localtime(), gmtime(), and ctime()
functions convert the system time (what time() returns) into the usual
fields (year, month, week, day, hour, minute, second, etc.).  No surprises
here.

----------

Mappings:

	genuine uniform time  --->  genuine time since Epoch
		(by specification of when the Epoch is)

	genuine time since Epoch  --->  Draft 12 "time since Epoch"
		(by omitting leap seconds)

	Draft 12 "time since Epoch"  --->  system time
		(equivalence specified by Draft 12)

	system time  --->  broken-down time
		(by gmtime() or localtime())

	broken-down time  --->  string at user interface
		(by asctime())	note: ctime = asctime(localtime)

The two quantities of interest are the "system time" returned by time(),
and the string seen at the user interface, which undoubtedly should
correspond as closely as possible with the time of day on the wall clock.

The above mappings are all invertible (except near leap seconds), so if
a user-interface form of time is specified, as when the system administrator
"sets the time", we can track backwards to determine what "system time" the
operating system maintains.  Chongo apparently wanted to make the fourth
mapping arithmetically simpler, which is why leap secons are not visible
to it.  Unfortunately, when a leap second does occur, the user-interface
time is supposed to exhibit a glitch, which it can only do if someone
deliberately resets the system time to force the glitch.  How much smoother
it would be if the system clock did not need to be reset in order to keep
the user-interface times tracking the official wall clocks.  This could be
accomplished by instead using the following mappings:

	genuine uniform time  --->  genuine time since Epoch
		(by specification of when the Epoch is)

	genuine time since Epoch  --->  system time
		(equivalence recommended instead of Draft 12's)

	system time  --->  broken-down time
		(by gmtime() or localtime())

	broken-down time  --->  string at user interface
		(by asctime())	note: ctime = asctime(localtime)

This may seem like a minor difference, but it has practical effects:
It would leave the kernel clock runnning uniformly even across a leap
second, and with a bit more work in gmtime()/localtime() the leap seconds
can be allowed for and reported properly, as in the original "bug fix"
that prompted this discussion.  An added bonus is that the "seconds since
the Epoch" would mean just that, which is more useful than the Draft 12
meaning, if one really does have a synchronized system clock and cares
about absolute time references (for example, for event logging).

Two things are needed to achieve this:

	Leap seconds need to be reinstated into "seconds since the
	Epoch" in IEEE Std 1003.1.

	tm_sec must be allowed to hold the value 60 for a leap second
	in ANSI X3.159-198x.  (There really are 61 seconds in some
	minutes, just as there really are 29 days in some Februaries.)

If we can get agreement on this, there may still be time to get this
fixed before these standards are frozen.

bww@K.GP.CS.CMU.EDU (Bradley White) (01/13/88)

So, it seems we are all in agreement.  POSIX undoubtedly said the
opposite of what it meant, and system clocks should keep ticking during
leap seconds (just like our hearts).  Now, "time_t" -> "struct tm"
conversion routines already do different things in different timezones,
so outlawing allowances for leap seconds seems indefensible.  The only
requirement should be that any "struct tm" -> "time_t" function make
the same allowances.  The remaining issue, for those who care for leap
seconds, is a good implementation.  Clearly something based upon adotime
(ado says that ado is a trademark of something) is appropriate, although
I don't think a copy of the leap second rules in every timezone file as
ado earlier suggested is best; nor did this address the ??:59:60 issue.
-- 
Bradley White <bww@cs.cmu.edu>         +1-412-268-3060
CMU Computer Science Department  40 26'33"N 79 56'48"W

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

I decided to see what RFC 822 had to say, if anything, on the subject of
representing dates.  It says:

     5.  DATE AND TIME SPECIFICATION

     5.1.  SYNTAX

     date-time   =  [ day "," ] date time        ; dd mm yy
                                                 ;  hh:mm:ss zzz

     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
                 /  "Fri"  / "Sat" /  "Sun"

     date        =  1*2DIGIT month 2DIGIT        ; day month year
                                                 ;  e.g. 20 Jun 82

     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"

     time        =  hour zone                    ; ANSI and Military

     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
                                                 ; 00:00:00 - 23:59:59

The reference to 23:59:59 is in a comment, so it's not clear whether this means
that 23:59:60 in an header would violate this specification or not.  Section
2.8 "; COMMENTS" says

          A semi-colon, set off some distance to  the  right  of  rule
     text,  starts  a comment that continues to the end of line.  This
     is a simple way of including useful notes in  parallel  with  the
     specifications.

which I infer means that it is a note, not a part of the specification.  From
that, I presume that it was assumed that everybody would know what the Right
Thing to do was and that they'd do it, and no formal specification was
necessary.  I don't know if they took the possibility of leap seconds into
account or not.

RFC 822 also refers to the document

     ANSI.  "Representations of Universal Time, Local  Time  Differen-
        tials,  and United States Time Zone References for Information
        Interchange," X3.51-1975.  American National Standards  Insti-
        tute:  New York (1975).

I don't know if this document says anything about leap seconds or not, because
I don't have a copy of it.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

dupuy@westend.columbia.edu (Alexander Dupuy) (01/16/88)

In article <7562@elsie.UUCP> ado@elsie.UUCP (Arthur David Olson) writes:
>Of course if you're willing to fudge and simply repeat the
>		23:59:59
>or the
>		00:00:00
>there's no problem.  (For those using the mod.sources table-based time
>functions, lines such as
>
>	# Rule NAME FROM TO   TYPE IN  ON AT        SAVE     LETTER/S
>	Rule   US   1988 only -    Jan 1  00:00:00 -00:00:01 S
>
>do the trick by repeating the 23:59:59; change the "AT" filed to "00:00:01"
>to repeat the first second of the new year rather than the last of the old.)

Here's a revision of the tzone/etcetera zoneinfo source file which corrects for
leap seconds in UTC (not GMT though - so the GMT rules are consistent with the
standard ctime results).  After all, if you are a real clockwatcher, and care
about things like leap seconds, you have "TZ=UTC" in your .profile, right?

Aside from the problem of ctime not generating 23:59:60, zic also sets the dst
flag in the zoneinfo files for any rule with nonzero SAVE, even though (in this
case) the time is standard.  You can go and clear the flag with a binary editor
if you care, or you could hack zic.  By setting up the rules as decreasingly
positive offsets to GMT, we can make UTC a "standard" time with 0 offset from
GMT for all times since the last leap second, which is good enough for most
purposes.

Note also that you must not have NOSOLAR defined, since this creates 14
different UTC savings times.  You can work around this by deleting the first 5
leap second rules, at the expense of inaccuracy for years < 1975.

@alex
---
arpanet: dupuy@columbia.edu
uucp:	...!rutgers!columbia!dupuy

#!/bin/sh
: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract: 
:
:	etcetera
:
echo x - etcetera
sed 's/^X//' > etcetera << '//go.sysin dd *'
X# @(#)etcetera	3.2
X
X#
X# We leave GMT without adjustments for leap seconds, for simplicity
X#
X
X# Zone	NAME		GMTOFF	RULES	FORMAT	 [UNTIL]
XZone	GMT		0	-	GMT
X
X#
X# Names for zones that might exist, just so people can set a timezone
X# that's right for their area, even if it doesn't have a name or dst rules
X# (half hour zones are too much to bother with -- when someone asks!)
X#
X
XZone	GMT-12		-12	-	GMT-1200
XZone	GMT-11		-11	-	GMT-1100
XZone	GMT-10		-10	-	GMT-1000
XZone	GMT-9		-9	-	GMT-0900
XZone	GMT-8		-8	-	GMT-0800
XZone	GMT-7		-7	-	GMT-0700
XZone	GMT-6		-6	-	GMT-0600
XZone	GMT-5		-5	-	GMT-0500
XZone	GMT-4		-4	-	GMT-0400
XZone	GMT-3		-3	-	GMT-0300
XZone	GMT-2		-2	-	GMT-0200
XZone	GMT-1		-1	-	GMT-0100
XZone	GMT+1		1	-	GMT+0100
XZone	GMT+2		2	-	GMT+0200
XZone	GMT+3		3	-	GMT+0300
XZone	GMT+4		4	-	GMT+0400
XZone	GMT+5		5	-	GMT+0500
XZone	GMT+6		6	-	GMT+0600
XZone	GMT+7		7	-	GMT+0700
XZone	GMT+8		8	-	GMT+0800
XZone	GMT+9		9	-	GMT+0900
XZone	GMT+10		10	-	GMT+1000
XZone	GMT+11		11	-	GMT+1100
XZone	GMT+12		12	-	GMT+1200
XZone	GMT+13		13	-	GMT+1300	# GMT+12 with DST
X
XLink	GMT		Greenwich
X
X#
X# For the dedicated clockwatchers, true UTC with leap second adjustments
X# (not that this will not work if NOSOLAR is defined)
X#
X
X# Rule NAME FROM TO   TYPE IN  ON AT	    SAVE     LETTER/S
XRule   Leap min  only -	   Jan 1  00:00:00  00:00:14 -
XRule   Leap 1972 only -	   Jul 1  00:00:00  00:00:13 -
XRule   Leap 1973 only -	   Jan 1  00:00:00  00:00:12 -
XRule   Leap 1974 only -	   Jan 1  00:00:00  00:00:11 -
XRule   Leap 1975 only -	   Jan 1  00:00:00  00:00:10 -
XRule   Leap 1976 only -	   Jan 1  00:00:00  00:00:19 -
XRule   Leap 1977 only -	   Jan 1  00:00:00  00:00:08 -
XRule   Leap 1978 only -	   Jan 1  00:00:00  00:00:07 -
XRule   Leap 1979 only -	   Jan 1  00:00:00  00:00:06 -
XRule   Leap 1980 only -	   Jan 1  00:00:00  00:00:05 -
XRule   Leap 1981 only -	   Jul 1  00:00:00  00:00:04 -
XRule   Leap 1982 only -	   Jul 1  00:00:00  00:00:03 -
XRule   Leap 1983 only -	   Jul 1  00:00:00  00:00:02 -
XRule   Leap 1985 only -	   Jul 1  00:00:00  00:00:01 -
XRule   Leap 1988 only -	   Jan 1  00:00:00  00:00:00 -
X
X# Zone	NAME		GMTOFF	RULES	FORMAT	 [UNTIL]
XZone	UTC		0	Leap	UTC
X
XLink	UTC		UCT
XLink	UTC		Universal
//go.sysin dd *
exit
---
arpanet: dupuy@columbia.edu
uucp:	...!rutgers!columbia!dupuy