home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / zip22.zip / mktime.c < prev    next >
C/C++ Source or Header  |  1997-07-20  |  9KB  |  255 lines

  1. /* free mktime function
  2.    Copyright 1988, 1989 by David MacKenzie <djm@ai.mit.edu>
  3.    and Michael Haertel <mike@ai.mit.edu>
  4.    Unlimited distribution permitted provided this copyright notice is
  5.    retained and any functional modifications are prominently identified.  */
  6.  
  7. /* Revised 1997 by Christian Spieler:
  8.    The code was changed to get more conformance with ANSI's (resp. modern
  9.    UNIX releases) definition for mktime():
  10.    - Added adjustment for out-of-range values in the fields of struct tm.
  11.    - Added iterations to get the correct UTC result for input values at
  12.      the gaps when daylight saving time is switched on or off.
  13.    - Allow forcing of DST "on" or DST "off" by setting `tm_isdst' field in
  14.      the tm struct to positive number resp. zero. The `tm_isdst' field must
  15.      be negative on entrance of mktime() to enable automatic determination
  16.      if DST is in effect for the requested local time.
  17.    - Added optional check for overflowing the time_t range.  */
  18.  
  19. /* Note: This version of mktime is ignorant of the tzfile.
  20.    When the tm structure passed to mktime represents a local time that
  21.    is valid both as DST time and as standard time (= time value in the
  22.    gap when switching from DST back to standard time), the behaviour
  23.    for `tm_isdst < 0' depends on the current timezone: TZ east of GMT
  24.    assumes winter time, TZ west of GMT assumes summer time.
  25.    Although mktime() (resp. mkgmtime()) tries to adjust for invalid values
  26.    of struct tm members, this may fail for input values that are far away
  27.    from the valid ranges. The adjustment process does not check for overflows
  28.    or wrap arounds in the struct tm components.  */
  29.  
  30. #ifndef OF
  31. #  ifdef __STDC__
  32. #    define OF(a) a
  33. #  else
  34. #    define OF(a) ()
  35. #  endif
  36. #endif
  37.  
  38. #ifndef ZCONST
  39. #  define ZCONST const
  40. #endif
  41.  
  42. #include <time.h>
  43.  
  44. time_t mkgmtime OF((struct tm *));
  45. time_t mktime OF((struct tm *));
  46.  
  47. /* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
  48.    of the local time and date in the exploded time structure `tm',
  49.    adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'.
  50.    If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of
  51.    tm_isdst is determined and returned. Otherwise, mktime() assumes this
  52.    field as valid; its information is used when converting local time
  53.    to UTC.
  54.    Return -1 if time in `tm' cannot be represented as time_t value. */
  55.  
  56. time_t
  57. mktime(tm)
  58.      struct tm *tm;
  59. {
  60.   struct tm *ltm;               /* Local time. */
  61.   time_t loctime;               /* The time_t value of local time. */
  62.   time_t then;                  /* The time to return. */
  63.   long tzoffset_adj;            /* timezone-adjustment `remainder' */
  64.   int bailout_cnt;              /* counter of tries for tz correction */
  65.   int save_isdst;               /* Copy of the tm->isdst input value */
  66.  
  67.   save_isdst = tm->tm_isdst;
  68.   loctime = mkgmtime(tm);
  69.   if (loctime == -1) {
  70.     tm->tm_isdst = save_isdst;
  71.     return (time_t)-1;
  72.   }
  73.  
  74.   /* Correct for the timezone and any daylight savings time.
  75.      The correction is verified and repeated when not correct, to
  76.      take into account the rare case that a change to or from daylight
  77.      savings time occurs between when it is the time in `tm' locally
  78.      and when it is that time in Greenwich. After the second correction,
  79.      the "timezone & daylight" offset should be correct in all cases. To
  80.      be sure, we allow a third try, but then the loop is stopped. */
  81.   bailout_cnt = 3;
  82.   then = loctime;
  83.   do {
  84.     tzoffset_adj = loctime - mkgmtime(localtime(&then));
  85.     if (tzoffset_adj == 0L)
  86.       break;
  87.     then += tzoffset_adj;
  88.   } while (--bailout_cnt > 0);
  89.  
  90.   if (tzoffset_adj != 0L) {
  91.     /* Signal failure if timezone adjustment did not converge. */
  92.     tm->tm_isdst = save_isdst;
  93.     return (time_t)-1;
  94.   }
  95.  
  96.   ltm = localtime(&then);
  97.  
  98.   if (save_isdst >= 0) {
  99.     if (ltm->tm_isdst  && !save_isdst)
  100.       if (then + 3600 < then)
  101.         then = (time_t)-1;
  102.       else
  103.         then += 3600;
  104.     else if (!ltm->tm_isdst && save_isdst)
  105.       if (then - 3600 > then)
  106.         then = (time_t)-1;
  107.       else
  108.         then -= 3600;
  109.     ltm->tm_isdst = save_isdst;
  110.   }
  111.  
  112.   if (tm != ltm)  /* `tm' may already point to localtime's internal storage */
  113.     *tm = *ltm;
  114.  
  115.   return then;
  116. }
  117.  
  118.  
  119. #ifndef NO_TIME_T_MAX
  120.    /* Provide default values for the upper limit of the time_t range.
  121.       These are the result of the decomposition into a `struct tm' for
  122.       the time value 0xFFFFFFFEL ( = (time_t)-2 ).
  123.       Note: `(time_t)-1' is reserved for "invalid time"!  */
  124. #  ifndef TM_YEAR_MAX
  125. #    define TM_YEAR_MAX         2106
  126. #  endif
  127. #  ifndef TM_MON_MAX
  128. #    define TM_MON_MAX          1       /* February */
  129. #  endif
  130. #  ifndef TM_MDAY_MAX
  131. #    define TM_MDAY_MAX         7
  132. #  endif
  133. #  ifndef TM_HOUR_MAX
  134. #    define TM_HOUR_MAX         6
  135. #  endif
  136. #  ifndef TM_MIN_MAX
  137. #    define TM_MIN_MAX          28
  138. #  endif
  139. #  ifndef TM_SEC_MAX
  140. #    define TM_SEC_MAX          14
  141. #  endif
  142. #endif /* NO_TIME_T_MAX */
  143.  
  144. /* Adjusts out-of-range values for `tm' field `tm_member'. */
  145. #define ADJUST_TM(tm_member, tm_carry, modulus) \
  146.   if ((tm_member) < 0) { \
  147.     tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
  148.     tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
  149.   } else if ((tm_member) >= (modulus)) { \
  150.     tm_carry += (tm_member) / (modulus); \
  151.     tm_member = (tm_member) % (modulus); \
  152.   }
  153.  
  154. /* Nonzero if `y' is a leap year, else zero. */
  155. #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
  156.  
  157. /* Number of leap years from 1970 to `y' (not including `y' itself). */
  158. #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
  159.  
  160. /* Additional leapday in February of leap years. */
  161. #define leapday(m, y) ((m) == 1 && leap (y))
  162.  
  163. /* Length of month `m' (0 .. 11) */
  164. #define monthlen(m, y) (ydays[(m)+1] - ydays[m] + leapday (m, y))
  165.  
  166. /* Accumulated number of days from 01-Jan up to start of current month. */
  167. static ZCONST short ydays[] =
  168. {
  169.   0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  170. };
  171.  
  172. /* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
  173.    of the Greenwich Mean time and date in the exploded time structure `tm'.
  174.    This function does always put back normalized values into the `tm' struct,
  175.    parameter, including the calculated numbers for `tm->tm_yday',
  176.    `tm->tm_wday', and `tm->tm_isdst'.
  177.    Returns -1 if the time in the `tm' parameter cannot be represented
  178.    as valid `time_t' number. */
  179.  
  180. time_t
  181. mkgmtime(tm)
  182.      struct tm *tm;
  183. {
  184.   int years, months, days, hours, minutes, seconds;
  185.  
  186.   years = tm->tm_year + 1900;   /* year - 1900 -> year */
  187.   months = tm->tm_mon;          /* 0..11 */
  188.   days = tm->tm_mday - 1;       /* 1..31 -> 0..30 */
  189.   hours = tm->tm_hour;          /* 0..23 */
  190.   minutes = tm->tm_min;         /* 0..59 */
  191.   seconds = tm->tm_sec;         /* 0..61 in ANSI C. */
  192.  
  193.   ADJUST_TM(seconds, minutes, 60)
  194.   ADJUST_TM(minutes, hours, 60)
  195.   ADJUST_TM(hours, days, 24)
  196.   ADJUST_TM(months, years, 12)
  197.   if (days < 0)
  198.     do {
  199.       if (--months < 0) {
  200.         --years;
  201.         months = 11;
  202.       }
  203.       days += monthlen(months, years);
  204.     } while (days < 0);
  205.   else
  206.     while (days >= monthlen(months, years)) {
  207.       days -= monthlen(months, years);
  208.       if (++months >= 12) {
  209.         ++years;
  210.         months = 0;
  211.       }
  212.     }
  213.  
  214.   /* Restore adjusted values in tm structure */
  215.   tm->tm_year = years - 1900;
  216.   tm->tm_mon = months;
  217.   tm->tm_mday = days + 1;
  218.   tm->tm_hour = hours;
  219.   tm->tm_min = minutes;
  220.   tm->tm_sec = seconds;
  221.  
  222.   /* Set `days' to the number of days into the year. */
  223.   days += ydays[months] + (months > 1 && leap (years));
  224.   tm->tm_yday = days;
  225.  
  226.   /* Now calculate `days' to the number of days since Jan 1, 1970. */
  227.   days = (unsigned)days + 365 * (unsigned)(years - 1970) +
  228.          (unsigned)(nleap (years));
  229.   tm->tm_wday = ((unsigned)days + 4) % 7; /* Jan 1, 1970 was Thursday. */
  230.   tm->tm_isdst = 0;
  231.  
  232.   if (years < 1970)
  233.     return (time_t)-1;
  234.  
  235. #if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
  236. #if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
  237.   if (years > TM_YEAR_MAX ||
  238.       (years == TM_YEAR_MAX &&
  239.        (tm->tm_yday > ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
  240.                       (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) ||
  241.         (tm->tm_yday == ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
  242.                         (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) &&
  243.          (hours > TM_HOUR_MAX ||
  244.           (hours == TM_HOUR_MAX &&
  245.            (minutes > TM_MIN_MAX ||
  246.             (minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
  247.     return (time_t)-1;
  248. #endif
  249. #endif
  250.  
  251.   return (time_t)(86400L * (unsigned long)(unsigned)days +
  252.                   3600L * (unsigned long)hours +
  253.                   (unsigned long)(60 * minutes + seconds));
  254. }
  255.