home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / mktime.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  9KB  |  299 lines

  1. /***
  2. *mktime.c - Convert struct tm value to time_t value.
  3. *
  4. *       Copyright (c) 1987-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       Defines mktime() and _mkgmtime(), routines to converts a time value
  8. *       in a tm structure (possibly incomplete) into a time_t value, then
  9. *       update (all) the structure fields with "normalized" values.
  10. *
  11. *******************************************************************************/
  12.  
  13. #include <cruntime.h>
  14. #include <stddef.h>
  15. #include <ctime.h>
  16. #include <time.h>
  17. #include <internal.h>
  18.  
  19. /*
  20.  * ChkAdd evaluates to TRUE if dest = src1 + src2 has overflowed
  21.  */
  22. #define ChkAdd(dest, src1, src2)   ( ((src1 >= 0L) && (src2 >= 0L) \
  23.     && (dest < 0L)) || ((src1 < 0L) && (src2 < 0L) && (dest >= 0L)) )
  24.  
  25. /*
  26.  * ChkMul evaluates to TRUE if dest = src1 * src2 has overflowed
  27.  */
  28. #define ChkMul(dest, src1, src2)   ( src1 ? (dest/src1 != src2) : 0 )
  29.  
  30.  
  31. /*
  32.  * Core function for both mktime() and _mkgmtime()
  33.  */
  34. static time_t __cdecl _make_time_t( struct tm *, int);
  35.  
  36.  
  37. /***
  38. *time_t mktime(tb) - Normalize user time block structure
  39. *
  40. *Purpose:
  41. *       Mktime converts a time structure, passed in as an argument, into a
  42. *       calendar time value in internal format (time_t). It also completes
  43. *       and updates the fields the of the passed in structure with 'normalized'
  44. *       values. There are three practical uses for this routine:
  45. *
  46. *       (1) Convert broken-down time to internal time format (time_t).
  47. *       (2) To have mktime fill in the tm_wday, tm_yday, or tm_isdst fields.
  48. *       (3) To pass in a time structure with 'out of range' values for some
  49. *           fields and have mktime "normalize" them (e.g., pass in 1/35/87 and
  50. *           get back 2/4/87).
  51. *Entry:
  52. *       struct tm *tb - pointer to a tm time structure to convert and
  53. *                       normalize
  54. *
  55. *Exit:
  56. *       If successful, mktime returns the specified calender time encoded as
  57. *       a time_t value. Otherwise, (time_t)(-1) is returned to indicate an
  58. *       error.
  59. *
  60. *Exceptions:
  61. *       None.
  62. *
  63. *******************************************************************************/
  64.  
  65.  
  66. time_t __cdecl mktime (
  67.         struct tm *tb
  68.         )
  69. {
  70.         return( _make_time_t(tb, 1) );
  71. }
  72.  
  73.  
  74. /***
  75. *time_t _mkgmtime(tb) - Convert broken down UTC time to time_t
  76. *
  77. *Purpose:
  78. *       Convert a tm structure, passed in as an argument, containing a UTC
  79. *       time value to internal format (time_t). It also completes and updates
  80. *       the fields the of the passed in structure with 'normalized' values.
  81.  
  82. *Entry:
  83. *       struct tm *tb - pointer to a tm time structure to convert and
  84. *                       normalize
  85. *
  86. *Exit:
  87. *       If successful, _mkgmtime returns the calender time encoded as time_t
  88. *       Otherwise, (time_t)(-1) is returned to indicate an error.
  89. *
  90. *Exceptions:
  91. *       None.
  92. *
  93. *******************************************************************************/
  94.  
  95. time_t __cdecl _mkgmtime (
  96.         struct tm *tb
  97.         )
  98. {
  99.         return( _make_time_t(tb, 0) );
  100. }
  101.  
  102.  
  103. /***
  104. *static time_t make_time_t(tb, ultflag) -
  105. *
  106. *Purpose:
  107. *       Converts a struct tm value to a time_t value, then updates the struct
  108. *       tm value. Either local time or UTC is supported, based on ultflag.
  109. *       This is the routine that actually does the work for both mktime() and
  110. *       _mkgmtime().
  111. *
  112. *Entry:
  113. *       struct tm *tb - pointer to a tm time structure to convert and
  114. *                       normalize
  115. *       int ultflag   - use local time flag. the tb structure is assumed
  116. *                       to represent a local date/time if ultflag > 0.
  117. *                       otherwise, UTC is assumed.
  118. *
  119. *Exit:
  120. *       If successful, mktime returns the specified calender time encoded as
  121. *       a time_t value. Otherwise, (time_t)(-1) is returned to indicate an
  122. *       error.
  123. *
  124. *Exceptions:
  125. *       None.
  126. *
  127. *******************************************************************************/
  128.  
  129. static time_t __cdecl _make_time_t (
  130.         struct tm *tb,
  131.         int ultflag
  132.         )
  133. {
  134.         long tmptm1, tmptm2, tmptm3;
  135.         struct tm *tbtemp;
  136.  
  137.         /*
  138.          * First, make sure tm_year is reasonably close to being in range.
  139.          */
  140.         if ( ((tmptm1 = tb->tm_year) < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR
  141.           + 1) )
  142.             goto err_mktime;
  143.  
  144.  
  145.         /*
  146.          * Adjust month value so it is in the range 0 - 11.  This is because
  147.          * we don't know how many days are in months 12, 13, 14, etc.
  148.          */
  149.  
  150.         if ( (tb->tm_mon < 0) || (tb->tm_mon > 11) ) {
  151.  
  152.             /*
  153.              * no danger of overflow because the range check above.
  154.              */
  155.             tmptm1 += (tb->tm_mon / 12);
  156.  
  157.             if ( (tb->tm_mon %= 12) < 0 ) {
  158.                 tb->tm_mon += 12;
  159.                 tmptm1--;
  160.             }
  161.  
  162.             /*
  163.              * Make sure year count is still in range.
  164.              */
  165.             if ( (tmptm1 < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR + 1) )
  166.                 goto err_mktime;
  167.         }
  168.  
  169.         /***** HERE: tmptm1 holds number of elapsed years *****/
  170.  
  171.         /*
  172.          * Calculate days elapsed minus one, in the given year, to the given
  173.          * month. Check for leap year and adjust if necessary.
  174.          */
  175.         tmptm2 = _days[tb->tm_mon];
  176.         if ( !(tmptm1 & 3) && (tb->tm_mon > 1) )
  177.                 tmptm2++;
  178.  
  179.         /*
  180.          * Calculate elapsed days since base date (midnight, 1/1/70, UTC)
  181.          *
  182.          *
  183.          * 365 days for each elapsed year since 1970, plus one more day for
  184.          * each elapsed leap year. no danger of overflow because of the range
  185.          * check (above) on tmptm1.
  186.          */
  187.         tmptm3 = (tmptm1 - _BASE_YEAR) * 365L + ((tmptm1 - 1L) >> 2)
  188.           - _LEAP_YEAR_ADJUST;
  189.  
  190.         /*
  191.          * elapsed days to current month (still no possible overflow)
  192.          */
  193.         tmptm3 += tmptm2;
  194.  
  195.         /*
  196.          * elapsed days to current date. overflow is now possible.
  197.          */
  198.         tmptm1 = tmptm3 + (tmptm2 = (long)(tb->tm_mday));
  199.         if ( ChkAdd(tmptm1, tmptm3, tmptm2) )
  200.             goto err_mktime;
  201.  
  202.         /***** HERE: tmptm1 holds number of elapsed days *****/
  203.  
  204.         /*
  205.          * Calculate elapsed hours since base date
  206.          */
  207.         tmptm2 = tmptm1 * 24L;
  208.         if ( ChkMul(tmptm2, tmptm1, 24L) )
  209.             goto err_mktime;
  210.  
  211.         tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_hour);
  212.         if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
  213.             goto err_mktime;
  214.  
  215.         /***** HERE: tmptm1 holds number of elapsed hours *****/
  216.  
  217.         /*
  218.          * Calculate elapsed minutes since base date
  219.          */
  220.  
  221.         tmptm2 = tmptm1 * 60L;
  222.         if ( ChkMul(tmptm2, tmptm1, 60L) )
  223.             goto err_mktime;
  224.  
  225.         tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_min);
  226.         if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
  227.             goto err_mktime;
  228.  
  229.         /***** HERE: tmptm1 holds number of elapsed minutes *****/
  230.  
  231.         /*
  232.          * Calculate elapsed seconds since base date
  233.          */
  234.  
  235.         tmptm2 = tmptm1 * 60L;
  236.         if ( ChkMul(tmptm2, tmptm1, 60L) )
  237.             goto err_mktime;
  238.  
  239.         tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_sec);
  240.         if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
  241.             goto err_mktime;
  242.  
  243.         /***** HERE: tmptm1 holds number of elapsed seconds *****/
  244.  
  245.         if  ( ultflag ) {
  246.  
  247.             /*
  248.              * Adjust for timezone. No need to check for overflow since
  249.              * localtime() will check its arg value
  250.              */
  251.  
  252. #ifdef _WIN32
  253.             __tzset();
  254. #else  /* _WIN32 */
  255. #if defined (_M_MPPC) || defined (_M_M68K)
  256.             _tzset();
  257. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  258. #endif  /* _WIN32 */
  259.  
  260.             tmptm1 += _timezone;
  261.  
  262.             /*
  263.              * Convert this second count back into a time block structure.
  264.              * If localtime returns NULL, return an error.
  265.              */
  266.             if ( (tbtemp = localtime(&tmptm1)) == NULL )
  267.                 goto err_mktime;
  268.  
  269.             /*
  270.              * Now must compensate for DST. The ANSI rules are to use the
  271.              * passed-in tm_isdst flag if it is non-negative. Otherwise,
  272.              * compute if DST applies. Recall that tbtemp has the time without
  273.              * DST compensation, but has set tm_isdst correctly.
  274.              */
  275.             if ( (tb->tm_isdst > 0) || ((tb->tm_isdst < 0) &&
  276.               (tbtemp->tm_isdst > 0)) ) {
  277.                 tmptm1 += _dstbias;
  278.                 tbtemp = localtime(&tmptm1);    /* reconvert, can't get NULL */
  279.             }
  280.  
  281.         }
  282.         else {
  283.             if ( (tbtemp = gmtime(&tmptm1)) == NULL )
  284.                 goto err_mktime;
  285.         }
  286.  
  287.         /***** HERE: tmptm1 holds number of elapsed seconds, adjusted *****/
  288.         /*****       for local time if requested                      *****/
  289.  
  290.         *tb = *tbtemp;
  291.         return (time_t)tmptm1;
  292.  
  293. err_mktime:
  294.         /*
  295.          * All errors come to here
  296.          */
  297.         return (time_t)(-1);
  298. }
  299.