Qt。时区和 addSecs 的 QDateTime 不可预测的行为
Qt. QDateTime unpredictable behaviour with timezones and addSecs
我不知道出了什么问题。
QTimeZone zone1(QTimeZone("Europe/Moscow"));
QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone1);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 15:05:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)
这在 Europe/Moscow(+3) 上工作正常,但是当我将时区更改为 Asia/Yekaterinburg(+5) 时,它工作起来很奇怪
QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone2);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 10:05:00.000 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
St运行ge 东西,我用 Qt 5.6.0 和这个调整后的例子重现了它。
QDateTime test1 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Moscow"));
QDateTime test2 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Asia/Yekaterinburg"));
QDateTime test3 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Berlin"));
qDebug() << test1;
qDebug() << test2;
qDebug() << test3;
qDebug() << test1.addSecs(5*60);
qDebug() << test2.addSecs(5*60);
qDebug() << test3.addSecs(5*60);
输出:
QDateTime(2016-11-11 15:00:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 MEZ Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 MEZ Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
请注意,我添加了另一个时区,这是我当地的时区。您可能注意到这是有效的时区 (Europe/Berlin
)。
接下来要做的是偏移量分析。您会看到以下偏移量:
Europe/Moscow
: +3h
Asia/Yekaterinburg
: +5h
看一些 time zone map 可能会注意到两个区域之间的偏移正好是 2h。那么 +3h 是从哪里来的呢?
不用说了,我运行又考了
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("UTC"));
qDebug() << dt;
qDebug() << dt.addSecs(5*60);
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));
输出:
QDateTime(2016-11-11 15:00:00.000 UTC Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 15:05:00.000 UTC Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
我们开始了:Qt 似乎没有在计算之前将时区 运行 转换为 UTC,而是将其用作 UTC,这会导致具有相应偏移量的后移。
但是等等...
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), Qt::UTC);
qDebug() << dt.toLocalTime();
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.toUTC();
输出:
QDateTime(2016-11-11 16:00:00.000 MEZ Qt::TimeSpec(LocalTime))
QDateTime(2016-11-11 20:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 UTC Qt::TimeSpec(UTC))
现在看来,如果构造函数获得一个时区,它会假定它是 UTC,而不是提供的时区。如果省略 Qt 需要本地时间,这是显而易见的。
因为没有记录它 used to be 这对我来说似乎是一个错误。
长话短说,怎么办?
如果适用,请尝试提供本地时间或 UTC 时间,t运行将它们转换为 UTC,用它们计算,然后格式化为所需的输出。
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(10, 00), Qt::UTC);
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));
输出:
QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
另一种可能的解决方法是使用此 free, open source, C++11/14 library,它基于 <chrono>
:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto zone1 = make_zoned("Europe/Moscow", local_days{2016_y/11/11} + 15h);
std::cout << zone1 << '\n';
zone1 = zone1.get_local_time() + 5min;
std::cout << zone1 << '\n';
auto zone2 = make_zoned("Asia/Yekaterinburg", local_days{2016_y/11/11} + 15h);
std::cout << zone2 << '\n';
zone2 = zone2.get_local_time() + 5min;
std::cout << zone2 << '\n';
}
输出为:
2016-11-11 15:00:00 MSK
2016-11-11 15:05:00 MSK
2016-11-11 15:00:00 +05
2016-11-11 15:05:00 +05
这里是转换函数to/fromQDate
:
https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#QDate
我不知道出了什么问题。
QTimeZone zone1(QTimeZone("Europe/Moscow"));
QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone1);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 15:05:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)
这在 Europe/Moscow(+3) 上工作正常,但是当我将时区更改为 Asia/Yekaterinburg(+5) 时,它工作起来很奇怪
QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone2);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 10:05:00.000 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
St运行ge 东西,我用 Qt 5.6.0 和这个调整后的例子重现了它。
QDateTime test1 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Moscow"));
QDateTime test2 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Asia/Yekaterinburg"));
QDateTime test3 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Berlin"));
qDebug() << test1;
qDebug() << test2;
qDebug() << test3;
qDebug() << test1.addSecs(5*60);
qDebug() << test2.addSecs(5*60);
qDebug() << test3.addSecs(5*60);
输出:
QDateTime(2016-11-11 15:00:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 MEZ Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 MEZ Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
请注意,我添加了另一个时区,这是我当地的时区。您可能注意到这是有效的时区 (Europe/Berlin
)。
接下来要做的是偏移量分析。您会看到以下偏移量:
Europe/Moscow
: +3hAsia/Yekaterinburg
: +5h
看一些 time zone map 可能会注意到两个区域之间的偏移正好是 2h。那么 +3h 是从哪里来的呢?
不用说了,我运行又考了
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("UTC"));
qDebug() << dt;
qDebug() << dt.addSecs(5*60);
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));
输出:
QDateTime(2016-11-11 15:00:00.000 UTC Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 15:05:00.000 UTC Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
我们开始了:Qt 似乎没有在计算之前将时区 运行 转换为 UTC,而是将其用作 UTC,这会导致具有相应偏移量的后移。
但是等等...
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), Qt::UTC);
qDebug() << dt.toLocalTime();
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.toUTC();
输出:
QDateTime(2016-11-11 16:00:00.000 MEZ Qt::TimeSpec(LocalTime))
QDateTime(2016-11-11 20:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 UTC Qt::TimeSpec(UTC))
现在看来,如果构造函数获得一个时区,它会假定它是 UTC,而不是提供的时区。如果省略 Qt 需要本地时间,这是显而易见的。
因为没有记录它 used to be 这对我来说似乎是一个错误。
长话短说,怎么办?
如果适用,请尝试提供本地时间或 UTC 时间,t运行将它们转换为 UTC,用它们计算,然后格式化为所需的输出。
QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(10, 00), Qt::UTC);
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));
输出:
QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
另一种可能的解决方法是使用此 free, open source, C++11/14 library,它基于 <chrono>
:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto zone1 = make_zoned("Europe/Moscow", local_days{2016_y/11/11} + 15h);
std::cout << zone1 << '\n';
zone1 = zone1.get_local_time() + 5min;
std::cout << zone1 << '\n';
auto zone2 = make_zoned("Asia/Yekaterinburg", local_days{2016_y/11/11} + 15h);
std::cout << zone2 << '\n';
zone2 = zone2.get_local_time() + 5min;
std::cout << zone2 << '\n';
}
输出为:
2016-11-11 15:00:00 MSK
2016-11-11 15:05:00 MSK
2016-11-11 15:00:00 +05
2016-11-11 15:05:00 +05
这里是转换函数to/fromQDate
:
https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#QDate