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>
This commit is contained in:
parent
1e295cff49
commit
6d97cf0e57
@ -216,6 +216,7 @@ qt_internal_add_module(Core
|
|||||||
time/qcalendarbackend_p.h
|
time/qcalendarbackend_p.h
|
||||||
time/qcalendarmath_p.h
|
time/qcalendarmath_p.h
|
||||||
time/qdatetime.cpp time/qdatetime.h time/qdatetime_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/qgregoriancalendar.cpp time/qgregoriancalendar_p.h
|
||||||
time/qjuliancalendar.cpp time/qjuliancalendar_p.h
|
time/qjuliancalendar.cpp time/qjuliancalendar_p.h
|
||||||
time/qmilankoviccalendar.cpp time/qmilankoviccalendar_p.h
|
time/qmilankoviccalendar.cpp time/qmilankoviccalendar_p.h
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "private/qcore_mac_p.h"
|
#include "private/qcore_mac_p.h"
|
||||||
#endif
|
#endif
|
||||||
#include "private/qgregoriancalendar_p.h"
|
#include "private/qgregoriancalendar_p.h"
|
||||||
|
#include "private/qlocaltime_p.h"
|
||||||
#include "private/qnumeric_p.h"
|
#include "private/qnumeric_p.h"
|
||||||
#include "private/qstringiterator_p.h"
|
#include "private/qstringiterator_p.h"
|
||||||
#if QT_CONFIG(timezone)
|
#if QT_CONFIG(timezone)
|
||||||
@ -35,20 +36,13 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
using namespace QtPrivate::DateTimeConstants;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Date/Time Constants
|
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 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
|
QDate static helper functions
|
||||||
@ -2564,73 +2558,6 @@ static inline bool callMkTime(tm *local, time_t *secs)
|
|||||||
return good;
|
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:
|
// Converts milliseconds since the start of 1970 into a date and/or time:
|
||||||
static qint64 msecsToJulianDay(qint64 msecs)
|
static qint64 msecsToJulianDay(qint64 msecs)
|
||||||
{
|
{
|
||||||
@ -2903,7 +2830,7 @@ QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs)
|
|||||||
ZoneState result{utcMSecs};
|
ZoneState result{utcMSecs};
|
||||||
// Within the time_t supported range, localtime() can handle it:
|
// Within the time_t supported range, localtime() can handle it:
|
||||||
if (millisInSystemRange(utcMSecs)) {
|
if (millisInSystemRange(utcMSecs)) {
|
||||||
result = utcToLocal(utcMSecs);
|
result = QLocalTime::utcToLocal(utcMSecs);
|
||||||
if (result.valid)
|
if (result.valid)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -2935,7 +2862,7 @@ QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = utcToLocal(fakeUtc);
|
result = QLocalTime::utcToLocal(fakeUtc);
|
||||||
// Now correct result.when for the use of the fake date:
|
// Now correct result.when for the use of the fake date:
|
||||||
if (!result.valid || add_overflow(result.when, diffMillis, &result.when)) {
|
if (!result.valid || add_overflow(result.when, diffMillis, &result.when)) {
|
||||||
// If utcToLocal() failed, its return has the fake when; restore utcMSecs.
|
// If utcToLocal() failed, its return has the fake when; restore utcMSecs.
|
||||||
|
@ -111,6 +111,22 @@ public:
|
|||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimePrivate::StatusFlags)
|
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
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // QDATETIME_P_H
|
#endif // QDATETIME_P_H
|
||||||
|
108
src/corelib/time/qlocaltime.cpp
Normal file
108
src/corelib/time/qlocaltime.cpp
Normal file
@ -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
|
31
src/corelib/time/qlocaltime_p.h
Normal file
31
src/corelib/time/qlocaltime_p.h
Normal file
@ -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
|
@ -71,6 +71,7 @@ qt_internal_extend_target(Bootstrap
|
|||||||
../../corelib/time/qcalendar.cpp
|
../../corelib/time/qcalendar.cpp
|
||||||
../../corelib/time/qdatetime.cpp
|
../../corelib/time/qdatetime.cpp
|
||||||
../../corelib/time/qgregoriancalendar.cpp
|
../../corelib/time/qgregoriancalendar.cpp
|
||||||
|
../../corelib/time/qlocaltime.cpp
|
||||||
../../corelib/time/qromancalendar.cpp
|
../../corelib/time/qromancalendar.cpp
|
||||||
../../corelib/tools/qarraydata.cpp
|
../../corelib/tools/qarraydata.cpp
|
||||||
../../corelib/tools/qbitarray.cpp
|
../../corelib/tools/qbitarray.cpp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user