[comp.lang.c] struct tm -> time_t converter wanted

day@grand.UUCP (Dave Yost) (10/18/88)

I was surprised to find no routine in the C library
to convert a struct tm into a time_t.  Is there
such a routine floating around somewhere?

 --dave yost

scs@athena.mit.edu (Steve Summit) (10/18/88)

In article <442@grand.UUCP> day@grand.UUCP (Dave Yost) writes:
>I was surprised to find no routine in the C library
>to convert a struct tm into a time_t.  Is there
>such a routine floating around somewhere?

Here's mine.  You may have to uncomment the dysize() routine at
the end (but not on BSD systems, which already have this function in
ctime.o in libc.a).  Also -DBSD4_2, if necessary, to find
<time.h> correctly.  (Ditto previous requests in this newsgroup
for more consistency in #include file naming, or, since it's too
late for that, some good way of dealing with the resulting
confusion.)

Since either tm_yday or the pair tm_mon,tm_mday uniquely
determine the day, I think you're supposed to set the ones you don't
know to -1.

There may be a better way to figure out DST correction than
calling localtime() again, but I don't know it.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

--------------------------unlocaltime.c--------------------------
/*
 *  Convert a struct tm (actually, a pointer thereto) back to a
 *  time_t, essentially performing the inverse of localtime().
 *
 *  Written by Steve Summit in October, 1985.
 *  Please keep this comment if you keep this code.
 */

#include <sys/types.h>
#ifndef BSD4_2
#include <time.h>
#else
#include <sys/time.h>
#endif
#include <sys/timeb.h>

#define TRUE 1
#define FALSE 0

struct tm *localtime();

static int dmsize[12] =
	{
	31,
	28,
	31,
	30,
	31,
	30,
	31,
	31,
	30,
	31,
	30,
	31
	};

time_t
unlocaltime(tm)
struct tm *tm;
{
time_t timbuf;
struct timeb info;
int year;
int i;
int fillin;

struct tm *tm2;

ftime(&info);

timbuf = 0;

year = tm->tm_year + 1900;

for(i = 1970; i < year; i++)
	timbuf += dysize(i);

if(tm->tm_yday == -1)
	{
	tm->tm_yday = 0;

	for(i = 0; i < tm->tm_mon; i++)
		tm->tm_yday += dmsize[i];

	/* Leap year */

	if (dysize(year) == 366 && tm->tm_mon >= 2)
		tm->tm_yday++;

	tm->tm_yday += tm->tm_mday - 1;		/* mday is 1 based */

	fillin = FALSE;
	}
else	fillin = TRUE;

timbuf = (((timbuf + tm->tm_yday) * 24 + tm->tm_hour) * 60 +
				tm->tm_min + info.timezone) * 60 + tm->tm_sec;

timbuf -= 60 * 60;

tm2 = localtime(&timbuf);

if(!tm2->tm_isdst)
	timbuf += 60 * 60;

tm->tm_wday = tm2->tm_wday;

if(fillin)
	{
	tm->tm_mon = tm2->tm_mon;
	tm->tm_mday = tm2->tm_mday;
	}

return(timbuf);
}

/*
dysize(y)
int y;
{
if((y % 4) == 0)
	return(366);
return(365);
}
*/

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/18/88)

In article <442@grand.UUCP> day@grand.UUCP (Dave Yost) writes:
>I was surprised to find no routine in the C library
>to convert a struct tm into a time_t.  Is there
>such a routine floating around somewhere?

Hi, Dave!

The proposed ANSI C Standard specifies
	time_t mktime(struct tm *timeptr);
for this purpose.  This is essentially an invention, as X3J11
also was unable to identify such a routine in wide use.  It
will not be widely available just yet, but if someone is going
to write one, they should follow this form.  The most important
thing to know that isn't evident is that the struct tm need not
be "normalized"; mktime does the appropriate range reduction.
If the time cannot be represented, (time_t)-1 is returned.

tanner@cdis-1.uucp (Dr. T. Andrews) (10/19/88)

In article <8700@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
) 	time_t mktime(struct tm *timeptr);
) If the time cannot be represented, (time_t)-1 is returned.

On many machines, time_t seems to be a long integer number of seconds
since the beginning of 1970.  Which this (at the moment) seems handy,
the evil that programmers write lives long after them.

If any of the aforementioned evil lasts until 2038, we've got fun.
That's when our present time_t should become negative.  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.
-- 
...!bikini.cis.ufl.edu!ki4pv!cdis-1!tanner  ...!bpa!cdin-1!cdis-1!tanner
or...  {allegra killer gatech!uflorida decvax!ucf-cs}!ki4pv!cdis-1!tanner

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

