xref: /reactos/sdk/lib/ucrt/time/localtime.cpp (revision 04e0dc4a)
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, &ltime);
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, &ltime);
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