1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qlocaltime_p.h" |
5 | #include "qplatformdefs.h" |
6 | |
7 | #include "private/qcalendarmath_p.h" |
8 | #if QT_CONFIG(datetimeparser) |
9 | #include "private/qdatetimeparser_p.h" |
10 | #endif |
11 | #include "private/qgregoriancalendar_p.h" |
12 | #include "private/qnumeric_p.h" |
13 | #include "private/qtenvironmentvariables_p.h" |
14 | #if QT_CONFIG(timezone) |
15 | #include "private/qtimezoneprivate_p.h" |
16 | #endif |
17 | |
18 | #include <time.h> |
19 | #ifdef Q_OS_WIN |
20 | # include <qt_windows.h> |
21 | #endif |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | using namespace QtPrivate::DateTimeConstants; |
26 | namespace { |
27 | /* |
28 | Qt represents n BCE as -n, whereas struct tm's tm_year field represents a |
29 | year by the number of years after (negative for before) 1900, so that 1+m |
30 | BCE is -1900 -m; so treating 1 BCE as 0 CE. We thus shift by different |
31 | offsets depending on whether the year is BCE or CE. |
32 | */ |
33 | constexpr int tmYearFromQYear(int year) { return year - (year < 0 ? 1899 : 1900); } |
34 | constexpr int qYearFromTmYear(int year) { return year + (year < -1899 ? 1899 : 1900); } |
35 | |
36 | constexpr inline qint64 tmSecsWithinDay(const struct tm &when) |
37 | { |
38 | return (when.tm_hour * MINS_PER_HOUR + when.tm_min) * SECS_PER_MIN + when.tm_sec; |
39 | } |
40 | |
41 | /* If mktime() returns -1, is it really an error ? |
42 | |
43 | It might return -1 because we're looking at the last second of 1969 and |
44 | mktime does support times before 1970 (POSIX says "If the year is <1970 or |
45 | the value is negative, the relationship is undefined" and MS rejects the |
46 | value, consistent with that; so we don't call mktime() on MS in this case and |
47 | can't get -1 unless it's a real error). However, on UNIX, that's -1 UTC time |
48 | and all we know, aside from mktime's return, is the local time. (We could |
49 | check errno, but we call mktime from within a qt_scoped_lock(QBasicMutex), |
50 | whose unlocking and destruction of the locker might frob errno.) |
51 | |
52 | We can assume the zone offset is a multiple of five minutes and less than a |
53 | day, so this can only arise for the last second of a minute that differs from |
54 | 59 by a multiple of 5 on the last day of 1969 or the first day of 1970. That |
55 | makes for a cheap pre-test; if it holds, we can ask mktime about the first |
56 | second of the same minute; if it gives us -60, then the -1 we originally saw |
57 | is not an error (or was an error, but needn't have been). |
58 | */ |
59 | inline bool meansEnd1969(tm *local) |
60 | { |
61 | #ifdef Q_OS_WIN |
62 | Q_UNUSED(local); |
63 | return false; |
64 | #else |
65 | if (local->tm_sec < 59 || local->tm_year < 69 || local->tm_year > 70 |
66 | || local->tm_min % 5 != 4 // Assume zone offset is a multiple of 5 mins |
67 | || (local->tm_year == 69 |
68 | ? local->tm_mon < 11 || local->tm_mday < 31 |
69 | : local->tm_mon > 0 || local->tm_mday > 1)) { |
70 | return false; |
71 | } |
72 | tm copy = *local; |
73 | copy.tm_sec--; // Preceding second should get -2, not -1 |
74 | if (qMkTime(when: ©) != -2) |
75 | return false; |
76 | // The original call to qMkTime() may have returned -1 as failure, not |
77 | // updating local, even though it could have; so fake it here. Assumes there |
78 | // was no transition in the last minute of the day ! |
79 | *local = copy; |
80 | local->tm_sec++; // Advance back to the intended second |
81 | return true; |
82 | #endif |
83 | } |
84 | |
85 | /* |
86 | Call mktime but bypass its fixing of denormal times. |
87 | |
88 | The POSIX spec says mktime() accepts a struct tm whose fields lie outside |
89 | the usual ranges; the parameter is not const-qualified and will be updated |
90 | to have values in those ranges. However, MS's implementation doesn't do that |
91 | (or hasn't always done it); and the only member we actually want updated is |
92 | the tm_isdst flag. (Aside: MS's implementation also only works for tm_year |
93 | >= 70; this is, in fact, in accordance with the POSIX spec; but all known |
94 | UNIX libc implementations in fact have a signed time_t and Do The Sensible |
95 | Thing, to the best of their ability, at least for 0 <= tm_year < 70; see |
96 | meansEnd1969 for the handling of the last second of UTC's 1969.) |
97 | |
98 | If we thought we knew tm_isdst and mktime() disagrees, it'll let us know |
99 | either by correcting it - in which case it adjusts the struct tm to reflect |
100 | the same time, but represented using the right tm_isdst, so typically an |
101 | hour earlier or later - or by returning -1. When this happens, the way we |
102 | actually use mktime(), we don't want a revised time with corrected DST, we |
103 | want the original time with its corrected DST; so we retry the call, this |
104 | time not claiming to know the DST-ness. |
105 | |
106 | POSIX doesn't actually say what to do if the specified struct tm describes a |
107 | time in a spring-forward gap: read literally, this is an unrepresentable |
108 | time and it could return -1, setting errno to EOVERFLOW. However, actual |
109 | implementations chose a time one side or the other of the gap. For example, |
110 | if we claim to know DST, glibc pushes to the other side of the gap (changing |
111 | tm_isdst), but stays on the indicated branch of a repetition (no change to |
112 | tm_isdst); this matches how QTimeZonePrivate::dataForLocalTime() uses its |
113 | hint; in either case, if we don't claim to know DST, glibc picks the DST |
114 | candidate. (Experiments conducted with glibc 2.31-9.) |
115 | */ |
116 | inline bool callMkTime(tm *local, time_t *secs) |
117 | { |
118 | constexpr time_t maybeError = -1; // mktime()'s return on error; or last second of 1969 UTC. |
119 | const tm copy = *local; |
120 | *secs = qMkTime(when: local); |
121 | bool good = *secs != maybeError || meansEnd1969(local); |
122 | if (copy.tm_isdst >= 0 && (!good || local->tm_isdst != copy.tm_isdst)) { |
123 | // We thought we knew DST-ness, but were wrong: |
124 | *local = copy; |
125 | local->tm_isdst = -1; |
126 | *secs = qMkTime(when: local); |
127 | good = *secs != maybeError || meansEnd1969(local); |
128 | } |
129 | #if defined(Q_OS_WIN) |
130 | // Windows mktime for the missing hour backs up 1 hour instead of advancing |
131 | // 1 hour. If time differs and is standard time then this has happened, so |
132 | // add 2 hours to the time and 1 hour to the secs |
133 | if (local->tm_isdst == 0 && local->tm_hour != copy.tm_hour) { |
134 | local->tm_hour += 2; |
135 | if (local->tm_hour > 23) { |
136 | local->tm_hour -= 24; |
137 | if (++local->tm_mday > QGregorianCalendar::monthLength( |
138 | local->tm_mon + 1, qYearFromTmYear(local->tm_year))) { |
139 | local->tm_mday = 1; |
140 | if (++local->tm_mon > 11) { |
141 | local->tm_mon = 0; |
142 | ++local->tm_year; |
143 | } |
144 | } |
145 | } |
146 | *secs += 3600; |
147 | local->tm_isdst = 1; |
148 | } |
149 | #endif // Q_OS_WIN |
150 | return good; |
151 | } |
152 | |
153 | struct tm timeToTm(qint64 localDay, int secs, QDateTimePrivate::DaylightStatus dst) |
154 | { |
155 | Q_ASSERT(0 <= secs && secs < 3600 * 24); |
156 | const auto ymd = QGregorianCalendar::partsFromJulian(jd: JULIAN_DAY_FOR_EPOCH + localDay); |
157 | struct tm local = {}; |
158 | local.tm_year = tmYearFromQYear(year: ymd.year); |
159 | local.tm_mon = ymd.month - 1; |
160 | local.tm_mday = ymd.day; |
161 | local.tm_hour = secs / 3600; |
162 | local.tm_min = (secs % 3600) / 60; |
163 | local.tm_sec = (secs % 60); |
164 | local.tm_isdst = int(dst); |
165 | return local; |
166 | } |
167 | |
168 | inline std::optional<qint64> tmToJd(const struct tm &date) |
169 | { |
170 | return QGregorianCalendar::julianFromParts(year: qYearFromTmYear(year: date.tm_year), |
171 | month: date.tm_mon + 1, day: date.tm_mday); |
172 | } |
173 | |
174 | #define IC(N) std::integral_constant<qint64, N>() |
175 | |
176 | // True if combining day and seconds overflows qint64; otherwise, sets *epochSeconds |
177 | inline bool daysAndSecondsOverflow(qint64 julianDay, qint64 daySeconds, qint64 *epochSeconds) |
178 | { |
179 | return qMulOverflow(v1: julianDay - JULIAN_DAY_FOR_EPOCH, IC(SECS_PER_DAY), r: epochSeconds) |
180 | || qAddOverflow(v1: *epochSeconds, v2: daySeconds, r: epochSeconds); |
181 | } |
182 | |
183 | // True if combining seconds and millis overflows; otherwise sets *epochMillis |
184 | inline bool secondsAndMillisOverflow(qint64 epochSeconds, qint64 millis, qint64 *epochMillis) |
185 | { |
186 | return qMulOverflow(v1: epochSeconds, IC(MSECS_PER_SEC), r: epochMillis) |
187 | || qAddOverflow(v1: *epochMillis, v2: millis, r: epochMillis); |
188 | } |
189 | |
190 | #undef IC |
191 | |
192 | } // namespace |
193 | |
194 | namespace QLocalTime { |
195 | |
196 | #ifndef QT_BOOTSTRAPPED |
197 | // Even if local time is currently in DST, this returns the standard time offset |
198 | // (in seconds) nominally in effect at present: |
199 | int getCurrentStandardUtcOffset() |
200 | { |
201 | #ifdef Q_OS_WIN |
202 | TIME_ZONE_INFORMATION tzInfo; |
203 | if (GetTimeZoneInformation(&tzInfo) != TIME_ZONE_ID_INVALID) { |
204 | int bias = tzInfo.Bias; // In minutes. |
205 | // StandardBias is usually zero, but include it if given: |
206 | if (tzInfo.StandardDate.wMonth) // Zero month means ignore StandardBias. |
207 | bias += tzInfo.StandardBias; |
208 | // MS's bias is +ve in the USA, so minutes *behind* UTC - we want seconds *ahead*: |
209 | return -bias * SECS_PER_MIN; |
210 | } |
211 | #else |
212 | qTzSet(); |
213 | const time_t curr = time(timer: nullptr); |
214 | if (curr != -1) { |
215 | /* Set t to the UTC representation of curr; the time whose local |
216 | standard time representation coincides with that differs from curr by |
217 | local time's standard offset. Note that gmtime() leaves the tm_isdst |
218 | flag set to 0, so mktime() will, even if local time is currently |
219 | using DST, return the time since epoch at which local standard time |
220 | would have the same representation as UTC's representation of |
221 | curr. The fact that mktime() also flips tm_isdst and updates the time |
222 | fields to the DST-equivalent time needn't concern us here; all that |
223 | matters is that it returns the time after epoch at which standard |
224 | time's representation would have matched UTC's, had it been in |
225 | effect. |
226 | */ |
227 | # if defined(_POSIX_THREAD_SAFE_FUNCTIONS) |
228 | struct tm t; |
229 | if (gmtime_r(timer: &curr, tp: &t)) { |
230 | time_t mkt = qMkTime(when: &t); |
231 | int offset = int(curr - mkt); |
232 | Q_ASSERT(std::abs(offset) <= SECS_PER_DAY); |
233 | return offset; |
234 | } |
235 | # else |
236 | if (struct tm *tp = gmtime(&curr)) { |
237 | struct tm t = *tp; // Copy it quick, hopefully before it can get stomped |
238 | time_t mkt = qMkTime(&t); |
239 | int offset = int(curr - mkt); |
240 | Q_ASSERT(std::abs(offset) <= SECS_PER_DAY); |
241 | return offset; |
242 | } |
243 | # endif |
244 | } // else, presumably: errno == EOVERFLOW |
245 | #endif // Platform choice |
246 | qDebug(msg: "Unable to determine current standard time offset from UTC" ); |
247 | // We can't tell, presume UTC. |
248 | return 0; |
249 | } |
250 | |
251 | // This is local time's offset (in seconds), at the specified time, including |
252 | // any DST part. |
253 | int getUtcOffset(qint64 atMSecsSinceEpoch) |
254 | { |
255 | return QDateTimePrivate::expressUtcAsLocal(utcMSecs: atMSecsSinceEpoch).offset; |
256 | } |
257 | #endif // QT_BOOTSTRAPPED |
258 | |
259 | // Calls the platform variant of localtime() for the given utcMillis, and |
260 | // returns the local milliseconds, offset from UTC and DST status. |
261 | QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis) |
262 | { |
263 | const auto epoch = QRoundingDown::qDivMod<MSECS_PER_SEC>(a: utcMillis); |
264 | const time_t epochSeconds = epoch.quotient; |
265 | const int msec = epoch.remainder; |
266 | Q_ASSERT(msec >= 0 && msec < MSECS_PER_SEC); |
267 | if (qint64(epochSeconds) * MSECS_PER_SEC + msec != utcMillis) // time_t range too narrow |
268 | return {utcMillis}; |
269 | |
270 | tm local; |
271 | if (!qLocalTime(utc: epochSeconds, local: &local)) |
272 | return {utcMillis}; |
273 | |
274 | auto jd = tmToJd(date: local); |
275 | if (Q_UNLIKELY(!jd)) |
276 | return {utcMillis}; |
277 | |
278 | const qint64 daySeconds = tmSecsWithinDay(when: local); |
279 | Q_ASSERT(0 <= daySeconds && daySeconds < SECS_PER_DAY); |
280 | qint64 localSeconds, localMillis; |
281 | if (Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySeconds, &localSeconds) |
282 | || secondsAndMillisOverflow(localSeconds, qint64(msec), &localMillis))) { |
283 | return {utcMillis}; |
284 | } |
285 | const auto dst |
286 | = local.tm_isdst ? QDateTimePrivate::DaylightTime : QDateTimePrivate::StandardTime; |
287 | return { localMillis, int(localSeconds - epochSeconds), dst }; |
288 | } |
289 | |
290 | QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::DaylightStatus dst) |
291 | { |
292 | const auto localDayMilli = QRoundingDown::qDivMod<MSECS_PER_DAY>(a: local); |
293 | qint64 millis = localDayMilli.remainder; |
294 | Q_ASSERT(0 <= millis && millis < MSECS_PER_DAY); // Definition of QRD::qDiv. |
295 | struct tm tmLocal = timeToTm(localDay: localDayMilli.quotient, secs: int(millis / MSECS_PER_SEC), dst); |
296 | time_t utcSecs; |
297 | if (!callMkTime(local: &tmLocal, secs: &utcSecs)) |
298 | return {}; |
299 | return qTzName(dstIndex: tmLocal.tm_isdst > 0 ? 1 : 0); |
300 | } |
301 | |
302 | QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::DaylightStatus dst) |
303 | { |
304 | qint64 localSecs = local / MSECS_PER_SEC; |
305 | qint64 millis = local - localSecs * MSECS_PER_SEC; // 0 or with same sign as local |
306 | const auto localDaySec = QRoundingDown::qDivMod<SECS_PER_DAY>(a: localSecs); |
307 | qint64 daySecs = localDaySec.remainder; |
308 | Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY); // Definition of QRD::qDiv. |
309 | |
310 | struct tm tmLocal = timeToTm(localDay: localDaySec.quotient, secs: daySecs, dst); |
311 | time_t utcSecs; |
312 | if (!callMkTime(local: &tmLocal, secs: &utcSecs)) |
313 | return {local}; |
314 | |
315 | // TODO: for glibc, we could use tmLocal.tm_gmtoff |
316 | // That would give us offset directly, hence localSecs = offset + utcSecs |
317 | // Provisional offset, until we have a revised localSeconds: |
318 | int offset = localSecs - utcSecs; |
319 | dst = tmLocal.tm_isdst > 0 ? QDateTimePrivate::DaylightTime : QDateTimePrivate::StandardTime; |
320 | auto jd = tmToJd(date: tmLocal); |
321 | if (Q_UNLIKELY(!jd)) |
322 | return {local, offset, dst, false}; |
323 | |
324 | daySecs = tmSecsWithinDay(when: tmLocal); |
325 | Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY); |
326 | if (daySecs > 0 && *jd < JULIAN_DAY_FOR_EPOCH) { |
327 | jd = *jd + 1; |
328 | daySecs -= SECS_PER_DAY; |
329 | } |
330 | if (Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySecs, &localSecs))) |
331 | return {local, offset, dst, false}; |
332 | |
333 | offset = localSecs - utcSecs; |
334 | |
335 | // The only way localSecs and millis can now have opposite sign is for |
336 | // resolution of the local time to have kicked us across the epoch, in which |
337 | // case there's no danger of overflow. So if overflow is in danger of |
338 | // happening, we're already doing the best we can to avoid it. |
339 | qint64 revised; |
340 | const bool overflow = secondsAndMillisOverflow(epochSeconds: localSecs, millis, epochMillis: &revised); |
341 | return {overflow ? local : revised, offset, dst, !overflow}; |
342 | } |
343 | |
344 | /*! |
345 | \internal |
346 | Determine the range of the system time_t functions. |
347 | |
348 | On MS-systems (where time_t is 64-bit by default), the start-point is the |
349 | epoch, the end-point is the end of the year 3000 (for mktime(); for |
350 | _localtime64_s it's 18 days later, but we ignore that here). Darwin's range |
351 | runs from the beginning of 1900 to the end of its 64-bit time_t and Linux |
352 | uses the full range of time_t (but this might still be 32-bit on some |
353 | embedded systems). |
354 | |
355 | (One potential constraint might appear to be the range of struct tm's int |
356 | tm_year, only allowing time_t to represent times from the start of year |
357 | 1900+INT_MIN to the end of year INT_MAX. The 26-bit number of seconds in a |
358 | year means that a 64-bit time_t can indeed represent times outside the range |
359 | of 32-bit years, by a factor of 32 - but the range of representable |
360 | milliseconds needs ten more bits than that of seconds, so can't reach the |
361 | ends of the 32-bit year range.) |
362 | |
363 | Given the diversity of ranges, we conservatively estimate the actual |
364 | supported range by experiment on the first call to qdatetime.cpp's |
365 | millisInSystemRange() by exploration among the known candidates, converting |
366 | the result to milliseconds and flagging whether each end is the qint64 |
367 | range's bound (so millisInSystemRange will know not to try to pad beyond |
368 | those bounds). The probed date-times are somewhat inside the range, but |
369 | close enough to the relevant bound that we can be fairly sure the bound is |
370 | reached, if the probe succeeds. |
371 | */ |
372 | SystemMillisRange computeSystemMillisRange() |
373 | { |
374 | // Assert this here, as this is called just once, in a static initialization. |
375 | Q_ASSERT(QGregorianCalendar::julianFromParts(1970, 1, 1) == JULIAN_DAY_FOR_EPOCH); |
376 | |
377 | constexpr qint64 TIME_T_MAX = std::numeric_limits<time_t>::max(); |
378 | using Bounds = std::numeric_limits<qint64>; |
379 | constexpr bool isNarrow = Bounds::max() / MSECS_PER_SEC > TIME_T_MAX; |
380 | if constexpr (isNarrow) { |
381 | const qint64 msecsMax = quint64(TIME_T_MAX) * MSECS_PER_SEC - 1 + MSECS_PER_SEC; |
382 | const qint64 msecsMin = -1 - msecsMax; // TIME_T_MIN is -1 - TIME_T_MAX |
383 | // If we reach back to msecsMin, use it; otherwise, assume 1970 cut-off (MS). |
384 | struct tm local = {}; |
385 | local.tm_year = tmYearFromQYear(year: 1901); |
386 | local.tm_mon = 11; |
387 | local.tm_mday = 15; // A day and a bit after the start of 32-bit time_t: |
388 | local.tm_isdst = -1; |
389 | return {.min: qMkTime(when: &local) == -1 ? 0 : msecsMin, .max: msecsMax, .minClip: false, .maxClip: false}; |
390 | } else { |
391 | const struct { int year; qint64 millis; } starts[] = { |
392 | { .year: int(QDateTime::YearRange::First) + 1, .millis: Bounds::min() }, |
393 | // Beginning of the Common Era: |
394 | { .year: 1, .millis: -Q_INT64_C(62135596800000) }, |
395 | // Invention of the Gregorian calendar: |
396 | { .year: 1582, .millis: -Q_INT64_C(12244089600000) }, |
397 | // Its adoption by the anglophone world: |
398 | { .year: 1752, .millis: -Q_INT64_C(6879427200000) }, |
399 | // Before this, struct tm's tm_year is negative (Darwin): |
400 | { .year: 1900, .millis: -Q_INT64_C(2208988800000) }, |
401 | }, ends[] = { |
402 | { .year: int(QDateTime::YearRange::Last) - 1, .millis: Bounds::max() }, |
403 | // MS's end-of-range, end of year 3000: |
404 | { .year: 3000, Q_INT64_C(32535215999999) }, |
405 | }; |
406 | // Assume we do at least reach the end of a signed 32-bit time_t (since |
407 | // our actual time_t is bigger than that): |
408 | qint64 stop = |
409 | quint64(std::numeric_limits<qint32>::max()) * MSECS_PER_SEC - 1 + MSECS_PER_SEC; |
410 | // Cleared if first pass round loop fails: |
411 | bool stopMax = true; |
412 | for (const auto c : ends) { |
413 | struct tm local = {}; |
414 | local.tm_year = tmYearFromQYear(year: c.year); |
415 | local.tm_mon = 11; |
416 | local.tm_mday = 31; |
417 | local.tm_hour = 23; |
418 | local.tm_min = local.tm_sec = 59; |
419 | local.tm_isdst = -1; |
420 | if (qMkTime(when: &local) != -1) { |
421 | stop = c.millis; |
422 | break; |
423 | } |
424 | stopMax = false; |
425 | } |
426 | bool startMin = true; |
427 | for (const auto c : starts) { |
428 | struct tm local {}; |
429 | local.tm_year = tmYearFromQYear(year: c.year); |
430 | local.tm_mon = 1; |
431 | local.tm_mday = 1; |
432 | local.tm_isdst = -1; |
433 | if (qMkTime(when: &local) != -1) |
434 | return {.min: c.millis, .max: stop, .minClip: startMin, .maxClip: stopMax}; |
435 | startMin = false; |
436 | } |
437 | return {.min: 0, .max: stop, .minClip: false, .maxClip: stopMax}; |
438 | } |
439 | } |
440 | |
441 | } // QLocalTime |
442 | |
443 | QT_END_NAMESPACE |
444 | |