home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / zip / gnu / mntlib16.lzh / MNTLIB16 / LOCALTIM.C < prev    next >
C/C++ Source or Header  |  1993-07-29  |  9KB  |  325 lines

  1. /* mktime, localtime, gmtime */
  2. /* written by ERS and placed in the public domain */
  3.  
  4. #include <stddef.h>
  5. #include <stdlib.h>
  6. #include <time.h>
  7. #include <ctype.h>
  8. #ifndef _COMPILER_H
  9. #include <compiler.h>
  10. #endif
  11.  
  12. #if 0
  13. static void
  14. DEBUG_TM(nm, tm)
  15.     char *nm;
  16.     struct tm *tm;
  17. {
  18.     char buf[100];
  19.  
  20.     (void)strftime(buf, 100, "%c %z", tm);
  21.     printf("%s: %s\n", nm, buf);
  22. }
  23. #else
  24. #define DEBUG_TM(nm, tm)
  25. #endif
  26.  
  27. #define SECS_PER_MIN    (60L)
  28. #define SECS_PER_HOUR   (3600L)
  29. #define SECS_PER_DAY    (86400L)
  30. #define SECS_PER_YEAR   (31536000L)
  31. #define SECS_PER_LEAPYEAR (SECS_PER_DAY + SECS_PER_YEAR)
  32.  
  33. time_t _timezone = -1;    /* holds # seconds west of GMT */
  34.  
  35. static int
  36. days_per_mth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  37.  
  38. static int
  39. mth_start[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  40.  
  41. static time_t tzoffset __PROTO((char *s, int *hasdst));
  42. static int indst __PROTO((const struct tm *t));
  43.  
  44. static int dst = -1;    /* whether dst holds in current timezone */
  45.  
  46. /*
  47.  * FIXME: none of these routines is very efficient. Also, none of them
  48.  * handle dates before Jan 1, 1970.
  49.  *
  50.  */
  51.  
  52. /*
  53.  * mktime: take a time structure representing the local time (such as is
  54.  *  returned by localtime() and convert it into the standard representation
  55.  *  (as seconds since midnight Jan. 1 1970, GMT).
  56.  *
  57.  * Note that time() sends us such a structure with tm_yday undefined, so
  58.  * we shouldn't count on tm_yday being correct.
  59.  */
  60.  
  61. time_t
  62. mktime(t)
  63.         const struct tm *t;
  64. {
  65.         time_t s;
  66.         int y;
  67.  
  68. DEBUG_TM("mktime", t);
  69.         if (t->tm_year < 70)      /* year before 1970 */
  70.                 return (time_t) -1;
  71.  
  72. /* calculate tm_yday here */
  73.     y = (t->tm_mday - 1) + mth_start[t->tm_mon] + /* leap year correction */
  74.         ( ( (t->tm_year % 4) != 0 ) ? 0 : (t->tm_mon > 1) );
  75.  
  76.     s = (t->tm_sec)+(t->tm_min*SECS_PER_MIN)+(t->tm_hour*SECS_PER_HOUR) +
  77.         (y*SECS_PER_DAY)+((t->tm_year - 70)*SECS_PER_YEAR) +
  78.         ((t->tm_year - 69)/4)*SECS_PER_DAY;
  79.  
  80. /* Now adjust for the time zone and possible daylight savings time */
  81. /* note that we have to call tzset() every time; see 1003.1 sect 8.1.1 */
  82.     tzset();
  83.  
  84.         s += _timezone;
  85.         if (dst == 1 && indst(t))
  86.                 s -= 3600;
  87.  
  88.         return s;
  89. }
  90.  
  91.  
  92. struct tm *_gmtime(t, stm)
  93.         const time_t *t;
  94.     struct tm *stm;
  95. {
  96.         time_t  time = *t;
  97.         int     year, mday, i;
  98.  
  99.         if (time < 0)   /* negative times are bad */
  100.                 return 0;
  101.         stm->tm_wday = ((time/SECS_PER_DAY) + 4) % 7;
  102.  
  103.         year = 70;
  104.         for (;;) {
  105.                 if (time < SECS_PER_YEAR) break;
  106.                 if ((year % 4) == 0) {
  107.                         if (time < SECS_PER_LEAPYEAR)
  108.                                 break;
  109.                         else
  110.                                 time -= SECS_PER_LEAPYEAR;
  111.                 }
  112.                 else
  113.                         time -= SECS_PER_YEAR;
  114.                 year++;
  115.         }
  116.         stm->tm_year = year;
  117.         mday = stm->tm_yday = time/SECS_PER_DAY;
  118.  
  119.         days_per_mth[1] = (year % 4) ? 28 : 29;
  120.         for (i = 0; mday >= days_per_mth[i]; i++)
  121.                 mday -= days_per_mth[i];
  122.         stm->tm_mon = i;
  123.         stm->tm_mday = mday + 1;
  124.  
  125.         time = time % SECS_PER_DAY;
  126.         stm->tm_hour = time/SECS_PER_HOUR;
  127.         time = time % SECS_PER_HOUR;
  128.         stm->tm_min = time/SECS_PER_MIN;
  129.         stm->tm_sec = time % SECS_PER_MIN;
  130.         stm->tm_isdst = 0;
  131.  
  132. DEBUG_TM("gmtime", stm);
  133.         return stm;
  134. }
  135.  
  136. struct tm *gmtime(t)
  137.     const time_t *t;
  138. {
  139.     static struct tm gtime;
  140.  
  141.     return _gmtime(t, >ime);
  142. }
  143.  
  144. /* given a standard time, convert it to a local time */
  145.  
  146. struct tm *localtime(t)
  147.         const time_t *t;
  148. {
  149.     static struct tm ltim;
  150.         struct tm *stm;
  151.         time_t offset;  /* seconds between local time and GMT */
  152.  
  153.     tzset();
  154.         offset = *t - _timezone;
  155.         stm = _gmtime(&offset, <im);
  156.     if (stm == NULL) return stm;        /* check for illegal time */
  157.         stm->tm_isdst = (dst == -1) ? -1 : 0;
  158.  
  159.         if (dst == 1 && indst((const struct tm *)stm)) {   /* daylight savings time in effect */
  160.                 stm->tm_isdst = 1;
  161.                 if (++stm->tm_hour > 23) {
  162.                         stm->tm_hour -= 24;
  163.                         stm->tm_wday = (stm->tm_wday + 1) % 7;
  164.                         stm->tm_yday++;
  165.                         stm->tm_mday++;
  166.                         if (stm->tm_mday > days_per_mth[stm->tm_mon]) {
  167.                                 stm->tm_mday = 1;
  168.                                 stm->tm_mon++;
  169.                         }
  170.                 }
  171.         }
  172. DEBUG_TM("localtime", stm);
  173.         return stm;
  174. }
  175.  
  176. /*
  177.  * THIS IS A DELIBERATE VIOLATION OF THE ANSI STANDARD:
  178.  * there appears to be a conflict between Posix and ANSI; the former
  179.  * mandates a "tzset()" function that gets called whenever time()
  180.  * does, and which sets some global variables. ANSI wants none of
  181.  * this. Several Unix implementations have tzset(), and few people are
  182.  * going to be hurt by it, so it's included here.
  183.  */
  184.  
  185. /* set the timezone and dst flag to the local rules. Also sets the
  186.    global variable tzname to the names of the timezones
  187.  */
  188.  
  189. char *tzname[2] = {"UCT", "UCT"};
  190.  
  191. void
  192. tzset()
  193. {
  194.     _timezone = tzoffset(getenv("TZ"), &dst);
  195. }
  196.  
  197. /*
  198.  * determine the difference, in seconds, between the given time zone
  199.  * and Greenwich Mean. As a side effect, the integer pointed to
  200.  * by hasdst is set to 1 if the given time zone follows daylight
  201.  * savings time, 0 if there is no DST.
  202.  *
  203.  * Time zones are given as strings of the form
  204.  * "[TZNAME][h][:m][TZDSTNAME]" where h:m gives the hours:minutes
  205.  * east of GMT for the timezone (if [:m] does not appear, 0 is assumed).
  206.  * If the final field, TZDSTNAME, appears, then the time zone follows
  207.  * daylight savings time.
  208.  *
  209.  * Example: EST5EDT would represent the N. American Eastern time zone
  210.  *          CST6CDT would represent the N. American Central time zone
  211.  *          NFLD3:30NFLD would represent Newfoundland time (one and a
  212.  *              half hours ahead of Eastern).
  213.  *          OZCST-9:30 would represent the Australian central time zone.
  214.  *              (which, so I hear, doesn't have DST).
  215.  *
  216.  * NOTE: support for daylight savings time is currently very bogus.
  217.  * It's probably best to do without, unless you live in North America.
  218.  *
  219.  */
  220. #define TZNAMLEN    8    /* max. length of time zone name */
  221.  
  222. static
  223. time_t 
  224. tzoffset(s, hasdst)
  225.         char *s;
  226.         int  *hasdst;
  227. {
  228.         time_t off = 0;
  229.         int x, sgn = 1;
  230.     static char stdname[TZNAMLEN+1], dstname[TZNAMLEN+1];
  231.     static char unknwn[4] = "???";
  232.  
  233.     char *n;
  234.  
  235.         *hasdst = -1;                   /* Assume unknown */
  236.         if (!s || !*s)
  237.                 return 0;               /* Assume GMT */
  238.  
  239.        *hasdst = 0;
  240.  
  241.     n = stdname;
  242.         while (*s && isalpha(*s)) {
  243.         *n++ = *s++;        /* skip name */
  244.     }
  245.     *n++ = 0;
  246.  
  247. /* now figure out the offset */
  248.  
  249.         x = 0;
  250.         if (*s == '-') {
  251.                 sgn = -1;
  252.                 s++;
  253.         }
  254.         while (isdigit(*s)) {
  255.                 x = 10 * x + toint(*s);
  256.                 s++;
  257.         }
  258.         off = x * SECS_PER_HOUR;
  259.         if (*s == ':') {
  260.                 x = 0;
  261.                 s++;
  262.                 while (isdigit(*s)) {
  263.                         x = 10 * x + toint(*s);
  264.                         s++;
  265.                 }
  266.             off += (x * SECS_PER_MIN);
  267.         }
  268.  
  269.     n = dstname;
  270.         if (isalpha(*s)) {
  271.                 *hasdst = 1;
  272.         while (*s && isalpha(*s)) *n++ = *s++;
  273.     }
  274.     *n++ = 0;
  275.  
  276.     if (stdname[0])
  277.         tzname[0] = stdname;
  278.     else
  279.         tzname[0] = unknwn;
  280.  
  281.     if (dstname[0])
  282.         tzname[1] = dstname;
  283.     else
  284.         tzname[1] = stdname;
  285.  
  286.         return sgn * off;
  287. }
  288.  
  289. /*
  290.  * Given a tm struct representing the local time, determine whether
  291.  * DST is currently in effect. This should only be
  292.  * called if it is known that the time zone indeed supports DST.
  293.  *
  294.  * FIXME: For now, assume that everyone follows the North American
  295.  *   time zone rules, all the time. This means daylight savings
  296.  *   time is assumed to be in effect from the first Sunday in April
  297.  *   to the last Sunday in October. Prior to 1987, the old rules
  298.  *   (last Sunday in April to last Sunday in Oct.) are used, even when
  299.  *   (as in 1974) they're not applicable. Sorry.
  300.  *
  301.  */
  302.  
  303. static
  304. int indst(t)
  305.         const struct tm *t;
  306. {
  307.         if (t->tm_mon == 3) {           /* April */
  308. /* before 1987, see if there's another sunday in the month */
  309.                 if (t->tm_year < 87 && t->tm_wday + 30 - t->tm_mday < 7)
  310.                         return 1;       /* no there isn't */
  311. /* after 1987, see if a sunday has happened yet */
  312.                 if (t->tm_wday - t->tm_mday < 0)
  313.                         return 1;       /* yep */
  314.                 return 0;
  315.         }
  316.         if (t->tm_mon == 9) {           /* October */
  317.                 if (t->tm_wday + 31 - t->tm_mday < 7)
  318.                         return 0;       /* there are no more sundays */
  319.                 return 1;
  320.         }
  321. /* Otherwise, see if it's a month between April and October exclusive */
  322.         return (t->tm_mon > 3 && t->tm_mon < 9);
  323. }
  324.  
  325.