Search code examples
c++qtqdatetime

Qt. QDateTime unpredictable behaviour with timezones and addSecs


I can't realize what's wrong.

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)

This is working fine with Europe/Moscow(+3), but when I change timezone to Asia/Yekaterinburg(+5) it works very strange

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)

Solution

  • Strange thing, I reproduced it using Qt 5.6.0 with this adjusted example.

    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);
    

    Output:

    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)
    

    Note that I added another time zone, which is my local time zone. You probably note that this is the time zone which works (Europe/Berlin).

    Next thing to do is offset analysis. You see the following offsets:

    • Europe/Moscow: +3h
    • Asia/Yekaterinburg: +5h

    Looking at some time zone map one might note that the offset between the two zones is exactly 2h. So where is the +3h coming from?

    Needless to say, I ran another test.

    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"));
    

    Output:

    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)
    

    And here we go: It seems like Qt is not transfering the time zone to UTC before calculation but uses it as UTC which results in a back shift with the corresponding offset.

    But wait...

    QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), Qt::UTC);
    qDebug() << dt.toLocalTime();
    qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
    qDebug() << dt.toUTC();
    

    Output:

    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))
    

    Now it looks like if the constructor gets a time zone it assumes it is UTC, not the provided one. If leaving it out Qt takes local time, which is obvious.

    As it is not documented where it used to be this seems like a bug to me.


    Long story short, what to do?

    If applicable, try to provide times as local time or UTC, tranform them to UTC, calcualte with them and then format to desired output.

    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"));
    

    Output:

    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)