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