Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlocaltime.cpp
Go to the documentation of this file.
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#ifdef __GLIBC__ // Extends struct tm with some extra fields:
24#define HAVE_TM_GMTOFF // tm_gmtoff is the UTC offset.
25#define HAVE_TM_ZONE // tm_zone is the zone abbreviation.
26#endif
27
29
30using namespace QtPrivate::DateTimeConstants;
31namespace {
32/*
33 Qt represents n BCE as -n, whereas struct tm's tm_year field represents a
34 year by the number of years after (negative for before) 1900, so that 1+m
35 BCE is -1900 -m; so treating 1 BCE as 0 CE. We thus shift by different
36 offsets depending on whether the year is BCE or CE.
37*/
38constexpr int tmYearFromQYear(int year) { return year - (year < 0 ? 1899 : 1900); }
39constexpr int qYearFromTmYear(int year) { return year + (year < -1899 ? 1899 : 1900); }
40
41constexpr inline qint64 tmSecsWithinDay(const struct tm &when)
42{
43 return (when.tm_hour * MINS_PER_HOUR + when.tm_min) * SECS_PER_MIN + when.tm_sec;
44}
45
46/* Call mktime() and make sense of the result.
47
48 This packages the call to mktime() with the needed determination of whether
49 that succeeded and whether the call has materially perturbed, including
50 normalizing, the struct tm it was passed (as opposed to merely filling in
51 details).
52*/
53class MkTimeResult
54{
55 // mktime()'s return on error; or last second of 1969 UTC:
56 static constexpr time_t maybeError = -1;
57 inline bool meansEnd1969();
58 bool changed(const struct tm &prior) const;
59
60public:
61 struct tm local = {}; // Describes the local time in familiar form.
62 time_t utcSecs = maybeError; // Seconds since UTC epoch.
63 bool good = false; // Ignore the rest unless this is true.
64 bool adjusted = true; // Is local at odds with prior ?
65 MkTimeResult() { local.tm_isdst = -1; }
66
67 // Note: the calls to qMkTime() and meansEnd1969() potentially modify local.
68 explicit MkTimeResult(const struct tm &prior)
69 : local(prior), utcSecs(qMkTime(&local)),
70 good(utcSecs != maybeError || meansEnd1969()),
71 adjusted(changed(prior))
72 {}
73};
74
75/* If mktime() returns -1, is it really an error ?
76
77 It might return -1 because we're looking at the last second of 1969 and
78 mktime does support times before 1970 (POSIX says "If the year is <1970 or
79 the value is negative, the relationship is undefined" and MS rejects the
80 value, consistent with that; so we don't call mktime() on MS in this case and
81 can't get -1 unless it's a real error). However, on UNIX, that's -1 UTC time
82 and all we know, aside from mktime's return, is the local time. (We could
83 check errno, but we call mktime from within a qt_scoped_lock(QBasicMutex),
84 whose unlocking and destruction of the locker might frob errno.)
85
86 We can assume time-zone offsets are less than a day, so this can only arise
87 if the struct tm describes either the last day of 1969 or the first day of
88 1970. When we do know the offset (a glibc extension supplies it as a member
89 of struct tm), we can determine whether we're on the last second of the day,
90 refining that check. That makes for a cheap pre-test; if it holds, we can ask
91 mktime() about the preceding second; if it gives us -2, then the -1 we
92 originally saw is not (or at least didn't need to be) an error. We can then
93 synthesize a corrected value for local using the -2 result.
94*/
95inline bool MkTimeResult::meansEnd1969()
96{
97#ifdef Q_OS_WIN
98 return false;
99#else
100 if (local.tm_year < 69 || local.tm_year > 70
101# ifdef HAVE_TM_GMTOFF
102 // Africa/Monrovia had offset 00:44:30 at the epoch, so (although all
103 // other zones' offsets were round multiples of five minutes) we need
104 // the offset to determine whether the time might match:
105 || (tmSecsWithinDay(local) - local.tm_gmtoff + 1) % SECS_PER_DAY
106# endif
107 || (local.tm_year == 69 // ... and less than a day:
108 ? local.tm_mon < 11 || local.tm_mday < 31
109 : local.tm_mon > 0 || local.tm_mday > 1)) {
110 return false;
111 }
112 struct tm copy = local;
113 copy.tm_sec--; // Preceding second should get -2, not -1
114 if (qMkTime(&copy) != -2)
115 return false;
116 // The original call to qMkTime() may have returned -1 as failure, not
117 // updating local, even though it could have; so fake it here. Assumes there
118 // was no transition in the last minute of the day !
119 local = copy;
120 local.tm_sec++; // Advance back to the intended second
121 return true;
122#endif
123}
124
125bool MkTimeResult::changed(const struct tm &prior) const
126{
127 // If mktime() has been passed a copy of prior and local is its value on
128 // return, this checks whether mktime() has made a material change
129 // (including normalization) to the value, as opposed to merely filling in
130 // the fields that it's specified to fill in. It returns true if there has
131 // been any material change.
132 return !(prior.tm_year == local.tm_year && prior.tm_mon == local.tm_mon
133 && prior.tm_mday == local.tm_mday && prior.tm_hour == local.tm_hour
134 && prior.tm_min == local.tm_min && prior.tm_sec == local.tm_sec
135 && (prior.tm_isdst == -1
136 ? local.tm_isdst >= 0 : prior.tm_isdst == local.tm_isdst));
137}
138
139struct tm timeToTm(qint64 localDay, int secs)
140{
141 Q_ASSERT(0 <= secs && secs < SECS_PER_DAY);
143 struct tm local = {};
144 local.tm_year = tmYearFromQYear(ymd.year);
145 local.tm_mon = ymd.month - 1;
146 local.tm_mday = ymd.day;
147 local.tm_hour = secs / 3600;
148 local.tm_min = (secs % 3600) / 60;
149 local.tm_sec = (secs % 60);
150 local.tm_isdst = -1;
151 return local;
152}
153
154// Transitions account for a small fraction of 1% of the time.
155// So mark functions only used in handling them as cold.
157struct tm matchYearMonth(struct tm when, const struct tm &base)
158{
159 // Adjust *when to be a denormal representation of the same point in time
160 // but with tm_year and tm_mon the same as base. In practice this will
161 // represent an adjacent month, so don't worry too much about optimising for
162 // any other case; we almost certainly run zero or one iteration of one of
163 // the year loops then zero or one iteration of one of the month loops.
164 while (when.tm_year > base.tm_year) {
165 --when.tm_year;
166 when.tm_mon += 12;
167 }
168 while (when.tm_year < base.tm_year) {
169 ++when.tm_year;
170 when.tm_mon -= 12;
171 }
172 Q_ASSERT(when.tm_year == base.tm_year);
173 while (when.tm_mon > base.tm_mon) {
174 const auto yearMon = QRoundingDown::qDivMod<12>(when.tm_mon);
175 int year = yearMon.quotient;
176 // We want the month before's Qt month number, which is the tm_mon mod 12:
177 int month = yearMon.remainder;
178 if (month == 0) {
179 --year;
180 month = 12;
181 }
182 year += when.tm_year;
183 when.tm_mday += QGregorianCalendar::monthLength(month, qYearFromTmYear(year));
184 --when.tm_mon;
185 }
186 while (when.tm_mon < base.tm_mon) {
187 const auto yearMon = QRoundingDown::qDivMod<12>(when.tm_mon);
188 // Qt month number is offset from tm_mon by one:
189 when.tm_mday -= QGregorianCalendar::monthLength(
190 yearMon.remainder + 1, qYearFromTmYear(yearMon.quotient + when.tm_year));
191 ++when.tm_mon;
192 }
193 Q_ASSERT(when.tm_mon == base.tm_mon);
194 return when;
195}
196
198struct tm adjacentDay(struct tm when, int dayStep)
199{
200 // Before we adjust it, when is a return from timeToTm(), so in normal form.
201 Q_ASSERT(dayStep * dayStep == 1);
202 when.tm_mday += dayStep;
203 // That may have bumped us across a month boundary or even a year one.
204 // So now we normalize it.
205
206 if (dayStep < 0) {
207 if (when.tm_mday <= 0) {
208 // Month before's day-count; but tm_mon's value is one less than Qt's
209 // month numbering so, before we decrement it, it has the value we need,
210 // unless it's 0.
211 int daysInMonth = when.tm_mon
212 ? QGregorianCalendar::monthLength(when.tm_mon, qYearFromTmYear(when.tm_year))
213 : QGregorianCalendar::monthLength(12, qYearFromTmYear(when.tm_year - 1));
214 when.tm_mday += daysInMonth;
215 if (--when.tm_mon < 0) {
216 --when.tm_year;
217 when.tm_mon = 11;
218 }
219 Q_ASSERT(when.tm_mday >= 1);
220 }
221 } else if (when.tm_mday > 28) {
222 // We have to wind through months one at a time, since their lengths vary.
223 int daysInMonth = QGregorianCalendar::monthLength(
224 when.tm_mon + 1, qYearFromTmYear(when.tm_year));
225 if (when.tm_mday > daysInMonth) {
226 when.tm_mday -= daysInMonth;
227 if (++when.tm_mon > 11) {
228 ++when.tm_year;
229 when.tm_mon = 0;
230 }
232 when.tm_mon + 1, qYearFromTmYear(when.tm_year)));
233 }
234 }
235 return when;
236}
237
239qint64 secondsBetween(const struct tm &start, const struct tm &stop)
240{
241 // Nominal difference between start and stop, in seconds (negative if start
242 // is after stop); may differ from actual UTC difference if there's a
243 // transition between them.
244 struct tm from = matchYearMonth(start, stop);
245 qint64 diff = stop.tm_mday - from.tm_mday; // in days
246 diff = diff * 24 + stop.tm_hour - from.tm_hour; // in hours
247 diff = diff * 60 + stop.tm_min - from.tm_min; // in minutes
248 return diff * 60 + stop.tm_sec - from.tm_sec; // in seconds
249}
250
252MkTimeResult hopAcrossGap(const MkTimeResult &outside, const struct tm &base)
253{
254 // base fell in a gap; outside is one resolution
255 // This returns the other resolution, if possible.
256 const qint64 shift = secondsBetween(outside.local, base);
257 struct tm across;
258 // Shift is the nominal time adjustment between outside and base; now obtain
259 // the actual time that far from outside:
260 if (qLocalTime(outside.utcSecs + shift, &across)) {
261 const qint64 wider = secondsBetween(outside.local, across);
262 // That should be bigger than shift (typically by a factor of two), in
263 // the same direction:
264 if (shift > 0 ? wider > shift : wider < shift) {
265 MkTimeResult result(across);
266 if (result.good && !result.adjusted)
267 return result;
268 }
269 }
270 // This can surely only arise if the other resolution lies outside the
271 // time_t-range supported by the system functions.
272 return {};
273}
274
276MkTimeResult resolveRejected(struct tm base, MkTimeResult result,
277 QDateTimePrivate::TransitionOptions resolve)
278{
279 // May result from a time outside the supported range of system time_t
280 // functions, or from a gap (on a platform where mktime() rejects them).
281 // QDateTime filters on times well outside the supported range, but may
282 // pass values only slightly outside the range.
283
284 // The easy case - no need to find a resolution anyway:
285 if (!resolve.testAnyFlags(QDateTimePrivate::GapMask))
286 return {};
287
288 constexpr time_t twoDaysInSeconds = 2 * 24 * 60 * 60;
289 // Bracket base, one day each side (in case the zone skipped a whole day):
290 MkTimeResult early(adjacentDay(base, -1));
291 MkTimeResult later(adjacentDay(base, +1));
292 if (!early.good || !later.good) // Assume out of range, rather than gap.
293 return {};
294
295 // OK, looks like a gap.
296 Q_ASSERT(twoDaysInSeconds + early.utcSecs > later.utcSecs);
297 result.adjusted = true;
298
299 // Extrapolate backwards from later if this option is set:
302 // Reverse DST has DST before a gap and not after:
303 if (early.local.tm_isdst == 1 && !later.local.tm_isdst)
304 beforeLater = QDateTimePrivate::GapUseAfter;
305 }
306 if (resolve.testFlag(beforeLater)) // Result will be before the gap:
307 result.utcSecs = later.utcSecs - secondsBetween(base, later.local);
308 else // Result will be after the gap:
309 result.utcSecs = early.utcSecs + secondsBetween(early.local, base);
310
311 if (!qLocalTime(result.utcSecs, &result.local)) // Abandon hope.
312 return {};
313
314 return result;
315}
316
318bool preferAlternative(QDateTimePrivate::TransitionOptions resolve,
319 // is_dst flags of incumbent and an alternative:
320 int gotDst, int altDst,
321 // True precisely if alternative selects a later UTC time:
322 bool altIsLater,
323 // True for a gap, false for a fold:
324 bool inGap)
325{
326 // If resolve has this option set, prefer the later candidate, else the earlier:
330 // gotDst and altDst are {-1: unknown, 0: standard, 1: daylight-saving}
331 // So gotDst ^ altDst is 1 precisely if exactly one candidate thinks it's DST.
332 if ((altDst ^ gotDst) == 1) {
333 // In this case, we can tell whether we have reversed DST: that's a
334 // gap with DST before it or a fold with DST after it.
335#if 1
336 const bool isReversed = (altDst == 1) != (altIsLater == inGap);
337#else // Pedagogic version of the same thing:
338 bool isReversed;
339 if (altIsLater == inGap) // alt is after a gap or before a fold, so summer-time
340 isReversed = altDst != 1; // flip if summer-time isn't DST
341 else // alt is before a gap or after a fold, so winter-time
342 isReversed = altDst == 1; // flip if winter-time is DST
343#endif
344 if (isReversed) {
345 preferLater = inGap ? QDateTimePrivate::GapUseBefore
347 }
348 } // Otherwise, we can't tell, so assume not.
349 }
350 return resolve.testFlag(preferLater) == altIsLater;
351}
352
353/*
354 Determine UTC time and offset, if possible, at a given local time.
355
356 The local time is specified as a number of seconds since the epoch (so, in
357 effect, a time_t, albeit delivered as qint64). If the specified local time
358 falls in a transition, resolve determines what to do.
359
360 If the specified local time is outside what the system time_t APIs will
361 handle, this fails.
362*/
363MkTimeResult resolveLocalTime(qint64 local, QDateTimePrivate::TransitionOptions resolve)
364{
365 const auto localDaySecs = QRoundingDown::qDivMod<SECS_PER_DAY>(local);
366 struct tm base = timeToTm(localDaySecs.quotient, localDaySecs.remainder);
367
368 // Get provisional result (correct > 99.9 % of the time):
369 MkTimeResult result(base);
370
371 // Our callers (mostly) deal with questions of being within the range that
372 // system time_t functions can handle, and timeToTm() gave us data in
373 // normalized form, so the only excuse for !good or a change to the HH:mm:ss
374 // fields (aside from being at the boundary of time_t's supported range) is
375 // that we hit a gap, although we have to handle these cases differently:
376 if (!result.good) {
377 // Rejected. The tricky case: maybe mktime() doesn't resolve gaps.
378 return resolveRejected(base, result, resolve);
379 } else if (result.local.tm_isdst < 0) {
380 // Apparently success without knowledge of whether this is DST or not.
381 // Should not happen, but that means our usual understanding of what the
382 // system is up to has gone out the window. So just let it be.
383 } else if (result.adjusted) {
384 // Shunted out of a gap.
385 if (!resolve.testAnyFlags(QDateTimePrivate::GapMask)) {
386 result = {};
387 return result;
388 }
389
390 // Try to obtain a matching point on the other side of the gap:
391 const MkTimeResult flipped = hopAcrossGap(result, base);
392 // Even if that failed, result may be the correct resolution
393
394 if (preferAlternative(resolve, result.local.tm_isdst, flipped.local.tm_isdst,
395 flipped.utcSecs > result.utcSecs, true)) {
396 // If hopAcrossGap() failed and we do need its answer, give up.
397 if (!flipped.good || flipped.adjusted)
398 return {};
399
400 // As resolution of local, flipped involves adjustment (across gap):
401 result = flipped;
402 result.adjusted = true;
403 }
405 // In fold, DST counts as before and standard as after -
406 // we may not need to check whether we're in a transition:
407 && resolve.testFlag(result.local.tm_isdst ? QDateTimePrivate::FoldUseBefore
408 : QDateTimePrivate::FoldUseAfter)) {
409 // We prefer DST or standard and got what we wanted, so we're good.
410 // As below, but we don't need to check, because we're on the side of
411 // the transition that it would select as valid, if we were near one.
412 // NB: this branch is routinely exercised, when QDT::Data::isShort()
413 // obliges us to rediscover an offsetFromUtc that ShortData has no space
414 // to store, as it does remember the DST status we got before.
415 } else {
416 // What we gave was valid. However, it might have been in a fall-back.
417 // If so, the same input but with tm_isdst flipped should also be valid.
418 struct tm copy = base;
419 copy.tm_isdst = !result.local.tm_isdst;
420 const MkTimeResult flipped(copy);
421 if (flipped.good && !flipped.adjusted) {
422 // We're in a fall-back
423 if (!resolve.testAnyFlags(QDateTimePrivate::FoldMask)) {
424 result = {};
425 return result;
426 }
427
428 // Work out which repeat to use:
429 if (preferAlternative(resolve, result.local.tm_isdst, flipped.local.tm_isdst,
430 flipped.utcSecs > result.utcSecs, false)) {
431 result = flipped;
432 }
433 } // else: not in a transition, nothing to worry about.
434 }
435 return result;
436}
437
438inline std::optional<qint64> tmToJd(const struct tm &date)
439{
440 return QGregorianCalendar::julianFromParts(qYearFromTmYear(date.tm_year),
441 date.tm_mon + 1, date.tm_mday);
442}
443
444#define IC(N) std::integral_constant<qint64, N>()
445
446// True if combining day and seconds overflows qint64; otherwise, sets *epochSeconds
447inline bool daysAndSecondsOverflow(qint64 julianDay, qint64 daySeconds, qint64 *epochSeconds)
448{
449 return qMulOverflow(julianDay - JULIAN_DAY_FOR_EPOCH, IC(SECS_PER_DAY), epochSeconds)
450 || qAddOverflow(*epochSeconds, daySeconds, epochSeconds);
451}
452
453// True if combining seconds and millis overflows; otherwise sets *epochMillis
454inline bool secondsAndMillisOverflow(qint64 epochSeconds, qint64 millis, qint64 *epochMillis)
455{
456 return qMulOverflow(epochSeconds, IC(MSECS_PER_SEC), epochMillis)
457 || qAddOverflow(*epochMillis, millis, epochMillis);
458}
459
460#undef IC
461
462} // namespace
463
464namespace QLocalTime {
465
466#ifndef QT_BOOTSTRAPPED
467// Even if local time is currently in DST, this returns the standard time offset
468// (in seconds) nominally in effect at present:
470{
471#ifdef Q_OS_WIN
472 TIME_ZONE_INFORMATION tzInfo;
473 if (GetTimeZoneInformation(&tzInfo) != TIME_ZONE_ID_INVALID) {
474 int bias = tzInfo.Bias; // In minutes.
475 // StandardBias is usually zero, but include it if given:
476 if (tzInfo.StandardDate.wMonth) // Zero month means ignore StandardBias.
477 bias += tzInfo.StandardBias;
478 // MS's bias is +ve in the USA, so minutes *behind* UTC - we want seconds *ahead*:
479 return -bias * SECS_PER_MIN;
480 }
481#else
482 qTzSet();
483 const time_t curr = time(nullptr);
484 if (curr != -1) {
485 /* Set t to the UTC representation of curr; the time whose local
486 standard time representation coincides with that differs from curr by
487 local time's standard offset. Note that gmtime() leaves the tm_isdst
488 flag set to 0, so mktime() will, even if local time is currently
489 using DST, return the time since epoch at which local standard time
490 would have the same representation as UTC's representation of
491 curr. The fact that mktime() also flips tm_isdst and updates the time
492 fields to the DST-equivalent time needn't concern us here; all that
493 matters is that it returns the time after epoch at which standard
494 time's representation would have matched UTC's, had it been in
495 effect.
496 */
497# if defined(_POSIX_THREAD_SAFE_FUNCTIONS)
498 struct tm t;
499 if (gmtime_r(&curr, &t)) {
500 time_t mkt = qMkTime(&t);
501 int offset = int(curr - mkt);
502 Q_ASSERT(std::abs(offset) <= SECS_PER_DAY);
503 return offset;
504 }
505# else
506 if (struct tm *tp = gmtime(&curr)) {
507 struct tm t = *tp; // Copy it quick, hopefully before it can get stomped
508 time_t mkt = qMkTime(&t);
509 int offset = int(curr - mkt);
510 Q_ASSERT(std::abs(offset) <= SECS_PER_DAY);
511 return offset;
512 }
513# endif
514 } // else, presumably: errno == EOVERFLOW
515#endif // Platform choice
516 qDebug("Unable to determine current standard time offset from UTC");
517 // We can't tell, presume UTC.
518 return 0;
519}
520
521// This is local time's offset (in seconds), at the specified time, including
522// any DST part.
523int getUtcOffset(qint64 atMSecsSinceEpoch)
524{
525 return QDateTimePrivate::expressUtcAsLocal(atMSecsSinceEpoch).offset;
526}
527#endif // QT_BOOTSTRAPPED
528
529// Calls the platform variant of localtime() for the given utcMillis, and
530// returns the local milliseconds, offset from UTC and DST status.
532{
533 const auto epoch = QRoundingDown::qDivMod<MSECS_PER_SEC>(utcMillis);
534 const time_t epochSeconds = epoch.quotient;
535 const int msec = epoch.remainder;
536 Q_ASSERT(msec >= 0 && msec < MSECS_PER_SEC);
537 if (qint64(epochSeconds) * MSECS_PER_SEC + msec != utcMillis) // time_t range too narrow
538 return {utcMillis};
539
540 tm local;
541 if (!qLocalTime(epochSeconds, &local))
542 return {utcMillis};
543
544 auto jd = tmToJd(local);
545 if (Q_UNLIKELY(!jd))
546 return {utcMillis};
547
548 const qint64 daySeconds = tmSecsWithinDay(local);
549 Q_ASSERT(0 <= daySeconds && daySeconds < SECS_PER_DAY);
550 qint64 localSeconds, localMillis;
551 if (Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySeconds, &localSeconds)
552 || secondsAndMillisOverflow(localSeconds, qint64(msec), &localMillis))) {
553 return {utcMillis};
554 }
555 const auto dst
557 return { localMillis, int(localSeconds - epochSeconds), dst };
558}
559
560QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::TransitionOptions resolve)
561{
562 auto use = resolveLocalTime(QRoundingDown::qDiv<MSECS_PER_SEC>(local), resolve);
563 if (!use.good)
564 return {};
565#ifdef HAVE_TM_ZONE
566 if (use.local.tm_zone)
567 return QString::fromLocal8Bit(use.local.tm_zone);
568#endif
569 return qTzName(use.local.tm_isdst > 0 ? 1 : 0);
570}
571
572QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::TransitionOptions resolve)
573{
574 // Revised later to match what use.local tells us:
575 qint64 localSecs = local / MSECS_PER_SEC;
576 auto use = resolveLocalTime(localSecs, resolve);
577 if (!use.good)
578 return {local};
579
580 qint64 millis = local - localSecs * MSECS_PER_SEC;
581 // Division is defined to round towards zero:
582 Q_ASSERT(local < 0 ? (millis <= 0 && millis > -MSECS_PER_SEC)
583 : (millis >= 0 && millis < MSECS_PER_SEC));
584
587
588#ifdef HAVE_TM_GMTOFF
589 const int offset = use.local.tm_gmtoff;
590 localSecs = offset + use.utcSecs;
591#else
592 // Provisional offset, until we have a revised localSecs:
593 int offset = localSecs - use.utcSecs;
594 auto jd = tmToJd(use.local);
595 if (Q_UNLIKELY(!jd))
596 return {local, offset, dst, false};
597
598 qint64 daySecs = tmSecsWithinDay(use.local);
599 Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY);
600 if (daySecs > 0 && *jd < JULIAN_DAY_FOR_EPOCH) {
601 jd = *jd + 1;
602 daySecs -= SECS_PER_DAY;
603 }
604 if (Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySecs, &localSecs)))
605 return {local, offset, dst, false};
606
607 // Use revised localSecs to refine offset:
608 offset = localSecs - use.utcSecs;
609#endif // HAVE_TM_GMTOFF
610
611 // The only way localSecs and millis can now have opposite sign is for
612 // resolution of the local time to have kicked us across the epoch, in which
613 // case there's no danger of overflow. So if overflow is in danger of
614 // happening, we're already doing the best we can to avoid it.
615 qint64 revised;
616 if (secondsAndMillisOverflow(localSecs, millis, &revised))
617 return {local, offset, QDateTimePrivate::UnknownDaylightTime, false};
618 return {revised, offset, dst, true};
619}
620
650{
651 // Assert this here, as this is called just once, in a static initialization.
653
654 constexpr qint64 TIME_T_MAX = std::numeric_limits<time_t>::max();
655 using Bounds = std::numeric_limits<qint64>;
656 constexpr bool isNarrow = Bounds::max() / MSECS_PER_SEC > TIME_T_MAX;
657 if constexpr (isNarrow) {
658 const qint64 msecsMax = quint64(TIME_T_MAX) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
659 const qint64 msecsMin = -1 - msecsMax; // TIME_T_MIN is -1 - TIME_T_MAX
660 // If we reach back to msecsMin, use it; otherwise, assume 1970 cut-off (MS).
661 struct tm local = {};
662 local.tm_year = tmYearFromQYear(1901);
663 local.tm_mon = 11;
664 local.tm_mday = 15; // A day and a bit after the start of 32-bit time_t:
665 local.tm_isdst = -1;
666 return {qMkTime(&local) == -1 ? 0 : msecsMin, msecsMax, false, false};
667 } else {
668 const struct { int year; qint64 millis; } starts[] = {
669 { int(QDateTime::YearRange::First) + 1, Bounds::min() },
670 // Beginning of the Common Era:
671 { 1, -Q_INT64_C(62135596800000) },
672 // Invention of the Gregorian calendar:
673 { 1582, -Q_INT64_C(12244089600000) },
674 // Its adoption by the anglophone world:
675 { 1752, -Q_INT64_C(6879427200000) },
676 // Before this, struct tm's tm_year is negative (Darwin):
677 { 1900, -Q_INT64_C(2208988800000) },
678 }, ends[] = {
679 { int(QDateTime::YearRange::Last) - 1, Bounds::max() },
680 // MS's end-of-range, end of year 3000:
681 { 3000, Q_INT64_C(32535215999999) },
682 };
683 // Assume we do at least reach the end of a signed 32-bit time_t (since
684 // our actual time_t is bigger than that):
685 qint64 stop =
686 quint64(std::numeric_limits<qint32>::max()) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
687 // Cleared if first pass round loop fails:
688 bool stopMax = true;
689 for (const auto c : ends) {
690 struct tm local = {};
691 local.tm_year = tmYearFromQYear(c.year);
692 local.tm_mon = 11;
693 local.tm_mday = 31;
694 local.tm_hour = 23;
695 local.tm_min = local.tm_sec = 59;
696 local.tm_isdst = -1;
697 if (qMkTime(&local) != -1) {
698 stop = c.millis;
699 break;
700 }
701 stopMax = false;
702 }
703 bool startMin = true;
704 for (const auto c : starts) {
705 struct tm local {};
706 local.tm_year = tmYearFromQYear(c.year);
707 local.tm_mon = 1;
708 local.tm_mday = 1;
709 local.tm_isdst = -1;
710 if (qMkTime(&local) != -1)
711 return {c.millis, stop, startMin, stopMax};
712 startMin = false;
713 }
714 return {0, stop, false, stopMax};
715 }
716}
717
718} // QLocalTime
719
static ZoneState expressUtcAsLocal(qint64 utcMSecs)
The QGregorianCalendar class implements the Gregorian calendar.
static std::optional< qint64 > julianFromParts(int year, int month, int day)
static int monthLength(int month, int year)
static QCalendar::YearMonthDay partsFromJulian(qint64 jd)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
QDate date
[1]
QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::TransitionOptions resolve)
SystemMillisRange computeSystemMillisRange()
QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis)
QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::TransitionOptions resolve)
int getCurrentStandardUtcOffset()
int getUtcOffset(qint64 atMSecsSinceEpoch)
Combined button and popup list for selecting options.
constexpr qint64 SECS_PER_MIN
constexpr qint64 SECS_PER_DAY
constexpr qint64 JULIAN_DAY_FOR_EPOCH
constexpr qint64 MINS_PER_HOUR
constexpr qint64 MSECS_PER_SEC
static jboolean copy(JNIEnv *, jobject)
static QT_WARNING_DISABLE_FLOAT_COMPARE ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qreal threshold)
Definition qbezier.cpp:207
#define Q_UNLIKELY(x)
#define Q_DECL_COLD_FUNCTION
#define IC(N)
#define qDebug
[1]
Definition qlogging.h:164
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
std::enable_if_t< std::is_unsigned_v< T >||std::is_signed_v< T >, bool > qMulOverflow(T v1, T v2, T *r)
Definition qnumeric.h:182
GLenum GLenum dst
GLuint start
GLenum GLuint GLintptr offset
const GLubyte * c
GLfloat bias
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
bool qLocalTime(time_t utc, struct tm *local)
time_t qMkTime(struct tm *when)
void qTzSet()
QString qTzName(int dstIndex)
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60
#define Q_INT64_C(c)
Definition qtypes.h:57
static const uint base
Definition qurlidna.cpp:20