1 // 2 // gmtime.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // The gmtime() family of functions, which converts a time_t value into a tm 7 // structure in UTC. 8 // 9 #include <corecrt_internal_time.h> 10 11 12 13 // This is a utility that allows us to compute the year value differently for 14 // 32-bit and 64-bit time_t objects. 15 static int __cdecl compute_year(__time32_t& caltim, bool& is_leap_year) throw() 16 { 17 // Determine years since 1970. First, identify the four-year interval 18 // since this makes handling leap-years easy (note that 2000 IS a 19 // leap year and 2100 is out-of-range). 20 int tmptim = static_cast<int>(caltim / _FOUR_YEAR_SEC); 21 caltim -= static_cast<__time32_t>(tmptim) * _FOUR_YEAR_SEC; 22 23 // Determine which year of the interval 24 tmptim = (tmptim * 4) + 70; // 1970, 1974, 1978,...,etc. 25 26 if (caltim >= _YEAR_SEC) 27 { 28 tmptim++; // 1971, 1975, 1979,...,etc. 29 caltim -= _YEAR_SEC; 30 31 if (caltim >= _YEAR_SEC) 32 { 33 tmptim++; // 1972, 1976, 1980,...,etc. 34 caltim -= _YEAR_SEC; 35 36 // Note, it takes 366 days-worth of seconds to get past a leap 37 // year. 38 if (caltim >= (_YEAR_SEC + _DAY_SEC)) 39 { 40 tmptim++; // 1973, 1977, 1981,...,etc. 41 caltim -= (_YEAR_SEC + _DAY_SEC); 42 } 43 else 44 { 45 // In a leap year after all, set the flag. 46 is_leap_year = true; 47 } 48 } 49 } 50 51 return tmptim; 52 } 53 54 static int __cdecl compute_year(__time64_t& caltim, bool& is_leap_year) throw() 55 { 56 // Determine the years since 1900. Start by ignoring leap years: 57 int tmptim = static_cast<int>(caltim / _YEAR_SEC) + 70; 58 caltim -= static_cast<__time64_t>(tmptim - 70) * _YEAR_SEC; 59 60 // Correct for elapsed leap years: 61 caltim -= static_cast<__time64_t>(__crt_time_elapsed_leap_years(tmptim)) * _DAY_SEC; 62 63 // If we have underflowed the __time64_t range (i.e., if caltim < 0), 64 // back up one year, adjusting the correction if necessary. 65 if (caltim < 0) 66 { 67 caltim += static_cast<__time64_t>(_YEAR_SEC); 68 tmptim--; 69 if (__crt_time_is_leap_year(tmptim)) 70 { 71 caltim += _DAY_SEC; 72 is_leap_year = true; 73 } 74 } 75 else if (__crt_time_is_leap_year(tmptim)) 76 { 77 is_leap_year = true; 78 } 79 80 return tmptim; 81 } 82 83 // Converts a time_t value into a tm structure in UTC. Stores the tm structure 84 // into the '*ptm' buffer. Returns zero on success; returns an error code on 85 // failure 86 template <typename TimeType> 87 static errno_t __cdecl common_gmtime_s(tm* const ptm, TimeType const* const timp) throw() 88 { 89 typedef __crt_time_time_t_traits<__time64_t> time_traits; 90 91 _VALIDATE_RETURN_ERRCODE(ptm != nullptr, EINVAL) 92 memset(ptm, 0xff, sizeof(tm)); 93 94 _VALIDATE_RETURN_ERRCODE(timp != nullptr, EINVAL); 95 TimeType caltim = *timp; 96 97 _VALIDATE_RETURN_ERRCODE_NOEXC(caltim >= _MIN_LOCAL_TIME, EINVAL) 98 99 // Upper bound check only necessary for _gmtime64_s (it's > LONG_MAX). 100 // For _gmtime32_s, any positive number is within range (<= LONG_MAX). 101 _VALIDATE_RETURN_ERRCODE_NOEXC(caltim <= time_traits::max_time_t + _MAX_LOCAL_TIME, EINVAL) 102 103 // tmptim now holds the value for tm_year. caltim now holds the 104 // number of elapsed seconds since the beginning of that year. 105 bool is_leap_year = false; 106 ptm->tm_year = compute_year(caltim, is_leap_year); 107 108 // Determine days since January 1 (0 - 365). This is the tm_yday value. 109 // Leave caltim with number of elapsed seconds in that day. 110 ptm->tm_yday = static_cast<int>(caltim / _DAY_SEC); 111 caltim -= static_cast<TimeType>(ptm->tm_yday) * _DAY_SEC; 112 113 // Determine months since January (0 - 11) and day of month (1 - 31): 114 int const* const mdays = is_leap_year ? _lpdays : _days; 115 116 int tmptim = 0; 117 for (tmptim = 1 ; mdays[tmptim] < ptm->tm_yday ; tmptim++) 118 { 119 } 120 121 ptm->tm_mon = --tmptim; 122 123 ptm->tm_mday = ptm->tm_yday - mdays[tmptim]; 124 125 // Determine days since Sunday (0 - 6) 126 ptm->tm_wday = (static_cast<int>(*timp / _DAY_SEC) + _BASE_DOW) % 7; 127 128 // Determine hours since midnight (0 - 23), minutes after the hour 129 // (0 - 59), and seconds after the minute (0 - 59). 130 ptm->tm_hour = static_cast<int>(caltim / 3600); 131 caltim -= static_cast<TimeType>(ptm->tm_hour) * 3600L; 132 133 ptm->tm_min = static_cast<int>(caltim / 60); 134 ptm->tm_sec = static_cast<int>(caltim - (ptm->tm_min) * 60); 135 136 ptm->tm_isdst = 0; 137 return 0; 138 } 139 140 extern "C" errno_t __cdecl _gmtime32_s(tm* const result, __time32_t const* const time_value) 141 { 142 return common_gmtime_s(result, time_value); 143 } 144 145 extern "C" errno_t __cdecl _gmtime64_s(tm* const result, __time64_t const* const time_value) 146 { 147 return common_gmtime_s(result, time_value); 148 } 149 150 151 152 // Gets the thread-local buffer to be used by gmtime. Returns a pointer to the 153 // buffer on success; returns null and sets errno on failure. 154 extern "C" tm* __cdecl __getgmtimebuf() 155 { 156 __acrt_ptd* const ptd = __acrt_getptd_noexit(); 157 if (ptd == nullptr) 158 { 159 errno = ENOMEM; 160 return nullptr; 161 } 162 163 if (ptd->_gmtime_buffer != nullptr) 164 { 165 return ptd->_gmtime_buffer; 166 } 167 168 ptd->_gmtime_buffer = _malloc_crt_t(tm, 1).detach(); 169 if (ptd->_gmtime_buffer == nullptr) 170 { 171 errno = ENOMEM; 172 return nullptr; 173 } 174 175 return ptd->_gmtime_buffer; 176 } 177 178 179 // Converts a time_t value into a tm structure in UTC. Returns a pointer to a 180 // thread-local buffer containing the tm structure on success; returns null on 181 // failure. 182 template <typename TimeType> 183 _Success_(return != 0) 184 static tm* __cdecl common_gmtime(TimeType const* const time_value) throw() 185 { 186 tm* const ptm = __getgmtimebuf(); 187 if (ptm == nullptr) 188 return nullptr; 189 190 if (common_gmtime_s(ptm, time_value) != 0) 191 return nullptr; 192 193 return ptm; 194 } 195 196 extern "C" tm* __cdecl _gmtime32(__time32_t const* const time_value) 197 { 198 return common_gmtime(time_value); 199 } 200 201 extern "C" tm* __cdecl _gmtime64(__time64_t const* const time_value) 202 { 203 return common_gmtime(time_value); 204 } 205