summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2022-05-09 12:06:18 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2022-05-20 13:09:23 +0200
commit6d97cf0e573f11ae1fae11441e6fa747c34be21c (patch)
tree521d9e6c805ea0e63dec0780f4558ef5efc28dba
parent1e295cff491b30a88367259115fe429f18bab5f7 (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>
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/time/qdatetime.cpp81
-rw-r--r--src/corelib/time/qdatetime_p.h16
-rw-r--r--src/corelib/time/qlocaltime.cpp108
-rw-r--r--src/corelib/time/qlocaltime_p.h31
-rw-r--r--src/tools/bootstrap/CMakeLists.txt1
6 files changed, 161 insertions, 77 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 2615a71f8b..5fd60b5ac9 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -216,6 +216,7 @@ qt_internal_add_module(Core
time/qcalendarbackend_p.h
time/qcalendarmath_p.h
time/qdatetime.cpp time/qdatetime.h time/qdatetime_p.h
+ time/qlocaltime.cpp time/qlocaltime_p.h
time/qgregoriancalendar.cpp time/qgregoriancalendar_p.h
time/qjuliancalendar.cpp time/qjuliancalendar_p.h
time/qmilankoviccalendar.cpp time/qmilankoviccalendar_p.h
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index 817f37d4a4..27d31cca8f 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -20,6 +20,7 @@
#include "private/qcore_mac_p.h"
#endif
#include "private/qgregoriancalendar_p.h"
+#include "private/qlocaltime_p.h"
#include "private/qnumeric_p.h"
#include "private/qstringiterator_p.h"
#if QT_CONFIG(timezone)
@@ -35,20 +36,13 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace QtPrivate::DateTimeConstants;
/*****************************************************************************
Date/Time Constants
*****************************************************************************/
-constexpr qint64 SECS_PER_DAY = 86400;
-constexpr qint64 MSECS_PER_DAY = 86400000;
-constexpr qint64 SECS_PER_HOUR = 3600;
-constexpr qint64 MSECS_PER_HOUR = 3600000;
-constexpr qint64 SECS_PER_MIN = 60;
-constexpr qint64 MSECS_PER_MIN = 60000;
-constexpr qint64 MSECS_PER_SEC = 1000;
constexpr qint64 TIME_T_MAX = std::numeric_limits<time_t>::max();
-constexpr qint64 JULIAN_DAY_FOR_EPOCH = 2440588; // result of julianDayFromDate(1970, 1, 1)
/*****************************************************************************
QDate static helper functions
@@ -2564,73 +2558,6 @@ static inline bool callMkTime(tm *local, time_t *secs)
return good;
}
-namespace {
-bool qtLocalTime(time_t utc, struct tm *local)
-{
- // localtime() is specified to work as if it called tzset().
- // localtime_r() does not have this constraint, so make an explicit call.
- // The explicit call should also request a re-parse of timezone info.
- qTzSet();
-#if defined(Q_OS_WIN)
- return !localtime_s(local, &utc);
-#elif QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
- // Use the reentrant version of localtime() where available
- // as is thread-safe and doesn't use a shared static data area
- 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
-}
-
-// 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 * SECS_PER_HOUR + local.tm_min * SECS_PER_MIN + 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 };
-}
-}
-
// Converts milliseconds since the start of 1970 into a date and/or time:
static qint64 msecsToJulianDay(qint64 msecs)
{
@@ -2903,7 +2830,7 @@ QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs)
ZoneState result{utcMSecs};
// Within the time_t supported range, localtime() can handle it:
if (millisInSystemRange(utcMSecs)) {
- result = utcToLocal(utcMSecs);
+ result = QLocalTime::utcToLocal(utcMSecs);
if (result.valid)
return result;
}
@@ -2935,7 +2862,7 @@ QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs)
return result;
}
- result = utcToLocal(fakeUtc);
+ result = QLocalTime::utcToLocal(fakeUtc);
// Now correct result.when for the use of the fake date:
if (!result.valid || add_overflow(result.when, diffMillis, &result.when)) {
// If utcToLocal() failed, its return has the fake when; restore utcMSecs.
diff --git a/src/corelib/time/qdatetime_p.h b/src/corelib/time/qdatetime_p.h
index cccaba840a..0636a3a223 100644
--- a/src/corelib/time/qdatetime_p.h
+++ b/src/corelib/time/qdatetime_p.h
@@ -111,6 +111,22 @@ public:
Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimePrivate::StatusFlags)
+namespace QtPrivate {
+namespace DateTimeConstants {
+
+constexpr qint64 SECS_PER_MIN = 60;
+constexpr qint64 SECS_PER_HOUR = SECS_PER_MIN * 60;
+constexpr qint64 SECS_PER_DAY = SECS_PER_HOUR * 24;
+
+constexpr qint64 MSECS_PER_SEC = 1000;
+constexpr qint64 MSECS_PER_MIN = SECS_PER_MIN * MSECS_PER_SEC;
+constexpr qint64 MSECS_PER_HOUR = SECS_PER_HOUR * MSECS_PER_SEC;
+constexpr qint64 MSECS_PER_DAY = SECS_PER_DAY * MSECS_PER_SEC;
+
+constexpr qint64 JULIAN_DAY_FOR_EPOCH = 2440588; // result of QDate(1970, 1, 1).toJulianDay()
+}
+}
+
QT_END_NAMESPACE
#endif // QDATETIME_P_H
diff --git a/src/corelib/time/qlocaltime.cpp b/src/corelib/time/qlocaltime.cpp
new file mode 100644
index 0000000000..589a6e7688
--- /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
diff --git a/src/corelib/time/qlocaltime_p.h b/src/corelib/time/qlocaltime_p.h
new file mode 100644
index 0000000000..2a7b7bc098
--- /dev/null
+++ b/src/corelib/time/qlocaltime_p.h
@@ -0,0 +1,31 @@
+// 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
+
+#ifndef QLOCALTIME_P_H
+#define QLOCALTIME_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an implementation
+// detail. This header file may change from version to version without notice,
+// or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/private/qdatetime_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Packaging system time_t functions
+namespace QLocalTime {
+// Support for QDateTime
+QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis);
+}
+
+QT_END_NAMESPACE
+
+#endif // QLOCALTIME_P_H
diff --git a/src/tools/bootstrap/CMakeLists.txt b/src/tools/bootstrap/CMakeLists.txt
index 2f69849538..0d920624af 100644
--- a/src/tools/bootstrap/CMakeLists.txt
+++ b/src/tools/bootstrap/CMakeLists.txt
@@ -71,6 +71,7 @@ qt_internal_extend_target(Bootstrap
../../corelib/time/qcalendar.cpp
../../corelib/time/qdatetime.cpp
../../corelib/time/qgregoriancalendar.cpp
+ ../../corelib/time/qlocaltime.cpp
../../corelib/time/qromancalendar.cpp
../../corelib/tools/qarraydata.cpp
../../corelib/tools/qbitarray.cpp