> I was surprised to find no routine in the C library
> to convert a struct tm into a time_t.  Is there
> such a routine floating around somewhere?

Probably, but if you're going to do one of these yourself, please do it
in the form described by ANSI C, so you can use that when you get an
ANSI compiler and libraries:

    #include <time.h>

    time_t mktime(timeptr)
    struct tm *timeptr;

    The mktime function converts the broken-down time, expressed as
    local time, in the structure pointed to by timeptr into a calendar
    time value with the same encoding as that of the values returned by
    the [time()] function.  The original values of the tm_wday and
    tm_yday components of the structure are ignored, and the original
    values of the other components are not restricted to the [normal
    ranges of those members].  On successful completion, the values of
    the tm_wday and tm_yday components of the structure are set
    appropriately, and the other components are set to represent the
    specified calendar time, but with their values forced [into the
    normal ranges]; the final value of tm_mday is not set until tm_mon and
    tm_year are determined.

    If the calendar time cannot be represented, the function returns the
    value (time_t)-1.

Dave Decot
decot@hpda

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/23/88)

In article <6964@cdis-1.uucp> tanner@cdis-1.uucp (Dr. T. Andrews) writes:
>On many machines, time_t seems to be a long integer number of seconds
>since the beginning of 1970.  Which this (at the moment) seems handy,
>the evil that programmers write lives long after them.
>If any of the aforementioned evil lasts until 2038, we've got fun.

The epoch could be moved.  time_t is for internal program use, not
necessarily external archive time-stamping.

Some of us believe that well before 2038 the 64-bit integer will be in
wide use.

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (10/26/88)

In article <8716@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:

| Some of us believe that well before 2038 the 64-bit integer will be in
| wide use.

In fifty years 64 bit ints will be standard in digital watches. However,
changing the epoch or going to 64 bits would break existing programs. I
don't expect that my programs or I will live to see it, so I guess I'll
postpone worrying about it.

Not in any way to say that the problem doesn't exist, it's just not what
I consider *pressing*.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/26/88)

In article <12418@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>changing the epoch or going to 64 bits would break existing programs.

Neither will break existing correct programs that don't assume things
not promised by the specifications.

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

In article <12418@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>changing the epoch or going to 64 bits would break existing programs.

How could going to 64 bits break an existing program?  Only if it _depended_ on
2147483647 + 1 being -2147483648, as far as I can tell.  This is, of course,
assuming that longs have become 64 bits; time_t has to be typedefed as
_something_.

Or, of course, if it did stuff like "int *p = (int *)malloc(4)" instead of
using sizeof(int)...

But, show us a program which isn't broken already which will break when longs
become 64 bits.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/30/88)

In article <13422@cisunx.UUCP> cmf@unix.cis.pittsburgh.edu (Carl M. Fongheiser) writes:
>In article <8754@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>In article <12418@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>>>changing the epoch or going to 64 bits would break existing programs.
>>Neither will break existing correct programs that don't assume things
>>not promised by the specifications.
>Excuse me?  The last time I checked, the man page for time(3) (time(2)
>in the V7 man pages) clearly stated that the return value was the number
>of seconds since 00:00:00 GMT, January 1 1970.  Not having an SVID handy
>(or a POSIX spec, for that matter), I can't say anything authoritative
>about them.  But if I write a program that knows about that epoch, I'm
>violating the rules?

For C (without additional requirements imposed by SVID, POSIX, etc.),
time_t is just an arithmetic type with the time somehow encoded in it.
It is not guaranteed to use the same encoding from one program
execution to the next, so it permits moving the Epoch when necessary.
There is a macro CLK_TCK for converting between seconds of duration
and the difference of two values returned by clock(), but the origin
is not specified (it may, for example, be the beginning of program
execution).

Since this discussion is occurring in the C newsgroup, not the UNIX one,
you are definitely outside the scope of the specs for the meaning of
time_t if you assume that it means "seconds since the beginning of 1970
AD at Greenwich".  IEEE 1003.1-1988 (POSIX) imposes that additional
requirement, but they also don't require that time conversion from
time_t to/from struct tm be done accurately.  In fact POSIX requires
that it be done inaccurately whenever a leap second is involved!  As far
as ANSI C is concerned, that inaccuracy might be considered a (poor)
"quality of implementation" issue.  Certainly converting future times
to/from whatever Congress mandates cannot guarantee accuracy.

wald-david@CS.YALE.EDU (david wald) (10/30/88)

In article <8754@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <12418@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>>changing the epoch or going to 64 bits would break existing programs.
>
>Neither will break existing correct programs that don't assume things
>not promised by the specifications.

As Bill said, it will break existing programs.


============================================================================
David Wald                                              wald-david@yale.UUCP
						       waldave@yalevm.bitnet
============================================================================