Unifying Qt::TimeSpec within QTimeZone
在QTimeZone内统一Qt::TimeSpec
April 25, 2023 by Eddy | Comments
2023年4月25日Eddy | 评论
Qt 6.5 sees a quiet revolution in QDateTime's API, built on one in QTimeZone's.
Qt6.5见证了QDateTime的API在QTimeZone的API基础上的一场悄无声息的革命。
Previously
先前
QDateTime had an overload pair of constructors that looked like:
QDateTime有一对重载构造函数,看起来像:
QDateTime(QDate date, QTime time, Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0);
#if QT_CONFIG(timezone)
QDateTime(QDate date, QTime time, const QTimeZone &timeZone);
#endif // timezone
which, in turn, implied a similar duplication in various methods of QDateTime and QDate that returned a new QDateTime object.
这反过来意味着QDateTime和QDate的各种方法中存在类似的重复,返回了一个新的QDateTime对象。
class Q_CORE_EXPORT QDate
{
// …
QDateTime startOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const;
QDateTime endOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const;
#if QT_CONFIG(timezone)
QDateTime startOfDay(const QTimeZone &zone) const;
QDateTime endOfDay(const QTimeZone &zone) const;
#endif
// …
};
// …
class Q_CORE_EXPORT QDateTime
{
// …
void setTimeSpec(Qt::TimeSpec spec);
void setOffsetFromUtc(int offsetSeconds);
#if QT_CONFIG(timezone)
void setTimeZone(const QTimeZone &toZone);
#endif // timezone
// … similar for s/set/to/ …
static QDateTime fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec = Qt::LocalTime,
int offsetFromUtc = 0);
static QDateTime fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec = Qt::LocalTime,
int offsetFromUtc = 0);
#if QT_CONFIG(timezone)
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone);
static QDateTime fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone);
#endif
// …
};
Any future new methods or constructors were doomed to repeat this duplication, complete with its #if-ery (on a feature that's only defined on most platforms). So it wasn't a nice API to try to grow or evolve. But that's my problem, as a developer working on it; how about client code ?
任何未来的新方法或构造函数都注定要重复这种重复,包括#if-ery(在大多数平台上定义的功能上)。因此,它不是一个很好的API来尝试增长或发展。但这是我的问题,作为一名开发人员;客户端代码如何?
Client code's problem
客户端代码的问题
Code that needed to manipulate QDateTime instances had, in some cases, to switch on the Qt::TimeSpec of one of them. For example, if your code has a QDateTime active that it wants all the others it manipulates to use the same time spec as, it ended up needing a time-spec converter, for any received QDateTime datetime that looked like
在某些情况下,需要操作QDateTime实例的代码必须打开其中一个实例的Qt::TimeSpec。例如,如果您的代码有一个激活的QDateTime,它希望它操纵的所有其他代码使用相同的时间规范,那么它最终需要一个时间规范转换器,用于任何接收到的QDateTime-datetime
switch (active.timeSpec()) {
case Qt::UTC:
return datetime.toUTC();
case Qt::LocalTime:
return datetime.toLocalTime();
case Qt::OffsetFromUTC:
return datetime.toOffsetFromUtc(active.offsetFromUtc());
case Qt::TimeZone:
#if QT_CONFIG(timezone)
return datetime.toTimeZone(active.timeZone());
#else
qWarning("Enable timezone feature to support Qt::TimeZone");
return datetime; // Failed to convert.
#endif
}
Q_UNREACHABLE();
The extra complexity of such changes is one factor in why QDateTimeEdit still only supports UTC and local time; see QTBUG-80417.
这些变化的额外复杂性是QDateTimeEdit仍然只支持UTC和当地时间的原因之一;参见QTBUG-80417。
The solution
解决方案
The initial version of this was called QTimeSystem but Thiago persuaded me to roll it into QTimeZone, which did make a whole lot of sense, although the implementation had some trickiness to it. (Thanks to Ville and Marc for help with working out how to do that.) So now QTimeZone can be either
它的最初版本被称为QTimeSystem,但Thiago说服我将其引入QTimeZone,这确实很有意义,尽管实现过程中有一些棘手之处(感谢Ville和Marc的帮助。)所以现在QTimeZone可以是
-
an actual backend-backed time-zone object, as before, or
-
实际的后端支持的时区对象,与以前一样,或者
-
a lightweight time representation.
-
轻量级的时间表示。
Only the former is pimpled; the latter inhabits the space its d-ptr would occupy, with the least significant bits of that being an enum to tell it what it is; 0 means pimpled, of course.
只有前者是多余的;后者占据了d-ptr将占据的空间,其中最不重要的部分是一个枚举来告诉它它是什么;0当然意味着多余。
A lightweight time representation, then, is just a Qt::TimeSpec packaged, when necessary, with the offset it needs to carry around with it. When the Qt::TimeSpec is Qt::TimeZone, we're in the pimpled side of the union and using the same old back-end as ever, subject to the same old #if-ery on feature timezone. The rest of the class is now freed of that #if-ery and the header no longer does a QT_REQUIRE_CONFIG(timezone); so it can always be included.
因此,轻量级的时间表示只是一个Qt::TimeSpec封装,必要时带有它需要携带的偏移量。当Qt:::TimeSpec是Qt::TimeZone时,我们处于联合的粉刺侧,使用一如既往的旧后端,在功能时区上受相同的旧#if-ery约束。类的其余部分现在已经摆脱了#if-ery,并且头不再执行QT_REQUIRE_CONFIG(timezone);所以它总是可以被包括在内。
6.5 APIs
The changed QTimeZone API now only conditions the backend-based aspects on feature timezone; all the rest is always present.
更改后的QTimeZone API现在只在功能时区上限制基于后端的方面;其余的总是当前的。
The changed QDateTime API now prefers to go via QTimeZone, obtained from QDateTime::timeRepresentation() – the name timeZone() being already taken; it could now be implemented as timeRepresentation().asBackendZone(). That client code converter for time-spec becomes a one-liner, converting to the time-zone of the active value:
更改后的QDateTime API现在更倾向于通过QTimeZone,它是从QDateTime::timeRepresentation()获得的,名称timeZone()已经被使用;它现在可以实现为timeRepresentation().asBackendZone()。时间规范的客户端代码转换器变成了一个单行,转换为活动值的时区:
return datetime.toTimeZone(active.timeRepresentation());
and passing a QTimeZone to functions returning a QDateTime is now the uniform way to call relevant functions. Qt::TimeSpec should now almost never need to be seen in public any more.
将QTimeZone传递给返回QDateTime的函数现在是调用相关函数的统一方式。Qt::TimeSpec现在几乎不需要再公开露面了。
The one frustrating detail is that, to avoid mutual inclusion issues, qdatetime.h can't #include <QTimeZone> so the methods there taking a QTimeZone parameter can't pass QTimeZone::LocalTime as default, obliging them to still have an overload without the QTimeZone parameter, for backwards compatibility – but you should never need to use these.
一个令人沮丧的细节是,为了避免相互包含的问题,qdatetime.h不能#include<QTimeZone>,因此采用QTimeZone参数的方法不能将QTimeZone::LocalTime作为默认值传递,这迫使它们在没有QTimeZone参数的情况下仍有重载,以实现向后兼容性,但您永远不应该使用这些方法。
The usual extent of change needed in existing code, to minimally adapt to the coming deprecations, is to replace the Qt prefix with QTimeZone in all uses of Qt::UTC and Qt::LocalTime (which are easy to search for). Further changes to make the most of the new API can follow at leisure.
为了最低限度地适应即将到来的弃用,现有代码中通常需要进行的更改是在Qt::UTC和Qt:::LocalTime(很容易搜索)的所有使用中用QTimeZone替换Qt前缀。为了充分利用新的API,可以随时进行进一步的更改。
As a result, QTBUG-80417 is now more or less at the point where we just need to change the API of QDateTimeEdit to access the internals which just got simpler while becoming able to support any time-zone, as soon as the public API exposes that.
因此,QTBUG-80417现在或多或少已经到了我们只需要更改QDateTimeEdit的API来访问内部组件的时候了,只要公开的API暴露出来,内部组件就会变得更简单,同时能够支持任何时区。