1 //
2 // localtime.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the localtime family of functions, which convert a time_t to a tm
7 // structure containing the local time.
8 //
9 #include <corecrt_internal_time.h>
10
11
12
13 // Converts a time_t value to a tm value containing the corresponding local time.
14 // Returns zero and updates the tm structure on success; returns nonzero and
15 // leaves the tm structure in an indeterminate state on failure.
16 //
17 // Assumptions:
18 // (1) gmtime must be called before _isindst to ensure that the tb time
19 // structure is initialized.
20 // (2) gmtime, _gtime64, localtime and _localtime64() all use a single
21 // statically allocated buffer. Each call to one of these routines
22 // destroys the contents of the previous call.
23 // (3) It is assumed that __time64_t is a 64-bit integer representing
24 // the number of seconds since 00:00:00, 01-01-70 (UTC) (i.e., the
25 // Posix/Unix Epoch. Only non-negative values are supported.
26 // (4) It is assumed that the maximum adjustment for local time is
27 // less than three days (include Daylight Savings Time adjustment).
28 // This only a concern in Posix where the specification of the TZ
29 // environment restricts the combined offset for time zone and
30 // Daylight Savings Time to 2 * (24:59:59), just under 50 hours.
31 // If any of these assumptions are violated, the behavior is undefined.
32 template <typename TimeType>
common_localtime_s(tm * const ptm,TimeType const * const ptime)33 static errno_t __cdecl common_localtime_s(
34 tm* const ptm,
35 TimeType const* const ptime
36 ) throw()
37 {
38 typedef __crt_time_time_t_traits<TimeType> time_traits;
39
40 _VALIDATE_RETURN_ERRCODE(ptm != nullptr, EINVAL);
41 memset(ptm, 0xff, sizeof(tm));
42
43 _VALIDATE_RETURN_ERRCODE(ptime != nullptr, EINVAL);
44
45 // Check for illegal time_t value:
46 _VALIDATE_RETURN_ERRCODE_NOEXC(*ptime >= 0, EINVAL);
47 _VALIDATE_RETURN_ERRCODE_NOEXC(*ptime <= time_traits::max_time_t, EINVAL);
48
49 __tzset();
50
51 int daylight = 0;
52 long dstbias = 0;
53 long timezone = 0;
54 _ERRCHECK(_get_daylight(&daylight));
55 _ERRCHECK(_get_dstbias (&dstbias ));
56 _ERRCHECK(_get_timezone(&timezone));
57
58 if (*ptime > 3 * _DAY_SEC && *ptime < time_traits::max_time_t - 3 * _DAY_SEC)
59 {
60 // The date does not fall within the first three or last three representable
61 // days; therefore, there is no possibility of overflowing or underflowing
62 // the time_t representation as we compensate for time zone and daylight
63 // savings time.
64 TimeType ltime = *ptime - timezone;
65
66 errno_t status0 = time_traits::gmtime_s(ptm, <ime);
67 if (status0 != 0)
68 return status0;
69
70 // Check and adjust for daylight savings time:
71 if (daylight && _isindst(ptm))
72 {
73 ltime -= dstbias;
74
75 errno_t const status1 = time_traits::gmtime_s(ptm, <ime);
76 if (status1 != 0)
77 return status1;
78
79 ptm->tm_isdst = 1;
80 }
81 }
82 else
83 {
84 // The date falls within the first three or last three representable days;
85 // therefore, it is possible that the time_t representation would overflow
86 // or underflow while compensating for time zone and daylight savings time.
87 // Therefore, we make the time zone and daylight savings time adjustments
88 // directly in the tm structure.
89 errno_t const status0 = time_traits::gmtime_s(ptm, ptime);
90 if (status0 != 0)
91 return status0;
92
93 TimeType ltime = static_cast<TimeType>(ptm->tm_sec);
94
95 // First, adjust for the time zone:
96 if (daylight && _isindst(ptm))
97 {
98 ltime -= (timezone + dstbias);
99 ptm->tm_isdst = 1;
100 }
101 else
102 {
103 ltime -= timezone;
104 }
105
106 ptm->tm_sec = static_cast<int>(ltime % 60);
107 if (ptm->tm_sec < 0)
108 {
109 ptm->tm_sec += 60;
110 ltime -= 60;
111 }
112
113 ltime = static_cast<TimeType>(ptm->tm_min) + ltime / 60;
114 ptm->tm_min = static_cast<int>(ltime % 60);
115 if (ptm->tm_min < 0)
116 {
117 ptm->tm_min += 60;
118 ltime -= 60;
119 }
120
121 ltime = static_cast<TimeType>(ptm->tm_hour) + ltime / 60;
122 ptm->tm_hour = static_cast<int>(ltime % 24);
123 if (ptm->tm_hour < 0)
124 {
125 ptm->tm_hour += 24;
126 ltime -=24;
127 }
128
129 ltime /= 24;
130
131 if (ltime > 0)
132 {
133 // There is no possibility of overflowing the tm_day and tm_yday
134 // members because the date can be no later than January 19.
135 ptm->tm_wday = (ptm->tm_wday + static_cast<int>(ltime)) % 7;
136 ptm->tm_mday += static_cast<int>(ltime);
137 ptm->tm_yday += static_cast<int>(ltime);
138 }
139 else if (ltime < 0)
140 {
141 // It is possible to underflow the tm_mday and tm_yday fields. If
142 // this happens, then the adjusted date must lie in December 1969:
143 ptm->tm_wday = (ptm->tm_wday + 7 + static_cast<int>(ltime)) % 7;
144 ptm->tm_mday += static_cast<int>(ltime);
145 if (ptm->tm_mday <= 0)
146 {
147 ptm->tm_mday += 31;
148
149 // Per assumption #4 above, the time zone can cause the date to
150 // underflow the epoch by more than a day.
151 ptm->tm_yday = ptm->tm_yday + static_cast<int>(ltime) + 365;
152 ptm->tm_mon = 11;
153 ptm->tm_year--;
154 }
155 else
156 {
157 ptm->tm_yday += static_cast<int>(ltime);
158 }
159 }
160 }
161
162 return 0;
163 }
164
_localtime32_s(tm * const ptm,__time32_t const * const ptime)165 extern "C" errno_t __cdecl _localtime32_s(
166 tm* const ptm,
167 __time32_t const* const ptime
168 )
169 {
170 return common_localtime_s(ptm, ptime);
171 }
172
_localtime64_s(tm * const ptm,__time64_t const * const ptime)173 extern "C" errno_t __cdecl _localtime64_s(
174 tm* const ptm,
175 __time64_t const* const ptime
176 )
177 {
178 return common_localtime_s(ptm, ptime);
179 }
180
181
182
183 // Converts a time_t value to a tm value containing the corresponding local time.
184 // Returns a pointer to the thread-local tm buffer containing the result on
185 // success; returns nullptr on failure.
186 template <typename TimeType>
187 _Success_(return != 0)
common_localtime(TimeType const * const ptime)188 static tm* __cdecl common_localtime(TimeType const* const ptime) throw()
189 {
190 typedef __crt_time_time_t_traits<TimeType> time_traits;
191
192 tm* const ptm = __getgmtimebuf();
193 if (ptm == nullptr)
194 return nullptr;
195
196 errno_t const status = time_traits::localtime_s(ptm, ptime);
197 if (status != 0)
198 return nullptr;
199
200 return ptm;
201 }
202
_localtime32(__time32_t const * const ptime)203 extern "C" tm* __cdecl _localtime32(__time32_t const* const ptime)
204 {
205 return common_localtime(ptime);
206 }
207
_localtime64(__time64_t const * const ptime)208 extern "C" tm* __cdecl _localtime64(__time64_t const* const ptime)
209 {
210 return common_localtime(ptime);
211 }
212