Change handling of unspecified year in QCalendar::daysInMonth()

Previously, this was specified to return the usual number of days in
the month. What the date-time parser needs, though, is the longest
that the relevant month can be, in any year. So change its
specification to that. This affects all calendar backends, effectively
requiring daysInMonth() to treat Unspecified as if it were a leap
year.

[ChangeLog][QtCore][QCalendar][Important Behavior Changes] The
daysInMonth(month, Unspecified) corner case is now specified to return
the greatest number of days in that month across all years, where
previously it returned the usual number of days in the month. This
means QCalendar().daysInMonth(2) is now 29 rather than 28, with
similar for other calendars than the Gregorian default.

Change-Id: I165599c655df9fbd77700ed38d8860a4e5cca881
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Edward Welbourne 2024-08-16 16:01:04 +02:00
parent b612286696
commit cd442e439b
5 changed files with 19 additions and 19 deletions

View File

@ -1259,7 +1259,7 @@ QCalendar::QCalendar(QAnyStringView name)
Months are numbered consecutively, starting with 1 for the first month of
each year. If \a year is \c Unspecified (its default, if not passed), the
month's length in a normal year is returned.
month's greatest length in any year is returned.
\sa maximumDaysInMonth(), minimumDaysInMonth()
*/

View File

@ -61,7 +61,7 @@ int QHijriCalendar::daysInMonth(int month, int year) const
if (year == 0 || month < 1 || month > 12)
return 0;
if (month == 12 && isLeapYear(year))
if (month == 12 && (year == QCalendar::Unspecified || isLeapYear(year)))
return 30;
return month % 2 == 0 ? 29 : 30;

View File

@ -156,7 +156,8 @@ QCalendar::YearMonthDay QJalaliCalendar::julianDayToDate(qint64 jd) const
int QJalaliCalendar::daysInMonth(int month, int year) const
{
if (year && month > 0 && month <= 12)
return month < 7 ? 31 : month < 12 || isLeapYear(year) ? 30 : 29;
return month < 7 ? 31 : (month < 12 || year == QCalendar::Unspecified
|| isLeapYear(year)) ? 30 : 29;
return 0;
}

View File

@ -29,7 +29,7 @@ int QRomanCalendar::daysInMonth(int month, int year) const
return 0;
if (month == 2)
return isLeapYear(year) ? 29 : 28;
return year == QCalendar::Unspecified || isLeapYear(year) ? 29 : 28;
// Long if odd up to July = 7, or if even from 8 = August onwards:
return 30 | ((month & 1) ^ (month >> 3));

View File

@ -11,7 +11,7 @@ class tst_QCalendar : public QObject
{
Q_OBJECT
private:
void checkYear(const QCalendar &cal, int year, bool normal=false);
void checkYear(const QCalendar &cal, int year);
private slots:
void basic_data();
@ -75,7 +75,7 @@ static void checkCenturyResolution(const QCalendar &cal, const QCalendar::YearMo
}
// Support for basic():
void tst_QCalendar::checkYear(const QCalendar &cal, int year, bool normal)
void tst_QCalendar::checkYear(const QCalendar &cal, int year)
{
const int moons = cal.monthsInYear(year);
// Months are numbered from 1 to moons:
@ -103,8 +103,8 @@ void tst_QCalendar::checkYear(const QCalendar &cal, int year, bool normal)
QVERIFY(cal.isDateValid(year, i, last));
QVERIFY(!cal.isDateValid(year, i, 0));
QVERIFY(!cal.isDateValid(year, i, last + 1));
if (normal) // Unspecified year gets same daysInMonth():
QCOMPARE(cal.daysInMonth(i), last);
// Unspecified year gets max daysInMonth():
QCOMPARE_GE(cal.daysInMonth(i), last);
checkCenturyResolution(cal, {year, i, (last + 1) / 2});
if (QTest::currentTestFailed())
@ -114,11 +114,7 @@ void tst_QCalendar::checkYear(const QCalendar &cal, int year, bool normal)
QCOMPARE(sum, days);
}
#define CHECKYEAR(cal, year) checkYear(cal, year); \
if (QTest::currentTestFailed()) \
return
#define NORMALYEAR(cal, year) checkYear(cal, year, true); \
#define CHECKYEAR(cal, year) checkYear(cal, year); \
if (QTest::currentTestFailed()) \
return
@ -177,7 +173,7 @@ void tst_QCalendar::basic()
}
// Either year is non-leap or we have a decade of leap years together;
// expect daysInMonth() to treat year the same as unspecified.
NORMALYEAR(cal, year);
CHECKYEAR(cal, year);
}
void tst_QCalendar::unspecified()
@ -189,15 +185,18 @@ void tst_QCalendar::unspecified()
const int thisYear = today.year();
QCOMPARE(cal.monthsInYear(QCalendar::Unspecified), cal.maximumMonthsInYear());
for (int month = cal.maximumMonthsInYear(); month > 0; month--) {
const int days = cal.daysInMonth(month);
int count = 0;
const int maxDays = cal.daysInMonth(month);
bool hitMax = false;
// 19 years = one Metonic cycle (used by some lunar calendars)
for (int i = 19; i > 0; --i) {
if (cal.daysInMonth(month, thisYear - i) == days)
count++;
int days = cal.daysInMonth(month, thisYear - i);
if (days == maxDays)
hitMax = true;
else
QCOMPARE_LT(days, maxDays);
}
// Require a majority of the years tested:
QVERIFY2(count > 9, "Default daysInMonth() should be for a normal year");
QVERIFY2(hitMax, "Default daysInMonth() should be the longest that month gets");
}
}