// // mktime.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // // The mktime and mkgmtime families of functions, which convert a time value in // a (possibly incomplete) tm structure into a time_t value, then update all of // the tm structure fields with the "normalized" values. // #include // ChkAdd evaluates to TRUE if dest = src1 + src2 has overflowed #define ChkAdd(dest, src1, src2) \ (((src1 >= 0L) && (src2 >= 0L) && (dest < 0L)) || \ ((src1 < 0L) && (src2 < 0L) && (dest >= 0L))) // ChkMul evaluates to TRUE if dest = src1 * src2 has overflowed #define ChkMul(dest, src1, src2) ( src1 ? (dest / src1 != src2) : 0 ) // The implementation of the _mktime and _mkgmtime functions. If 'use_local_time' // is true, the time is assumed to be in local time; otherwise, the time is // assumed to be in UTC. template static TimeType __cdecl common_mktime( tm* const tb, bool const use_local_time ) throw() { typedef __crt_time_time_t_traits time_traits; TimeType const invalid_time = static_cast(-1); _VALIDATE_RETURN(tb != nullptr, EINVAL, invalid_time) TimeType tmptm1, tmptm2, tmptm3; // First, make sure tm_year is reasonably close to being in range. if ((tmptm1 = tb->tm_year) < _BASE_YEAR - 1 || tmptm1 > time_traits::max_year + 1) return (errno = EINVAL), invalid_time; // Adjust month value so it is in the range 0 - 11. This is because // we don't know how many days are in months 12, 13, 14, etc. if (tb->tm_mon < 0 || tb->tm_mon > 11) { // No danger of overflow because the range check above. tmptm1 += (tb->tm_mon / 12); if ((tb->tm_mon %= 12) < 0) { tb->tm_mon += 12; --tmptm1; } // Make sure year count is still in range. if (tmptm1 < _BASE_YEAR - 1 || tmptm1 > time_traits::max_year + 1) return (errno = EINVAL), invalid_time; } // HERE: tmptm1 holds number of elapsed years // Calculate days elapsed minus one, in the given year, to the given // month. Check for leap year and adjust if necessary. tmptm2 = _days[tb->tm_mon]; if (__crt_time_is_leap_year(tmptm1) && tb->tm_mon > 1) ++tmptm2; // Calculate elapsed days since base date (midnight, 1/1/70, UTC) // // 365 days for each elapsed year since 1970, plus one more day for // each elapsed leap year. no danger of overflow because of the range // check (above) on tmptm1. tmptm3 = (tmptm1 - _BASE_YEAR) * 365 + __crt_time_elapsed_leap_years(tmptm1); // Elapsed days to current month (still no possible overflow) tmptm3 += tmptm2; // Elapsed days to current date. overflow is now possible. tmptm1 = tmptm3 + (tmptm2 = static_cast(tb->tm_mday)); _VALIDATE_RETURN_NOEXC(!ChkAdd(tmptm1, tmptm3, tmptm2), EINVAL, invalid_time) // HERE: tmptm1 holds number of elapsed days // Calculate elapsed hours since base date tmptm2 = tmptm1 * 24; _VALIDATE_RETURN_NOEXC(!ChkMul(tmptm2, tmptm1, 24), EINVAL, invalid_time) tmptm1 = tmptm2 + (tmptm3 = static_cast(tb->tm_hour)); _VALIDATE_RETURN_NOEXC(!ChkAdd(tmptm1, tmptm2, tmptm3), EINVAL, invalid_time) // HERE: tmptm1 holds number of elapsed hours // Calculate elapsed minutes since base date tmptm2 = tmptm1 * 60; _VALIDATE_RETURN_NOEXC(!ChkMul(tmptm2, tmptm1, 60), EINVAL, invalid_time) tmptm1 = tmptm2 + (tmptm3 = static_cast(tb->tm_min)); _VALIDATE_RETURN_NOEXC(!ChkAdd(tmptm1, tmptm2, tmptm3), EINVAL, invalid_time) // HERE: tmptm1 holds number of elapsed minutes // Calculate elapsed seconds since base date tmptm2 = tmptm1 * 60L; _VALIDATE_RETURN_NOEXC(!ChkMul(tmptm2, tmptm1, 60L), EINVAL, invalid_time) tmptm1 = tmptm2 + (tmptm3 = static_cast(tb->tm_sec)); _VALIDATE_RETURN_NOEXC(!ChkAdd(tmptm1, tmptm2, tmptm3), EINVAL, invalid_time) // HERE: tmptm1 holds number of elapsed seconds tm tbtemp; if (use_local_time) { // Adjust for timezone. No need to check for overflow since // localtime() will check its arg value __tzset(); long dstbias = 0; long timezone = 0; _ERRCHECK(_get_dstbias(&dstbias)); _ERRCHECK(_get_timezone(&timezone)); tmptm1 += timezone; // Convert this second count back into a time block structure. // If localtime returns nullptr, return an error. if (time_traits::localtime_s(&tbtemp, &tmptm1) != 0) return (errno = EINVAL), invalid_time; // Now must compensate for DST. The ANSI rules are to use the passed-in // tm_isdst flag if it is non-negative. Otherwise, compute if DST // applies. Recall that tbtemp has the time without DST compensation, // but has set tm_isdst correctly. if (tb->tm_isdst > 0 || (tb->tm_isdst < 0 && tbtemp.tm_isdst > 0)) { tmptm1 += dstbias; if (time_traits::localtime_s(&tbtemp, &tmptm1) != 0) return (errno = EINVAL), invalid_time; } } else { if (time_traits::gmtime_s(&tbtemp, &tmptm1) != 0) return (errno = EINVAL), invalid_time; } // HERE: tmptm1 holds number of elapsed seconds, adjusted for local time if // requested *tb = tbtemp; return tmptm1; } // Converts a tm structure value into a time_t value. These functions also // update the tm structure to normalize it and populate any missing fields. // There are three practical uses for these functions: // // (1) To convert a broken-down time to the internal time format (time_t) // (2) To complete the tm value with the correct tm_wday, tm_yday, or tm_isdst // values given the rest of the contents of the tm. // (3) To pass in a time structure with "out of range" values for some fields // and get back a "normalized" tm structure (e.g., to pass in 1/35/1987 // and get back 2/4/1987). // // Returns the resulting time_t value on success; returns -1 on failure. extern "C" __time32_t __cdecl _mktime32(tm* const tb) { return common_mktime<__time32_t>(tb, true); } extern "C" __time64_t __cdecl _mktime64(tm* const tb) { return common_mktime<__time64_t>(tb, true); } // Converts a UTC time stored in a tm structure into a time_t value. These // functions also update the tm structure to normalize it and populate any // missing fields. Returns the resulting time_t value on success; returns // -1 on failure. extern "C" __time32_t __cdecl _mkgmtime32(tm* const tb) { return common_mktime<__time32_t>(tb, false); } extern "C" __time64_t __cdecl _mkgmtime64(tm* const tb) { return common_mktime<__time64_t>(tb, false); }