home *** CD-ROM | disk | FTP | other *** search
- /* mktime, localtime, gmtime */
- /* written by ERS and placed in the public domain */
-
- #include <stddef.h>
- #include <stdlib.h>
- #include <time.h>
- #include <ctype.h>
- #ifndef _COMPILER_H
- #include <compiler.h>
- #endif
-
- #if 0
- static void
- DEBUG_TM(nm, tm)
- char *nm;
- struct tm *tm;
- {
- char buf[100];
-
- (void)strftime(buf, 100, "%c %z", tm);
- printf("%s: %s\n", nm, buf);
- }
- #else
- #define DEBUG_TM(nm, tm)
- #endif
-
- #define SECS_PER_MIN (60L)
- #define SECS_PER_HOUR (3600L)
- #define SECS_PER_DAY (86400L)
- #define SECS_PER_YEAR (31536000L)
- #define SECS_PER_LEAPYEAR (SECS_PER_DAY + SECS_PER_YEAR)
-
- time_t _timezone = -1; /* holds # seconds west of GMT */
-
- static int
- days_per_mth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
- static int
- mth_start[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
-
- static time_t tzoffset __PROTO((char *s, int *hasdst));
- static int indst __PROTO((const struct tm *t));
-
- static int dst = -1; /* whether dst holds in current timezone */
-
- /*
- * FIXME: none of these routines is very efficient. Also, none of them
- * handle dates before Jan 1, 1970.
- *
- */
-
- /*
- * mktime: take a time structure representing the local time (such as is
- * returned by localtime() and convert it into the standard representation
- * (as seconds since midnight Jan. 1 1970, GMT).
- *
- * Note that time() sends us such a structure with tm_yday undefined, so
- * we shouldn't count on tm_yday being correct.
- */
-
- time_t
- mktime(t)
- const struct tm *t;
- {
- time_t s;
- int y;
-
- DEBUG_TM("mktime", t);
- if (t->tm_year < 70) /* year before 1970 */
- return (time_t) -1;
-
- /* calculate tm_yday here */
- y = (t->tm_mday - 1) + mth_start[t->tm_mon] + /* leap year correction */
- ( ( (t->tm_year % 4) != 0 ) ? 0 : (t->tm_mon > 1) );
-
- s = (t->tm_sec)+(t->tm_min*SECS_PER_MIN)+(t->tm_hour*SECS_PER_HOUR) +
- (y*SECS_PER_DAY)+((t->tm_year - 70)*SECS_PER_YEAR) +
- ((t->tm_year - 69)/4)*SECS_PER_DAY;
-
- /* Now adjust for the time zone and possible daylight savings time */
- /* note that we have to call tzset() every time; see 1003.1 sect 8.1.1 */
- tzset();
-
- s += _timezone;
- if (dst == 1 && indst(t))
- s -= 3600;
-
- return s;
- }
-
-
- struct tm *_gmtime(t, stm)
- const time_t *t;
- struct tm *stm;
- {
- time_t time = *t;
- int year, mday, i;
-
- if (time < 0) /* negative times are bad */
- return 0;
- stm->tm_wday = ((time/SECS_PER_DAY) + 4) % 7;
-
- year = 70;
- for (;;) {
- if (time < SECS_PER_YEAR) break;
- if ((year % 4) == 0) {
- if (time < SECS_PER_LEAPYEAR)
- break;
- else
- time -= SECS_PER_LEAPYEAR;
- }
- else
- time -= SECS_PER_YEAR;
- year++;
- }
- stm->tm_year = year;
- mday = stm->tm_yday = time/SECS_PER_DAY;
-
- days_per_mth[1] = (year % 4) ? 28 : 29;
- for (i = 0; mday >= days_per_mth[i]; i++)
- mday -= days_per_mth[i];
- stm->tm_mon = i;
- stm->tm_mday = mday + 1;
-
- time = time % SECS_PER_DAY;
- stm->tm_hour = time/SECS_PER_HOUR;
- time = time % SECS_PER_HOUR;
- stm->tm_min = time/SECS_PER_MIN;
- stm->tm_sec = time % SECS_PER_MIN;
- stm->tm_isdst = 0;
-
- DEBUG_TM("gmtime", stm);
- return stm;
- }
-
- struct tm *gmtime(t)
- const time_t *t;
- {
- static struct tm gtime;
-
- return _gmtime(t, >ime);
- }
-
- /* given a standard time, convert it to a local time */
-
- struct tm *localtime(t)
- const time_t *t;
- {
- static struct tm ltim;
- struct tm *stm;
- time_t offset; /* seconds between local time and GMT */
-
- tzset();
- offset = *t - _timezone;
- stm = _gmtime(&offset, <im);
- if (stm == NULL) return stm; /* check for illegal time */
- stm->tm_isdst = (dst == -1) ? -1 : 0;
-
- if (dst == 1 && indst((const struct tm *)stm)) { /* daylight savings time in effect */
- stm->tm_isdst = 1;
- if (++stm->tm_hour > 23) {
- stm->tm_hour -= 24;
- stm->tm_wday = (stm->tm_wday + 1) % 7;
- stm->tm_yday++;
- stm->tm_mday++;
- if (stm->tm_mday > days_per_mth[stm->tm_mon]) {
- stm->tm_mday = 1;
- stm->tm_mon++;
- }
- }
- }
- DEBUG_TM("localtime", stm);
- return stm;
- }
-
- /*
- * THIS IS A DELIBERATE VIOLATION OF THE ANSI STANDARD:
- * there appears to be a conflict between Posix and ANSI; the former
- * mandates a "tzset()" function that gets called whenever time()
- * does, and which sets some global variables. ANSI wants none of
- * this. Several Unix implementations have tzset(), and few people are
- * going to be hurt by it, so it's included here.
- */
-
- /* set the timezone and dst flag to the local rules. Also sets the
- global variable tzname to the names of the timezones
- */
-
- char *tzname[2] = {"UCT", "UCT"};
-
- void
- tzset()
- {
- _timezone = tzoffset(getenv("TZ"), &dst);
- }
-
- /*
- * determine the difference, in seconds, between the given time zone
- * and Greenwich Mean. As a side effect, the integer pointed to
- * by hasdst is set to 1 if the given time zone follows daylight
- * savings time, 0 if there is no DST.
- *
- * Time zones are given as strings of the form
- * "[TZNAME][h][:m][TZDSTNAME]" where h:m gives the hours:minutes
- * east of GMT for the timezone (if [:m] does not appear, 0 is assumed).
- * If the final field, TZDSTNAME, appears, then the time zone follows
- * daylight savings time.
- *
- * Example: EST5EDT would represent the N. American Eastern time zone
- * CST6CDT would represent the N. American Central time zone
- * NFLD3:30NFLD would represent Newfoundland time (one and a
- * half hours ahead of Eastern).
- * OZCST-9:30 would represent the Australian central time zone.
- * (which, so I hear, doesn't have DST).
- *
- * NOTE: support for daylight savings time is currently very bogus.
- * It's probably best to do without, unless you live in North America.
- *
- */
- #define TZNAMLEN 8 /* max. length of time zone name */
-
- static
- time_t
- tzoffset(s, hasdst)
- char *s;
- int *hasdst;
- {
- time_t off = 0;
- int x, sgn = 1;
- static char stdname[TZNAMLEN+1], dstname[TZNAMLEN+1];
- static char unknwn[4] = "???";
-
- char *n;
-
- *hasdst = -1; /* Assume unknown */
- if (!s || !*s)
- return 0; /* Assume GMT */
-
- *hasdst = 0;
-
- n = stdname;
- while (*s && isalpha(*s)) {
- *n++ = *s++; /* skip name */
- }
- *n++ = 0;
-
- /* now figure out the offset */
-
- x = 0;
- if (*s == '-') {
- sgn = -1;
- s++;
- }
- while (isdigit(*s)) {
- x = 10 * x + toint(*s);
- s++;
- }
- off = x * SECS_PER_HOUR;
- if (*s == ':') {
- x = 0;
- s++;
- while (isdigit(*s)) {
- x = 10 * x + toint(*s);
- s++;
- }
- off += (x * SECS_PER_MIN);
- }
-
- n = dstname;
- if (isalpha(*s)) {
- *hasdst = 1;
- while (*s && isalpha(*s)) *n++ = *s++;
- }
- *n++ = 0;
-
- if (stdname[0])
- tzname[0] = stdname;
- else
- tzname[0] = unknwn;
-
- if (dstname[0])
- tzname[1] = dstname;
- else
- tzname[1] = stdname;
-
- return sgn * off;
- }
-
- /*
- * Given a tm struct representing the local time, determine whether
- * DST is currently in effect. This should only be
- * called if it is known that the time zone indeed supports DST.
- *
- * FIXME: For now, assume that everyone follows the North American
- * time zone rules, all the time. This means daylight savings
- * time is assumed to be in effect from the first Sunday in April
- * to the last Sunday in October. Prior to 1987, the old rules
- * (last Sunday in April to last Sunday in Oct.) are used, even when
- * (as in 1974) they're not applicable. Sorry.
- *
- */
-
- static
- int indst(t)
- const struct tm *t;
- {
- if (t->tm_mon == 3) { /* April */
- /* before 1987, see if there's another sunday in the month */
- if (t->tm_year < 87 && t->tm_wday + 30 - t->tm_mday < 7)
- return 1; /* no there isn't */
- /* after 1987, see if a sunday has happened yet */
- if (t->tm_wday - t->tm_mday < 0)
- return 1; /* yep */
- return 0;
- }
- if (t->tm_mon == 9) { /* October */
- if (t->tm_wday + 31 - t->tm_mday < 7)
- return 0; /* there are no more sundays */
- return 1;
- }
- /* Otherwise, see if it's a month between April and October exclusive */
- return (t->tm_mon > 3 && t->tm_mon < 9);
- }
-
-