Questions about mktime() - and answer

D'Arcy J.M. Cain darcy at druid.uucp
Fri Feb 8 17:29:54 AEST 1991


Many thanks to everyone for their comments and suggestions on the mktime
function.  I have finished a version of mktime which I am posting here
(it's only about 150 lines) to see if anyone has any further comments
or suggestions.  Note the CHECK_INVALID manifest constant is used to
include code to check for the invalid times that occur for one hour
each year in the spring - e.g. 0230h on the first Sunday in April.  I
personally don't include that #define.  I prefer mktime to try to return
some sort of time without having to pre-check for that period.  I did
however test both versions.  sending the function 3 periods around
both changovers gave the following result without CHECK_DEFINE:

Sun Apr  7 01:30:00 1991 isdst: 0
Sun Apr  7 03:30:00 1991 isdst: 1 - NOTE: Extra hour added
Sun Apr  7 03:30:00 1991 isdst: 1
Sun Oct 27 01:30:00 1991 isdst: 1
Sun Oct 27 02:30:00 1991 isdst: 0
Sun Oct 27 03:30:00 1991 isdst: 0

With CHECK_DEFINE included I got the following:

Sun Apr  7 01:30:00 1991 isdst: 0
Wed Dec 31 18:59:59 1969 isdst: -1 - NOTE: Time corresponds to -1 return
Sun Apr  7 03:30:00 1991 isdst: 1
Sun Oct 27 01:30:00 1991 isdst: 1
Sun Oct 27 02:30:00 1991 isdst: 0
Sun Oct 27 03:30:00 1991 isdst: 0

Anyway, here is the code.

------------------------------ cut here --------------------------------
/*
mktime
Written by D'Arcy J.M. Cain

Many thanks to the various netters who helped with the various issues
involved here.  In particular thanks, in no particular order, to:
  Mark Brader (msb at sq.sq.com)
  David Tanguay (datangua at watmath.waterloo.edu)
  Geoff Clare (gwc at root.co.uk)
  Doug Gwyn (gwyn at smoke.brl.mil)

mktime converts the local time in the structure *tp into calendar time.

*/

#include    <types.h>
#include    <time.h>

#ifdef      __MSDOS__
#define		altzone		((time_t)(timezone + 3600))
#endif

/* CAUTION: side effects */
#define		is_leap(x)	(((!((x)%4)&&(x)%100)||!(((x)+1900)%400))?1:0)

static int  yday_size[2][12] = {
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};

static int	mon_size[2][12] = {
	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

/*e.g: normalize(&tm_secs, &tm_hour, 60); */
static void	normalize(int *x, int *y, int f)
{
	if (*x > f)
	{
		*y += *x/f;
		*x %= f;
	}

	/* can't rely on modulus for negative numbers */
	while (*x < 0)
	{
		*x += f;
		(*y)--;
	}
}

time_t	mktime(struct tm *tp)
{
    long unsigned	k;
    time_t			t, t1;
	struct tm		tm;

    tzset();					/* set up time zone */

	/* normalize the time */
	normalize(&tp->tm_sec, &tp->tm_min, 60);
	normalize(&tp->tm_min, &tp->tm_hour, 60);
	normalize(&tp->tm_hour, &tp->tm_mday, 24);

	/* normalize the month first */
	normalize(&tp->tm_mon, &tp->tm_year, 12);

	/* days to months a little tricky */
	while (tp->tm_mday < 1)
	{
		if (--tp->tm_mon < 0)
		{
			tp->tm_year--;
			tp->tm_mon += 12;
		}

		tp->tm_mday += mon_size[is_leap(tp->tm_year)][tp->tm_mon];
	}

	while (tp->tm_mday > (k = mon_size[is_leap(tp->tm_year)][tp->tm_mon]))
	{
		tp->tm_mday -= k;

		if (++tp->tm_mon > 12)
		{
			tp->tm_year++;
			tp->tm_mon -= 12;
		}
	}

    k = tp->tm_year/4;			/* number of 4 year groups */
    t = (k * 1461) - 1;			/* number of days */
    k = tp->tm_year % 4;		/* number of years beyond group */
    t += (k * 365);				/* add number of days */
    if (k)						/* if not group break year */
        t++;					/* add one day */

	/* Since the epoch starts at Jan 1/70, we have to subtract the */
	/* of days from Jan 1/00.  This is actually 25567 days.  See */
	/* below for the explanation of the discrepancy */
    t -= 25568;					/* t = # days to 00:00:00 Jan 1/70 */

    t += yday_size[is_leap(tp->tm_year)][tp->tm_mon];

	/* Add the number of days in month.  Note that we should really */
	/* subtract 1 from the day first but we effectively did this */
	/* above when we subtracted an extra day (25568 instead of 25567) */
    t += tp->tm_mday;			/* # days to given day at 00:00:00 */

	/* now add in the number of seconds in the day */
    t = (t * 24) + tp->tm_hour;
    t = (t * 60) + tp->tm_min;
    t = (t * 60) + tp->tm_sec;   /* total number of seconds */

	/* if caller thinks he/she knows what time zone then believe them */
	if (tp->tm_isdst == 0)
		t += timezone;
	else if (tp->tm_isdst > 0)
		t += altzone;
	/* otherwise we have to figure it out */
	else
	{
		/* guess dst first */
		t1 = t + altzone;
		tm = *localtime(&t1);

		/* see if the guess matches the reality */
		if (tm.tm_hour == tp->tm_hour && tm.tm_min == tp->tm_min)
			t = t1;
		else

/* if CHECK_INVALID is defined then we attempt to check for the invalid */
/* time case e.g. a time of 0230h on the first sunday in April will */
/* return -1 - personally I don't think this is polite behaviour */
#ifdef	CHECK_INVALID
		{
			t1 = t + timezone;
			tm = *localtime(&t1);

			if (tm.tm_hour == tp->tm_hour && tm.tm_min == tp->tm_min)
				t = t1;
			else
				return(-1);
		}
#else
			t += timezone;
#endif
	}

    *tp = *localtime(&t);		/* set other fields in structure */
    return(t);
}
-----------------------------------------------------------------------

-- 
D'Arcy J.M. Cain (darcy at druid)     |
D'Arcy Cain Consulting             |   There's no government
West Hill, Ontario, Canada         |   like no government!
+1 416 281 6094                    |



More information about the Comp.std.c mailing list