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