[comp.std.c] The type of time_t

karl@haddock.ima.isc.com (Karl Heuer) (10/21/88)

In article <6964@cdis-1.uucp> tanner@cdis-1.uucp (Dr. T. Andrews) writes:
>If any of the aforementioned evil lasts until 2038, we've got fun.
>That's when our present time_t should become negative.

There's nothing to prevent time_t from being typedef'd to unsigned long int,
which would double the range.  (Also, it would not surprise me if by 2038
there were no serious machines with only 32 bits in a long int.)

Of course, if you want to implement time_t with more precision than one tick
per second, then doomsday arrives much sooner.

>A more clever interface might arrange a slightly different way to indicate an
>error:
>	int mktime(struct tm *tmptr, time_t *t)
>returns 0 if it worked (*t filled in), or -1 if it fails.

That would have worked for mktime(), which was apparently an invention of the
Committee, but the time() function also needs to be able to indicate failure
(if the system has no clock).  Unfortunately time() as commonly implemented
returns a time_t as its value in addition to (optionally) storing it via the
argument, so the obvious solution (int time(time_t *), result 0 or -1) would
break existing code.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
Followups to comp.std.c.

decot@hpisod2.HP.COM (Dave Decot) (10/22/88)

> There's nothing to prevent time_t from being typedef'd to unsigned long int,
> which would double the range.  (Also, it would not surprise me if by 2038
> there were no serious machines with only 32 bits in a long int.)

In fact, the last time I checked, time_t was erquired to be an "arithmetic"
type, not necessarily an integral type.  So, you could use double to
implement it, correct?

Dave

henry@utzoo.uucp (Henry Spencer) (10/23/88)

In article <9816@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>There's nothing to prevent time_t from being typedef'd to unsigned long int,
>which would double the range...

Unfortunately, it would probably break a significant number of programs that
think time_t is signed.  Those programs are arguably broken already, but some
degree of pragmatism is necessary in such matters.
-- 
The meek can have the Earth;    |    Henry Spencer at U of Toronto Zoology
the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu

flaps@dgp.toronto.edu (Alan J Rosenthal) (10/23/88)

karl@haddock.ima.isc.com (Karl Heuer) writes:
>>There's nothing to prevent time_t from being typedef'd to unsigned long int,
>>which would double the range...

henry@utzoo.uucp (Henry Spencer) writes:
>Unfortunately, it would probably break a significant number of programs that
>think time_t is signed.  Those programs are arguably broken already, but some
>degree of pragmatism is necessary in such matters.

Is it possible to assume that time_t is signed without assuming it's either
int or long (or char)?  I can't see how, and if it's not then Karl's change
above wouldn't break any more programs than changing time_t between int and
long would.

(It is true that a cast to long sort of assumes that the value cast is signed,
but in the case of time_t I don't see what you want to do with that value
except cast it back to time_t, which makes the cast to long rather pointless.
Or if you want to printf the number, well ansi printf's %lu format takes a
long, not an unsigned long, anyway, so the cast to long is the correct way to
print unsigned longs as well as longs.)

ajr

--
#define __STDC__ 0

ron@ron.rutgers.edu (Ron Natalie) (10/24/88)

Frankly, life is going to be screwed up before the time bits are fixed because
many programs that compute dates think that 2000 is not a leap year.  We can
always follow the TOPS-10 convention of moving the Epoch.

-Ron

karl@haddock.ima.isc.com (Karl Heuer) (10/25/88)

In article <Oct.24.11.33.01.1988.12179@ron.rutgers.edu> ron@ron.rutgers.edu (Ron Natalie) writes:
>Frankly, life is going to be screwed up before the time bits are fixed because
>many programs that compute dates think that 2000 is not a leap year.

