home *** CD-ROM | disk | FTP | other *** search
- 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
-
- 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 <time.h>
-
- /*
- * 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
-