From 17d0bf258b40064f35189ca3cf62b3179ca65c72 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 2 Mar 2023 11:06:53 +0100 Subject: [PATCH] Fix counting of number of months from one date to another QQuickCalendarModelPrivate::getCount() was kludgey and wrong. Rewrite it to use QCalendar::partsFromDate(), thereby bypassing repeat calls to calendrical calculations, document what it's meant to be doing and do that. Fixes: QTBUG-111634 Pick-to: 6.5 6.4 Change-Id: I3bdf0233fc1b170eaeb1948ba74e93f636a29bd7 Reviewed-by: Qt CI Bot Reviewed-by: Mitch Curtis --- src/quicktemplates/qquickcalendarmodel.cpp | 18 +++++++------ .../controls/data/tst_calendarmodel.qml | 25 +++++++++++++------ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/quicktemplates/qquickcalendarmodel.cpp b/src/quicktemplates/qquickcalendarmodel.cpp index 8e2cfe3178..a1605ba77a 100644 --- a/src/quicktemplates/qquickcalendarmodel.cpp +++ b/src/quicktemplates/qquickcalendarmodel.cpp @@ -52,21 +52,23 @@ public: int count; }; +// Returns the number of months we need to display for both from and to to be shown, +// or zero if from is in a later month than to, or either is invalid. int QQuickCalendarModelPrivate::getCount(QDate from, QDate to) { if (!from.isValid() || !to.isValid()) return 0; - QDate f(from.year(), from.month(), 1); - QDate t(to.year(), to.month(), to.daysInMonth()); - int days = f.daysTo(t); - if (days < 0) + const QCalendar gregorian; + Q_ASSERT(gregorian.isGregorian()); + const QCalendar::YearMonthDay &f = gregorian.partsFromDate(from); + const QCalendar::YearMonthDay &t = gregorian.partsFromDate(to); + Q_ASSERT(f.isValid() && t.isValid()); // ... because from and to are valid. + if (f.year > t.year || (f.year == t.year && f.month > t.month)) return 0; - QDate r = QDate(1, 1, 1).addDays(days); - int years = r.year() - 1; - int months = r.month() - 1; - return 12 * years + months + (r.day() / t.day()); + // Count from's month and every subsequent month until to's: + return 1 + t.month + 12 * (t.year - f.year) - f.month; } void QQuickCalendarModelPrivate::populate(QDate f, QDate t, bool force) diff --git a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml index 19070e074d..a2f843678f 100644 --- a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml +++ b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml @@ -31,23 +31,32 @@ TestCase { function test_indices_data() { return [ + // "from" and "to" must currently be in the same year. { tag: "2013", from: "2013-01-01", to: "2013-12-31", count: 12 }, - { tag: "2016", from: "2016-01-01", to: "2016-03-31", count: 3 } + { tag: "2016", from: "2016-01-01", to: "2016-03-31", count: 3 }, + { tag: "2016-02-01 to 2016-12-31", from: "2016-02-01", to: "2016-12-31", count: 11 }, + { tag: "2014-11-30 to 2016-01-01", from: "2014-11-30", to: "2016-01-01", count: 15 } ] } function test_indices(data) { - var model = calendarModel.createObject(testCase, {from: data.from, to: data.to}) + let model = calendarModel.createObject(testCase, {from: data.from, to: data.to}) verify(model) compare(model.count, data.count) - var y = parseInt(data.tag) - for (var m = 0; m < 12; ++m) { - compare(model.yearAt(m), y) - compare(model.indexOf(y, m), m) - compare(model.indexOf(new Date(y, m, 1)), m) - compare(model.monthAt(m), m) + const from = new Date(data.from) + const to = new Date(data.to) + let index = 0 + for (let date = from; date <= to; date.setMonth(date.getMonth() + 1, 28), ++index) { + compare(model.yearAt(index), date.getFullYear(), + `yearAt(${index}) returned incorrect value`) + compare(model.indexOf(date.getFullYear(), date.getMonth()), index, + `indexOf(${date.getFullYear()}, ${date.getMonth()}) returned incorrect value`) + compare(model.indexOf(date), index, + `indexOf(${date}) returned incorrect value`) + compare(model.monthAt(index), date.getMonth(), + `monthAt(${index}) returned incorrect value`) } model.destroy()