I doubt that there are many such programs, since the naive algorithm (y%4==0)
happens to agree with the correct one until 2100 (the century and quadri-
century corrections cancel each other out in 2000).  The problem that's likely
to occur in 2000 has to do with two-digit notation for the year; now *that's*
going to screw up a lot of existing code.  (The `date' command doesn't even
*have* a notation to set the date beyond 1999!)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

mcgrath@tully.Berkeley.EDU.berkeley.edu (Roland McGrath) (10/27/88)

["The type of time_t (was: struct tm -> time_t converter wanted)"] - henry@utzoo.uucp (Henry Spencer):
) In article <9816@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
) >There's nothing to prevent time_t from being typedef'd to unsigned long int,
) >which would double the range...
) 
) Unfortunately, it would probably break a significant number of programs that
) think time_t is signed.  Those programs are arguably broken already, but some
) degree of pragmatism is necessary in such matters.

With the Epoch defined as it is for Unix (Jan 1 00:00:00, 1970), it is
desireable to have a signed `time_t' type.  Although I didn't, the world
did exist before 1970.
	Roland McGrath

roland@wheaties.ai.mit.edu, mit-eddie!wheaties.ai.mit.edu!roland

guy@auspex.UUCP (Guy Harris) (10/28/88)

>With the Epoch defined as it is for Unix (Jan 1 00:00:00, 1970), it is
>desireable to have a signed `time_t' type.  Although I didn't, the world
>did exist before 1970.

The world also existed before whatever 1902 or so; having "time_t" be
signed will help only to a limited degree.  If you want a type to be
used for time-stamping arbitrary events in the past and the future,
"time_t" on UNIX systems isn't going to be it no matter whether it's
signed or unsigned, unless you make it bigger than 32 bits (in which
case signedness will probably be irrelevant).  However, "time_t" is
generally used to time-stamp events occurring on a UNIX machine, and
there were relatively few of them around before 1970....

friedl@vsi.COM (Stephen J. Friedl) (10/28/88)

Somebody writes:
>With the Epoch defined as it is for Unix (Jan 1 00:00:00, 1970), it is
>desireable to have a signed `time_t' type.  Although I didn't, the world
>did exist before 1970.

In article <334@auspex.UUCP>, guy@auspex.UUCP (Guy Harris) writes:
> 
> The world also existed before whatever 1902 or so; having "time_t" be


So, Guy, you were born in 1902?  :-) :-) :-)
-- 
Steve Friedl    V-Systems, Inc.  +1 714 545 6442    3B2-kind-of-guy
friedl@vsi.com     {backbones}!vsi.com!friedl    attmail!vsi!friedl
----Nancy Reagan on 120MB SCSI cartridge tape: "Just say *now*"----

flaps@dgp.toronto.edu (Alan J Rosenthal) (10/28/88)

