diff options
| author | Edward Welbourne <edward.welbourne@qt.io> | 2022-05-09 12:06:18 +0200 |
|---|---|---|
| committer | Edward Welbourne <edward.welbourne@qt.io> | 2022-05-20 13:09:23 +0200 |
| commit | 6d97cf0e573f11ae1fae11441e6fa747c34be21c (patch) | |
| tree | 521d9e6c805ea0e63dec0780f4558ef5efc28dba /src/corelib/time/qlocaltime.cpp | |
| parent | 1e295cff491b30a88367259115fe429f18bab5f7 (diff) | |
Split qtLocalTime out into a new QLocalTime namespace
... in new qlocaltime* files, now that it's decoupled enough from the
internals of QDateTime for this to be possible. Part of the
consolidation of time_t code in one place.
Move assorted constants from qdatetime.cpp to a private namespace in
qdatetimeprivate_p.h to be shared between q*time.cpp hereafter (fixing
an out of date comment in the process - julianDayFromDate() is long
gone).
Task-number: QTBUG-95993
Change-Id: I03d97e959118041f9d86b8bb2e738599bc0b17e1
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/time/qlocaltime.cpp')
| -rw-r--r-- | src/corelib/time/qlocaltime.cpp | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/corelib/time/qlocaltime.cpp b/src/corelib/time/qlocaltime.cpp new file mode 100644 index 00000000000..589a6e76880 --- /dev/null +++ b/src/corelib/time/qlocaltime.cpp @@ -0,0 +1,108 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qlocaltime_p.h" +#include "qplatformdefs.h" + +#include "private/qgregoriancalendar_p.h" +#include "private/qnumeric_p.h" +#if QT_CONFIG(timezone) +#include "private/qtimezoneprivate_p.h" +#endif + +#include <time.h> +#ifdef Q_OS_WIN +# include <qt_windows.h> +#endif + +QT_BEGIN_NAMESPACE + +using namespace QtPrivate::DateTimeConstants; +namespace { +/* + Qt represents n BCE as -n, whereas struct tm's tm_year field represents a + year by the number of years after (negative for before) 1900, so that 1+m + BCE is -1900 -m; so treating 1 BCE as 0 CE. We thus shift by different + offsets depending on whether the year is BCE or CE. +*/ +// constexpr int tmYearFromQYear(int year) { return year - (year < 0 ? 1899 : 1900); } +constexpr int qYearFromTmYear(int year) { return year + (year < -1899 ? 1899 : 1900); } + +bool qtLocalTime(time_t utc, struct tm *local) +{ + // This should really be done under the environmentMutex wrapper qglobal.cpp + // uses in qTzSet() and friends. However, the only sane way to do that would + // be to move this whole function there (and replace its qTzSet() with a + // naked tzset(), since it'd already be mutex-protected). +#if defined(Q_OS_WIN) + // The doc of localtime_s() doesn't explicitly say that it calls _tzset(), + // but does say that localtime_s() corrects for the same things _tzset() + // sets the globals for, so presumably localtime_s() behaves as if it did. + return !localtime_s(local, &utc); +#elif QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // Use the reentrant version of localtime() where available, as it is + // thread-safe and doesn't use a shared static data area. + // As localtime() is specified to work as if it called tzset(), but + // localtime_r() does not have this constraint, make an explicit call. + // The explicit call should also request a re-parse of timezone info. + qTzSet(); + if (tm *res = localtime_r(&utc, local)) { + Q_ASSERT(res == local); + return true; + } + return false; +#else + // Returns shared static data which may be overwritten at any time + // So copy the result asap + if (tm *res = localtime(&utc)) { + *local = *res; + return true; + } + return false; +#endif +} +} // namespace + +namespace QLocalTime { + +// Calls the platform variant of localtime() for the given utcMillis, and +// returns the local milliseconds, offset from UTC and DST status. +QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis) +{ + const int signFix = utcMillis % MSECS_PER_SEC && utcMillis < 0 ? 1 : 0; + const time_t epochSeconds = utcMillis / MSECS_PER_SEC - signFix; + const int msec = utcMillis % MSECS_PER_SEC + signFix * MSECS_PER_SEC; + Q_ASSERT(msec >= 0 && msec < MSECS_PER_SEC); + if (qint64(epochSeconds) * MSECS_PER_SEC + msec != utcMillis) + return {utcMillis}; + + tm local; + if (!qtLocalTime(epochSeconds, &local)) + return {utcMillis}; + + qint64 jd; + if (Q_UNLIKELY(!QGregorianCalendar::julianFromParts(qYearFromTmYear(local.tm_year), + local.tm_mon + 1, local.tm_mday, &jd))) { + return {utcMillis}; + } + const qint64 daySeconds + = (local.tm_hour * 60 + local.tm_min) * 60 + local.tm_sec; + Q_ASSERT(0 <= daySeconds && daySeconds < SECS_PER_DAY); + qint64 localSeconds, localMillis; + if (Q_UNLIKELY( + mul_overflow(jd - JULIAN_DAY_FOR_EPOCH, std::integral_constant<qint64, SECS_PER_DAY>(), + &localSeconds) + || add_overflow(localSeconds, daySeconds, &localSeconds) + || mul_overflow(localSeconds, std::integral_constant<qint64, MSECS_PER_SEC>(), + &localMillis) + || add_overflow(localMillis, qint64(msec), &localMillis))) { + return {utcMillis}; + } + const auto dst + = local.tm_isdst ? QDateTimePrivate::DaylightTime : QDateTimePrivate::StandardTime; + return { localMillis, int(localSeconds - epochSeconds), dst }; +} + +} // QLocalTime + +QT_END_NAMESPACE |
