xref: /reactos/sdk/lib/ucrt/time/gmtime.cpp (revision e3e520d1)
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