roland@wheaties.ai.mit.edu (Roland McGrath) writes:
>With the Epoch defined as it is for Unix (Jan 1 00:00:00, 1970), it is
>desirable to have a signed `time_t' type.  Although I didn't, the world
>did exist before 1970.

First of all, negative time_t values don't mean time before time zero.  This is
confirmed by the return value (time_t)(-1) from mktime() which means "not a
time I can represent", rather than 31 Dec 1969 23:59:59 GMT.

Most of all, time_t is for representing events that happen while the system is
running:  the current time, the scheduled time of events in the future
(e.g. alarms, sleeps), and the time of events in the past (e.g. file
timestamps).  It is not sufficient, nor should it be, for representing the
times of all events in human history and future; a more sophisticated, and
larger, representation is required for that.  So any zero-date in the past
is ok, because a file need not be able to have a timestamp predating the
writing of the operating system it is available on.

ajr

--
#define __STDC__ 0

pozar@hoptoad.uucp (Tim Pozar) (11/01/88)

    Tom sent me this the other day...  Might be helpful for
    the algorythms...
	    Tim
---
/*
	Tom Jennings
	Fido Software
	27 Oct 88

These two functions convert MSDOS stored time and date (16 bit
integer each for time and date) to the 32 bit "number of seconds 
since 1970" used by Zmodem.

long dos2sec(dostime,dosdate)
unsigned dostime;		/* MSDOS time */
unsigned dosdate;		/* MSDOS date */

Converts the MSDOS time and date (as stored in the structure from an
MSDOS search first/next call or a get-file-time call) to the long
integer to put into a Zmodem ZFILE packet. It fails (overflows) somewhere
around the turn of the century (32 bits isn't long enough!)


long sec2dos(sec70)
long sec70;

Converts the "seconds since 1970" from Zmodem to a long imteger that
contains the MSDOS time and date within it; the high 16 bits are MSDOS
date, and the low 16 bits are MSDOS time, each as used by MSDOS set-file-
time functions. (They are stored packed as that's how my internal routines
handle them.)


long stonum(s,base)
char *s;
unsigned base;

A simple "atoi()" like function that converts a string of ASCII digits
into a long integer; it stops when it finds a non-digit. The conversion 
is done modulo (base), ie. 10 for decimal numbers, 8 for octal numbers.
(Zmodem file size is stored decimal; time as octal. Both are long.)

*/



/* Convert MSDOS packed time to long seconds since 1970. (Basic conversion
derived from code supplied by Stuart Gathman.) */

long
dos2sec(dostime,dosdate)
unsigned dostime,dosdate;
{
int y,dayofyear;
long time;

#define Uyear ((dosdate >> 9) & 0x3f)		/* as stored in search */
#define Umonth ((dosdate >> 5) & 0x0f)		/* first/next structure */
#define Uday (dosdate & 0x1f)			/* or get file time DOS call */
#define Uhour ((dostime >> 11) & 0x1f)
#define Uminute ((dostime >> 5) & 0x1f)
#define Usecond ((dostime & 0x1f) << 1)		/* (kept as 2 second resolution) */

	y= Uyear + 80; 				/* DOS date base is 1980 */
	dayofyear= Uday + ((Umonth * 275) / 9) - 30;/* day of year, */
	if (Umonth > 2) {			/* handle leapyears */
		--dayofyear;			/* Feb 28/29, 1 Mar */
		if (Uyear % 4) --dayofyear;
	}
	time= dayofyear + ((long)(y - 1) * 1461L) / 4; /* day of century */

	time -= 25203L;				/* minus days 1900 - 1970 */

	time *= 86400L;				/* second of century */
	time += (long)Uhour * 3600L;		/* add seconds this day, */
	time += Uminute * 60;
	time += Usecond;
/*	cprintf("dos2sec: %02d/%02d/%02d %02d:%02d:%02d=%lu\r\n",y,Umonth,Uday,Uhour,Uminute,Usecond,time);
*/	return(time);
}

/* Convert seconds since 1970 to MSDOS packed time and date. The DOS date is
in the upper two bytes, time in the lower. (Same as passed in a TELINK 
block.) (Basic conversion derived from code supplied by Stuart Gathman.) */

long
sec2dos(sec70)
long sec70;
{
long time;
int dayofyear,leapyear;
int year,month,day;

	time= sec70 % 86400L;			/* time only */
	sec70 /= 86400L;			/* date only */
	sec70 += 25203L;			/* add days 1900 - 1970 */
	leapyear= 2;

	year= ((sec70 - (sec70 / 1461) + 364) / 365);	/* make year, */
	dayofyear= sec70 - ((long)(year - 1) * 1461) / 4;/* day in current year */
	if (year % 4 == 0) leapyear= 1;
	if (dayofyear > 59 && ((dayofyear > 60) || (leapyear == 2)))
		dayofyear += leapyear;
	month= (269 + (dayofyear * 9)) / 275;
	day= dayofyear + 30 - ((275 * month) / 9);

/*	cprintf("\r\nsec2dos: %lu == %02d/%02d/%02d %02d:%02d:%02d\r\n",x,year,month,day,time / 3600L,(time % 3600L) / 60L,(time % 3600L) % 60L);
*/
	sec70= 0L;
	sec70 |= ((year - 80) & 0x3f) << 9;	/* pack the date (starts 1980) */
	sec70 |= (month & 0x0f) << 5;		/* into lower 16 bits */
	sec70 |= day & 0x1f;
	sec70 <<= 16L;				/* shift to upper 16 bits */

	sec70 |= ((time / 3600L) & 0x1f) << 11;	/* then pack the time */
	time %= 3600L;				/* hours, */
	sec70 |= ((time / 60L) & 0x3f) << 5;	/* minutes, */
	sec70 |= ((time % 60L) & 0x1f) >> 1;	/* seconds. */
	return(sec70);
}

/* Convert a string of ASCII digits to a long number, in the 
specified base. Works only for bases 1 - 10 (mainly: dec. and octal) */

long
stonum(s,base)
char *s;
int base;
{
int i;
long n;

	n= 0L;
	while ((*s >= '0') && (*s <= '9')) 
		n= (base * n) + (*s++ - '0');
	return(n);
}
-- 
 ...sun!hoptoad!\                                     Tim Pozar
                 >fidogate!pozar               Fido:  1:125/406
  ...lll-winken!/                            PaBell:  (415) 788-3904
       USNail:  KKSF / 77 Maiden Lane /  San Francisco CA 94108

guy@auspex.UUCP (Guy Harris) (11/02/88)

>    Tom sent me this the other day...  Might be helpful for
>    the algorythms...

At least for OSes that store local time rather than GMT.  OSes that
store GMT, such as UNIX, need more than that.

scs@athena.mit.edu (Steve Summit) (11/12/88)

In article <8810281846.AA20611@champlain.dgp.toronto.edu> flaps@dgp.toronto.edu (Alan J Rosenthal) writes:
>So any zero-date in the past
>is ok, because a file need not be able to have a timestamp predating the
>writing of the operating system it is available on.

This is, I'll admit, a nit, but I'd suggest otherwise.  I pay a
lot of attention to file modification times, and I often transfer
files between different operating systems (Unix, VMS, MS-DOS)
while attempting to preserve modification times (using tar and
the like).  This means that I have a problem if I take a file
last modified in 1979 to an MS-DOS system, because DOS's epoch is
1980.  (The problem is theoretically even worse when moving files
from VMS, which has an epoch of 1865 or so.)  Not much of a
problem in practice, but it's something to think about.  (And, of
course, it's insoluble.)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

lum@bat.cis.ohio-state.edu (Lum Johnson) (11/15/88)

In article <7917@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>In article <8810281846.AA20611@champlain.dgp.toronto.edu> flaps@dgp.toronto.edu (Alan J Rosenthal) writes:
>>
>>So any zero-date in the past is ok, because a file need not be able to have
>>a timestamp predating the writing of the operating system it is available on.
>
>... I'd suggest otherwise.  I pay a lot of attention to file modification
>times, and I often transfer files between different operating systems (Unix,
>VMS, MS-DOS) while attempting to preserve modification times (using tar and
>the like).  This means that I have a problem if I take a file last modified
>in 1979 to an MS-DOS system, because DOS's epoch is 1980.  (The problem is
>theoretically even worse when moving files from VMS, which has an epoch of
>1865 or so.)  Not much of a problem in practice, but it's something to think
>about.  (And, of course, it's insoluble.)

Good point - this might be important or useful information.  The epoch you're
referring to is probably 0000 GMT 17-Nov-1858, the same as for pdp-10 monitors
with which I am familiar.  The date was chosen by someone at The Smithsonian
Institution if I recall correctly;  I no longer remember the significance, but
it was probably when the Gregorian calendar was adopted by some official group
or major government.

Our local EXEC has been modified to change the write date when a file is copied
so as to avoid the file migration policy.  This annoys me so much that I have
another version of the EXEC without this modification for my use.  (In fact, I
have several EXECs with and without various nasty "features".)
-=-
-- 
Lum Johnson    lum@osu-20.ircc.ohio-state.edu    lum@tut.cis.ohio-state.edu
"You got it kid -- the large print giveth and the small print taketh away."
-------