5#include "qplatformdefs.h"
7#include "private/qcalendarmath_p.h"
8#if QT_CONFIG(datetimeparser)
9#include "private/qdatetimeparser_p.h"
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"
38constexpr int tmYearFromQYear(
int year) {
return year - (year < 0 ? 1899 : 1900); }
39constexpr int qYearFromTmYear(
int year) {
return year + (year < -1899 ? 1899 : 1900); }
41constexpr inline qint64 tmSecsWithinDay(
const struct tm &when)
56 static constexpr time_t maybeError = -1;
57 inline bool meansEnd1969();
58 bool changed(
const struct tm &prior)
const;
62 time_t utcSecs = maybeError;
65 MkTimeResult() { local.tm_isdst = -1; }
68 explicit MkTimeResult(
const struct tm &prior)
69 : local(prior), utcSecs(
qMkTime(&local)),
70 good(utcSecs != maybeError || meansEnd1969()),
71 adjusted(changed(prior))
95inline bool MkTimeResult::meansEnd1969()
100 if (local.tm_year < 69 || local.tm_year > 70
101# ifdef HAVE_TM_GMTOFF
105 || (tmSecsWithinDay(local) - local.tm_gmtoff + 1) %
SECS_PER_DAY
107 || (local.tm_year == 69
108 ? local.tm_mon < 11 || local.tm_mday < 31
109 : local.tm_mon > 0 || local.tm_mday > 1)) {
112 struct tm
copy = local;
125bool MkTimeResult::changed(
const struct tm &prior)
const
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));
139struct tm timeToTm(
qint64 localDay, int secs)
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);
157struct tm matchYearMonth(struct tm when, const struct tm &
base)
164 while (when.tm_year >
base.tm_year) {
168 while (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;
177 int month = yearMon.remainder;
182 year += when.tm_year;
186 while (when.tm_mon <
base.tm_mon) {
187 const auto yearMon = QRoundingDown::qDivMod<12>(when.tm_mon);
190 yearMon.remainder + 1, qYearFromTmYear(yearMon.quotient + when.tm_year));
198struct tm adjacentDay(struct tm when, int dayStep)
202 when.tm_mday += dayStep;
207 if (when.tm_mday <= 0) {
211 int daysInMonth = when.tm_mon
214 when.tm_mday += daysInMonth;
215 if (--when.tm_mon < 0) {
221 }
else if (when.tm_mday > 28) {
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) {
232 when.tm_mon + 1, qYearFromTmYear(when.tm_year)));
239qint64 secondsBetween(
const struct tm &
start,
const struct tm &stop)
244 struct tm from = matchYearMonth(
start, stop);
245 qint64 diff = stop.tm_mday - from.tm_mday;
246 diff = diff * 24 + stop.tm_hour - from.tm_hour;
247 diff = diff * 60 + stop.tm_min - from.tm_min;
248 return diff * 60 + stop.tm_sec - from.tm_sec;
252MkTimeResult hopAcrossGap(
const MkTimeResult &outside,
const struct tm &
base)
261 const qint64 wider = secondsBetween(outside.local, across);
265 MkTimeResult
result(across);
276MkTimeResult resolveRejected(
struct tm
base, MkTimeResult
result,
277 QDateTimePrivate::TransitionOptions
resolve)
288 constexpr time_t twoDaysInSeconds = 2 * 24 * 60 * 60;
290 MkTimeResult early(adjacentDay(
base, -1));
291 MkTimeResult later(adjacentDay(
base, +1));
292 if (!early.good || !later.good)
296 Q_ASSERT(twoDaysInSeconds + early.utcSecs > later.utcSecs);
303 if (early.local.tm_isdst == 1 && !later.local.tm_isdst)
306 if (
resolve.testFlag(beforeLater))
307 result.utcSecs = later.utcSecs - secondsBetween(
base, later.local);
309 result.utcSecs = early.utcSecs + secondsBetween(early.local,
base);
318bool preferAlternative(QDateTimePrivate::TransitionOptions
resolve,
320 int gotDst,
int altDst,
332 if ((altDst ^ gotDst) == 1) {
336 const bool isReversed = (altDst == 1) != (altIsLater == inGap);
339 if (altIsLater == inGap)
340 isReversed = altDst != 1;
342 isReversed = altDst == 1;
350 return resolve.testFlag(preferLater) == altIsLater;
363MkTimeResult resolveLocalTime(
qint64 local, QDateTimePrivate::TransitionOptions
resolve)
365 const auto localDaySecs = QRoundingDown::qDivMod<SECS_PER_DAY>(local);
366 struct tm
base = timeToTm(localDaySecs.quotient, localDaySecs.remainder);
379 }
else if (
result.local.tm_isdst < 0) {
383 }
else if (
result.adjusted) {
391 const MkTimeResult flipped = hopAcrossGap(
result,
base);
394 if (preferAlternative(
resolve,
result.local.tm_isdst, flipped.local.tm_isdst,
395 flipped.utcSecs >
result.utcSecs,
true)) {
397 if (!flipped.good || flipped.adjusted)
420 const MkTimeResult flipped(
copy);
421 if (flipped.good && !flipped.adjusted) {
429 if (preferAlternative(
resolve,
result.local.tm_isdst, flipped.local.tm_isdst,
430 flipped.utcSecs >
result.utcSecs,
false)) {
438inline std::optional<qint64> tmToJd(
const struct tm &
date)
444#define IC(N) std::integral_constant<qint64, N>()
447inline bool daysAndSecondsOverflow(
qint64 julianDay,
qint64 daySeconds,
qint64 *epochSeconds)
450 ||
qAddOverflow(*epochSeconds, daySeconds, epochSeconds);
454inline bool secondsAndMillisOverflow(
qint64 epochSeconds,
qint64 millis,
qint64 *epochMillis)
466#ifndef QT_BOOTSTRAPPED
472 TIME_ZONE_INFORMATION tzInfo;
473 if (GetTimeZoneInformation(&tzInfo) != TIME_ZONE_ID_INVALID) {
474 int bias = tzInfo.Bias;
476 if (tzInfo.StandardDate.wMonth)
477 bias += tzInfo.StandardBias;
483 const time_t curr =
time(
nullptr);
497# if defined(_POSIX_THREAD_SAFE_FUNCTIONS)
499 if (gmtime_r(&curr, &
t)) {
501 int offset = int(curr - mkt);
506 if (
struct tm *tp = gmtime(&curr)) {
509 int offset = int(curr - mkt);
516 qDebug(
"Unable to determine current standard time offset from UTC");
533 const auto epoch = QRoundingDown::qDivMod<MSECS_PER_SEC>(utcMillis);
534 const time_t epochSeconds = epoch.quotient;
535 const int msec = epoch.remainder;
544 auto jd = tmToJd(local);
548 const qint64 daySeconds = tmSecsWithinDay(local);
550 qint64 localSeconds, localMillis;
551 if (
Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySeconds, &localSeconds)
552 || secondsAndMillisOverflow(localSeconds,
qint64(msec), &localMillis))) {
557 return { localMillis, int(localSeconds - epochSeconds),
dst };
562 auto use = resolveLocalTime(QRoundingDown::qDiv<MSECS_PER_SEC>(local),
resolve);
566 if (use.local.tm_zone)
569 return qTzName(use.local.tm_isdst > 0 ? 1 : 0);
576 auto use = resolveLocalTime(localSecs,
resolve);
589 const int offset = use.local.tm_gmtoff;
590 localSecs =
offset + use.utcSecs;
593 int offset = localSecs - use.utcSecs;
594 auto jd = tmToJd(use.local);
598 qint64 daySecs = tmSecsWithinDay(use.local);
604 if (
Q_UNLIKELY(daysAndSecondsOverflow(*jd, daySecs, &localSecs)))
608 offset = localSecs - use.utcSecs;
616 if (secondsAndMillisOverflow(localSecs, millis, &revised))
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) {
659 const qint64 msecsMin = -1 - msecsMax;
661 struct tm local = {};
662 local.tm_year = tmYearFromQYear(1901);
666 return {
qMkTime(&local) == -1 ? 0 : msecsMin, msecsMax,
false,
false};
668 const struct {
int year;
qint64 millis; } starts[] = {
689 for (
const auto c : ends) {
690 struct tm local = {};
691 local.tm_year = tmYearFromQYear(
c.year);
695 local.tm_min = local.tm_sec = 59;
703 bool startMin =
true;
704 for (
const auto c : starts) {
706 local.tm_year = tmYearFromQYear(
c.year);
711 return {
c.millis, stop, startMin, stopMax};
714 return {0, stop,
false, stopMax};
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
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
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)
#define Q_DECL_COLD_FUNCTION
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
std::enable_if_t< std::is_unsigned_v< T >||std::is_signed_v< T >, bool > qMulOverflow(T v1, T v2, T *r)
GLenum GLuint GLintptr offset
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
bool qLocalTime(time_t utc, struct tm *local)
time_t qMkTime(struct tm *when)
QString qTzName(int dstIndex)
unsigned long long quint64