6#include "qplatformdefs.h"
8#include "private/qcalendarmath_p.h"
9#if QT_CONFIG(datetimeparser)
10#include "private/qdatetimeparser_p.h"
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"
19#include <QtCore/q20utility.h>
23# include <qt_windows.h>
33using namespace QtPrivate::DateTimeConstants;
36
37
38
39
40
41constexpr int tmYearFromQYear(
int year) {
return year - (year < 0 ? 1899 : 1900); }
42constexpr int qYearFromTmYear(
int year) {
return year + (year < -1899 ? 1899 : 1900); }
44constexpr inline qint64 tmSecsWithinDay(
const struct tm &when)
46 return (when.tm_hour * MINS_PER_HOUR + when.tm_min) * SECS_PER_MIN + when.tm_sec;
50
51
52
53
54
55
59 static constexpr time_t maybeError = -1;
60 inline bool meansEnd1969();
61 bool changed(
const struct tm &prior)
const;
65 time_t utcSecs = maybeError;
68 MkTimeResult() { local.tm_isdst = -1; }
71 explicit MkTimeResult(
const struct tm &prior)
72 : local(prior), utcSecs(qMkTime(&local)),
73 good(utcSecs != maybeError || meansEnd1969()),
74 adjusted(changed(prior))
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98inline bool MkTimeResult::meansEnd1969()
103 if (local.tm_year < 69 || local.tm_year > 70
104# ifdef HAVE_TM_GMTOFF
108 || (tmSecsWithinDay(local) - local.tm_gmtoff + 1) % SECS_PER_DAY
110 || (local.tm_year == 69
111 ? local.tm_mon < 11 || local.tm_mday < 31
112 : local.tm_mon > 0 || local.tm_mday > 1)) {
115 struct tm copy = local;
117 if (qMkTime(©) != -2)
128bool MkTimeResult::changed(
const struct tm &prior)
const
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));
142struct tm timeToTm(qint64 localDay,
int secs)
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);
160struct tm matchYearMonth(
struct tm when,
const struct tm &base)
167 while (when.tm_year > base.tm_year) {
171 while (when.tm_year < base.tm_year) {
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;
180 int month = yearMon.remainder;
185 year += when.tm_year;
186 when.tm_mday += QGregorianCalendar::monthLength(month, qYearFromTmYear(year));
189 while (when.tm_mon < base.tm_mon) {
190 const auto yearMon = QRoundingDown::qDivMod<12>(when.tm_mon);
192 when.tm_mday -= QGregorianCalendar::monthLength(
193 yearMon.remainder + 1, qYearFromTmYear(yearMon.quotient + when.tm_year));
196 Q_ASSERT(when.tm_mon == base.tm_mon);
201struct tm adjacentDay(
struct tm when,
int dayStep)
204 Q_ASSERT(dayStep * dayStep == 1);
205 when.tm_mday += dayStep;
210 if (when.tm_mday <= 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) {
222 Q_ASSERT(when.tm_mday >= 1);
224 }
else if (when.tm_mday > 28) {
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) {
234 Q_ASSERT(when.tm_mday <= QGregorianCalendar::monthLength(
235 when.tm_mon + 1, qYearFromTmYear(when.tm_year)));
242qint64 secondsBetween(
const struct tm &start,
const struct tm &stop)
247 struct tm from = matchYearMonth(start, stop);
248 qint64 diff = stop.tm_mday - from.tm_mday;
249 diff = diff * 24 + stop.tm_hour - from.tm_hour;
250 diff = diff * 60 + stop.tm_min - from.tm_min;
251 return diff * 60 + stop.tm_sec - from.tm_sec;
255MkTimeResult hopAcrossGap(
const MkTimeResult &outside,
const struct tm &base)
259 const qint64 shift = secondsBetween(outside.local, base);
263 if (qLocalTime(outside.utcSecs + shift, &across)) {
264 const qint64 wider = secondsBetween(outside.local, across);
267 if (shift > 0 ? wider > shift : wider < shift) {
268 MkTimeResult result(across);
269 if (result.good && !result.adjusted)
279MkTimeResult resolveRejected(
struct tm base, MkTimeResult result,
280 QDateTimePrivate::TransitionOptions resolve)
288 if (!resolve.testAnyFlags(QDateTimePrivate::GapMask))
291 constexpr time_t twoDaysInSeconds = 2 * 24 * 60 * 60;
293 MkTimeResult early(adjacentDay(base, -1));
294 MkTimeResult later(adjacentDay(base, +1));
295 if (!early.good || !later.good)
299 Q_ASSERT(twoDaysInSeconds + early.utcSecs > later.utcSecs);
300 result.adjusted =
true;
303 QDateTimePrivate::TransitionOption beforeLater = QDateTimePrivate::GapUseBefore;
304 if (resolve.testFlag(QDateTimePrivate::FlipForReverseDst)) {
306 if (early.local.tm_isdst == 1 && !later.local.tm_isdst)
307 beforeLater = QDateTimePrivate::GapUseAfter;
309 if (resolve.testFlag(beforeLater))
310 result.utcSecs = later.utcSecs - secondsBetween(base, later.local);
312 result.utcSecs = early.utcSecs + secondsBetween(early.local, base);
314 if (!qLocalTime(result.utcSecs, &result.local))
321bool preferAlternative(QDateTimePrivate::TransitionOptions resolve,
323 int gotDst,
int altDst,
330 QDateTimePrivate::TransitionOption preferLater = inGap ? QDateTimePrivate::GapUseAfter
331 : QDateTimePrivate::FoldUseAfter;
332 if (resolve.testFlag(QDateTimePrivate::FlipForReverseDst)) {
335 if ((altDst ^ gotDst) == 1) {
339 const bool isReversed = (altDst == 1) != (altIsLater == inGap);
342 if (altIsLater == inGap)
343 isReversed = altDst != 1;
345 isReversed = altDst == 1;
348 preferLater = inGap ? QDateTimePrivate::GapUseBefore
349 : QDateTimePrivate::FoldUseBefore;
353 return resolve.testFlag(preferLater) == altIsLater;
357
358
359
360
361
362
363
364
365
366MkTimeResult resolveLocalTime(qint64 local, QDateTimePrivate::TransitionOptions resolve)
368 const auto localDaySecs = QRoundingDown::qDivMod<SECS_PER_DAY>(local);
369 struct tm base = timeToTm(localDaySecs.quotient, localDaySecs.remainder);
372 MkTimeResult result(base);
381 return resolveRejected(base, result, resolve);
382 }
else if (result.local.tm_isdst < 0) {
386 }
else if (result.adjusted) {
388 if (!resolve.testAnyFlags(QDateTimePrivate::GapMask)) {
394 const MkTimeResult flipped = hopAcrossGap(result, base);
397 if (preferAlternative(resolve, result.local.tm_isdst, flipped.local.tm_isdst,
398 flipped.utcSecs > result.utcSecs,
true)) {
400 if (!flipped.good || flipped.adjusted)
405 result.adjusted =
true;
407 }
else if (resolve.testFlag(QDateTimePrivate::FlipForReverseDst)
410 && resolve.testFlag(result.local.tm_isdst ? QDateTimePrivate::FoldUseBefore
411 : QDateTimePrivate::FoldUseAfter)) {
421 struct tm copy = base;
422 copy.tm_isdst = !result.local.tm_isdst;
423 const MkTimeResult flipped(copy);
424 if (flipped.good && !flipped.adjusted) {
426 if (!resolve.testAnyFlags(QDateTimePrivate::FoldMask)) {
432 if (preferAlternative(resolve, result.local.tm_isdst, flipped.local.tm_isdst,
433 flipped.utcSecs > result.utcSecs,
false)) {
441inline std::optional<qint64> tmToJd(
const struct tm &date)
443 return QGregorianCalendar::julianFromParts(qYearFromTmYear(date.tm_year),
444 date.tm_mon + 1, date.tm_mday);
447#define IC(N) std::integral_constant<qint64, N>()
450inline bool daysAndSecondsOverflow(qint64 julianDay, qint64 daySeconds, qint64 *epochSeconds)
452 return qMulOverflow(julianDay - JULIAN_DAY_FOR_EPOCH,
IC(SECS_PER_DAY), epochSeconds)
453 || qAddOverflow(*epochSeconds, daySeconds, epochSeconds);
457inline bool secondsAndMillisOverflow(qint64 epochSeconds, qint64 millis, qint64 *epochMillis)
459 return qMulOverflow(epochSeconds,
IC(MSECS_PER_SEC), epochMillis)
460 || qAddOverflow(*epochMillis, millis, epochMillis);
469#ifndef QT_BOOTSTRAPPED
475 TIME_ZONE_INFORMATION tzInfo;
476 if (GetTimeZoneInformation(&tzInfo) != TIME_ZONE_ID_INVALID) {
477 int bias = tzInfo.Bias;
479 if (tzInfo.StandardDate.wMonth)
480 bias += tzInfo.StandardBias;
482 return -bias * SECS_PER_MIN;
486 const time_t curr = time(
nullptr);
489
490
491
492
493
494
495
496
497
498
499
500# if defined(_POSIX_THREAD_SAFE_FUNCTIONS)
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);
509 if (
struct tm *tp = gmtime(&curr)) {
511 time_t mkt = qMkTime(&t);
512 int offset =
int(curr - mkt);
513 Q_ASSERT(std::abs(offset) <= SECS_PER_DAY);
519 qDebug(
"Unable to determine current standard time offset from UTC");
528 return QDateTimePrivate::expressUtcAsLocal(atMSecsSinceEpoch).offset;
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))
542 const auto epochSeconds = time_t(epoch.quotient);
545 if (!qLocalTime(epochSeconds, &local))
548 auto jd = tmToJd(local);
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))) {
560 = local.tm_isdst ? QDateTimePrivate::DaylightTime : QDateTimePrivate::StandardTime;
561 return { localMillis,
int(localSeconds - epochSeconds), dst };
566 auto use = resolveLocalTime(QRoundingDown::qDiv<MSECS_PER_SEC>(local), resolve);
570 if (use.local.tm_zone)
571 return QString::fromLocal8Bit(use.local.tm_zone);
573 return qTzName(use.local.tm_isdst > 0 ? 1 : 0);
579 qint64 localSecs = local / MSECS_PER_SEC;
580 auto use = resolveLocalTime(localSecs, resolve);
584 qint64 millis = local - localSecs * MSECS_PER_SEC;
586 Q_ASSERT(local < 0 ? (millis <= 0 && millis > -MSECS_PER_SEC)
587 : (millis >= 0 && millis < MSECS_PER_SEC));
589 QDateTimePrivate::DaylightStatus dst =
590 use.local.tm_isdst > 0 ? QDateTimePrivate::DaylightTime : QDateTimePrivate::StandardTime;
593 const int offset = use.local.tm_gmtoff;
594 localSecs = offset + use.utcSecs;
597 int offset = localSecs - use.utcSecs;
598 auto jd = tmToJd(use.local);
600 return {local, offset, dst,
false};
602 qint64 daySecs = tmSecsWithinDay(use.local);
603 Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY);
604 if (daySecs > 0 && *jd < JULIAN_DAY_FOR_EPOCH) {
606 daySecs -= SECS_PER_DAY;
608 if (Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySecs, &localSecs)))
609 return {local, offset, dst,
false};
612 offset = localSecs - use.utcSecs;
620 if (secondsAndMillisOverflow(localSecs, millis, &revised))
621 return {local, offset, QDateTimePrivate::UnknownDaylightTime,
false};
622 return {revised, offset, dst,
true};
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
656 Q_ASSERT(QGregorianCalendar::julianFromParts(1970, 1, 1) == JULIAN_DAY_FOR_EPOCH);
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;
665 struct tm local = {};
666 local.tm_year = tmYearFromQYear(1901);
670 return {qMkTime(&local) == -1 ? 0 : msecsMin, msecsMax,
false,
false};
672 const struct {
int year; qint64 millis; } starts[] = {
673 {
int(QDateTime::YearRange::First) + 1, Bounds::min() },
675 { 1, -Q_INT64_C(62135596800000) },
677 { 1582, -Q_INT64_C(12244089600000) },
679 { 1752, -Q_INT64_C(6879427200000) },
681 { 1900, -Q_INT64_C(2208988800000) },
683 {
int(QDateTime::YearRange::Last) - 1, Bounds::max() },
685 { 3000, Q_INT64_C(32535215999999) },
690 quint64(std::numeric_limits<qint32>::max()) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
693 for (
const auto c : ends) {
694 struct tm local = {};
695 local.tm_year = tmYearFromQYear(c.year);
699 local.tm_min = local.tm_sec = 59;
701 if (qMkTime(&local) != -1) {
707 bool startMin =
true;
708 for (
const auto c : starts) {
710 local.tm_year = tmYearFromQYear(c.year);
714 if (qMkTime(&local) != -1)
715 return {c.millis, stop, startMin, stopMax};
718 return {0, stop,
false, stopMax};
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)