Search code examples
c++qtqt5qdatetime

QDateTime::secsTo returns the same value for different QDateTime's


I recently wrote a stopwatch and noticed some strange behavior of QDateTime::secsTo. I'm not sure if it's a bug or a feature (or perhaps I only did a crappy implementation ;-).

My stopwatch code can be stripped down to this minimal example to produce the questionable result (at least on Linux using Qt 5.7.1):

StopWatch.h

#ifndef STOPWATCH_H
#define STOPWATCH_H

#include <QDialog>
#include <QDateTime>

class QTimer;

class StopWatch : public QDialog
{
    Q_OBJECT

public:
    explicit StopWatch(QWidget *parent);

private slots:
    void update();

private:
    QTimer *m_timer;
    QDateTime m_targetTime;
};

#endif // STOPWATCH_H

StopWatch.cpp

#include "StopWatch.h"
#include <QDebug>
#include <QTimer>

StopWatch::StopWatch(QWidget *parent) : QDialog(parent)
{
    m_timer = new QTimer(this);
    m_timer->setTimerType(Qt::PreciseTimer);
    connect(m_timer, &QTimer::timeout, this, &StopWatch::update);
    m_targetTime = QDateTime::currentDateTime().addSecs(10);
    m_timer->start(1000);
}

void StopWatch::update()
{
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 secondsLeft = currentDateTime.secsTo(m_targetTime);
    qDebug() << secondsLeft;
}

And here's (a part of) the output:

4
3
2
1
0
0
-1
-2
-3
-4

So here we are: QDateTime::secsTo outputs 0 for the same QDateTime and for a QDateTime one second before.

I worked around this by doing

if (currentDateTime <= m_targetTime) {
    secondsLeft++;
}

but I don't understand the behavoir. Why is this the case?


Solution

  • Looking at the source code http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qdatetime.cpp

    int QTime::secsTo(const QTime &t) const
    {
        if (!isValid() || !t.isValid())
            return 0;
    
        // Truncate milliseconds as we do not want to consider them.
        int ourSeconds = ds() / 1000;
        int theirSeconds = t.ds() / 1000;
        return theirSeconds - ourSeconds;
    }
    

    It looks like it takes two positive integers that are under 1000, divides them by 1000, and then subtracts them from each other. If you use mSecsTo(), you will not have this problem.