Path: utzoo!utgpu!cs.utexas.edu!chinacat!sequoia!rpp386!jfh From: jfh@rpp386.cactus.org (John F. Haugh II) Newsgroups: alt.sources Subject: mktime() function (was: Re: UNIX-Time ...) Message-ID: <18767@rpp386.cactus.org> Date: 1 Dec 90 01:21:19 GMT Reply-To: jfh@rpp386.cactus.org (John F. Haugh II) Organization: Lone Star Cafe and BBS Service Lines: 190 X-Clever-Slogan: Recycle or Die. X-Archive-Name: mktime-jfh.c This is the last and final mktime()-like function. It has been tested for several million values of "time" and seems to work quite perfectly. One of the tests covered a random time over a several year stretch, comparing the value input to localtime() against the value returned by mktime() when handed the output of localtime(). It was also tested for over 10 million other values as well, all of which returned the same exact value for "time" that was initially fed to localtime(). This version includes support for the tm_isdst structure member, which is something which Boyd left out of his original version, and which is responsible for the ambiguity between which 1:51:45 his function computed the time for last October 28th. Below are the performance results of executing the two functions 1,000 times each. The driver function consists of main () { long i, t; struct tm tm; i = 12345678L; tm = *(localtime (&i)); for (i = 0;i < 100000000;i += 100000) #ifdef BOYD t = gmsecs (&tm); #else t = mktime (&tm); #endif } % timex ./bfoo execution complete, exit code = 1 real 21.81 user 21.76 sys 0.05 % timex ./jfoo execution complete, exit code = 1 real 1.79 user 1.73 sys 0.04 % calc 21.81 / 1.79 12.18435754189944 OK, so it's only 12 times faster instead of 15. Sue me ;-) Both were compiled with optimization turned on (-Ox) on a 16Mhz 386. Your milage may vary. I really didn't mean for this to be an attack on Boyd's code - I had initially made my suggestion about making a more educated guess to improve upon his code, not to replace it. And now for the source code ... ----- cut and save as mktime.c ----- /* * Copyright 1990, John F. Haugh II * All rights reserved. * * Use, duplication, and disclosure prohibited without * the express written permission of the author. * * Non-commercial (not for profit) source code distribution * is permitted, provided this notice remains intact. */ #include /* * days and juldays are used to compute the number of days in the * current month, and the cummulative number of days in the preceding * months. */ static short days[12] = { 31, 28, 31, 30, 31, 30, /* JAN - JUN */ 31, 31, 30, 31, 30, 31 }; /* JUL - DEC */ static short juldays[12] = { 0, 31, 59, 90, 120, 151, /* JAN - JUN */ 181, 212, 243, 273, 304, 334 }; /* JUL - DEC */ static time_t dtime (tm, time) struct tm *tm; time_t time; { struct tm *sm; time_t diff; time_t julian1, julian2; sm = localtime (&time); julian1 = ((tm->tm_year - 70) * 365) + /* days in whole years */ (((tm->tm_year + 1) - 70) / 4); /* days in leap years */ julian1 += juldays[tm->tm_mon] + /* days in whole months */ (tm->tm_mon > 1 && (tm->tm_year % 4) == 0 ? 1:0); /* leap day */ julian1 += tm->tm_mday - 1; /* days so far this month */ julian2 = ((sm->tm_year - 70) * 365) + /* days in whole years */ (((sm->tm_year + 1) - 70) / 4); /* days in leap years */ julian2 += juldays[sm->tm_mon] + /* days in whole months */ (sm->tm_mon > 1 && (sm->tm_year % 4) == 0 ? 1:0); /* leap day */ julian2 += sm->tm_mday - 1; /* days so far this month */ diff = (julian1 - julian2) * (24L*3600); /* add the days */ diff += (tm->tm_hour - sm->tm_hour) * (3600L); /* add the hours */ diff += (tm->tm_min - sm->tm_min) * (60L); /* add the minutes */ diff += (tm->tm_sec - sm->tm_sec); /* add the seconds */ if (daylight && ((tm->tm_isdst == 0) != (sm->tm_isdst == 0))) { if (tm->tm_isdst) diff -= (timezone - altzone); if (sm->tm_isdst) diff += (timezone - altzone); } return diff; /* that's how far off */ } time_t mktime (tm) struct tm *tm; { time_t init, diff; int i; /* * Let's validate the tm structure. */ if (! tm || tm->tm_year < 0 || tm->tm_year > 200 || tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_mday < 0 /* other half later */ || tm->tm_hour < 0 || tm->tm_hour > 23 || tm->tm_min < 0 || tm->tm_min > 59 || tm->tm_sec < 0 || tm->tm_sec > 59) return -1L; if (tm->tm_mon == 2 && tm->tm_year % 4 == 0) { if (tm->tm_mday > 29) return -1L; } else if (tm->tm_mday > days[tm->tm_mon]) return -1L; /* * POSIX says that tzset() must be called to get the * timezone information. [ "AS-IF" tzset() were called ] */ tzset (); /* * The first guess is within a few hours, the second * guess should get it exactly right. A third guess * may be needed in some cases. * * In practice the first call is exact and the second * call is only needed to verify this. Bizarre behavior * around daylight savings time could trip this up, so * I go again just to make sure. */ if ((init = dtime (tm, 0L)) == 0) return init; if ((diff = dtime (tm, init)) == 0) return init; if ((diff = dtime (tm, init += diff)) == 0) return init; /* * The difference had better be less than a minute * by this time or the date is probably invalid * some how. */ if (diff < -60 || diff > 60) return -1; return init + diff; } -- John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 832-8832 Domain: jfh@rpp386.cactus.org "SCCS, the source motel! Programs check in and never check out!" -- Ken Thompson Brought to you by Super Global Mega Corp .com