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
23QT_BEGIN_NAMESPACE
24
25using namespace QtPrivate::DateTimeConstants;
26namespace {
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*/
33constexpr int tmYearFromQYear(int year) { return year - (year < 0 ? 1899 : 1900); }
34constexpr int qYearFromTmYear(int year) { return year + (year < -1899 ? 1899 : 1900); }
35
36constexpr 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*/
59inline 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: &copy) != -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*/
116inline 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
153struct 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
168inline 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
177inline 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
184inline 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
194namespace 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:
199int 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.
253int 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.
261QDateTimePrivate::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
290QString 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
302QDateTimePrivate::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*/
372SystemMillisRange 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
443QT_END_NAMESPACE
444

source code of qtbase/src/corelib/time/qlocaltime.cpp