nasauber.de

Blog

Calculating the difference between two QDates

I wanted to calculate the difference between two QDates. Not only the days, but also the years, months, and weeks (for use in KPhotoAlbum).

I ended up using the following algorithm:

[Update 2024-02-07]: In some cases, the days weren't calculated correctly, becase a day surpassing the days in that specific month has been used, resulting in an invalid QDate. This is now fixed.

[Update 2024-03-05]: Now, we also calculate timespans correctly if the date we refer to is a February 29.

struct DateDifference
{
    int years;
    int months;
    int days;

    bool operator==(const DateDifference &other) const
    {
        return    this->years  == other.years
               && this->months == other.months
               && this->days   == other.days;
    }

    bool operator!=(const DateDifference &other) const
    {
        return    this->years  != other.years
               || this->months != other.months
               || this->days   != other.days;
    }
};

DateDifference dateDifference(const QDate &date, const QDate &reference)
{
    if (date > reference) {
        return dateDifference(reference, date);
    }

    int dateDay = date.day();
    if (date.month() == 2 && dateDay == 29
        && ! QDate::isLeapYear(reference.year())) {
        // If we calculate the timespan to a February 29 for a non-leap year, we use February 28
        // instead (the last day in February). This will also make birthdays for people born on
        // February 29 being calculated correctly (February 28, the last day in February, for
        // non-leap years)
        dateDay = 28;
    }

    int years = reference.year() - date.year();
    int months = reference.month() - date.month();
    if (reference.month() < date.month()
        || ((reference.month() == date.month()) && (reference.day() < dateDay))) {
        years--;
        months += 12;
    }
    if (reference.day() < dateDay) {
        months--;
    }

    int remainderMonth = reference.month() - (reference.day() < dateDay);
    int remainderYear = reference.year();
    if (remainderMonth == 0) {
        remainderMonth = 12;
        remainderYear--;
    }

    const auto daysOfRemainderMonth = QDate(remainderYear, remainderMonth, 1).daysInMonth();
    const auto remainderDay = dateDay > daysOfRemainderMonth ? daysOfRemainderMonth : dateDay;

    int days = QDate(remainderYear, remainderMonth, remainderDay).daysTo(reference);

    return { years, months, days };
}

Perhaps, this will help somebody.

I also filed a Feature request about adding such a function to Qt directly. Hopefully, it will be added in Qt 5.14 (to the new QCalendar class) :-)