blob: 5da69e76a544ef4311e9152324101c6dd1870049 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QCalendar>
Q_DECLARE_METATYPE(QCalendar::System)
class tst_QCalendar : public QObject
{
Q_OBJECT
private:
void checkYear(const QCalendar &cal, int year, bool normal=false);
private slots:
void basic_data();
void basic();
void unspecified_data() { basic_data(); }
void unspecified();
void nameCase();
void specific_data();
void specific();
void daily_data() { basic_data(); }
void daily();
};
// Support for basic():
void tst_QCalendar::checkYear(const QCalendar &cal, int year, bool normal)
{
const int moons = cal.monthsInYear(year);
// Months are numbered from 1 to moons:
QVERIFY(moons > 0);
QVERIFY(!cal.isDateValid(year, moons + 1, 1));
QVERIFY(!cal.isDateValid(year, 0, 1));
QVERIFY(moons <= cal.maximumMonthsInYear());
const int days = cal.daysInYear(year);
QVERIFY(days > 0);
int sum = 0;
const int longest = cal.maximumDaysInMonth();
for (int i = moons; i > 0; i--) {
const int last = cal.daysInMonth(i, year);
sum += last;
// Valid month has some days and no more than max:
QVERIFY(last > 0);
QVERIFY(last <= longest);
// Days are numbered from 1 to last:
QVERIFY(cal.isDateValid(year, i, 1));
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);
}
// Months add up to the whole year:
QCOMPARE(sum, days);
}
#define CHECKYEAR(cal, year) checkYear(cal, year); \
if (QTest::currentTestFailed()) \
return
#define NORMALYEAR(cal, year) checkYear(cal, year, true); \
if (QTest::currentTestFailed()) \
return
void tst_QCalendar::basic_data()
{
QTest::addColumn<QCalendar::System>("system");
QMetaEnum e = QCalendar::staticMetaObject.enumerator(0);
Q_ASSERT(qstrcmp(e.name(), "System") == 0);
for (int i = 0; i <= int(QCalendar::System::Last); ++i) {
// There may be gaps in the enum's numbering; and Last is a duplicate:
if (e.value(i) != -1 && qstrcmp(e.key(i), "Last"))
QTest::newRow(e.key(i)) << QCalendar::System(e.value(i));
}
}
void tst_QCalendar::basic()
{
QFETCH(QCalendar::System, system);
QCalendar cal(system);
QVERIFY(cal.isValid());
QCOMPARE(QCalendar(cal.name()).isGregorian(), cal.isGregorian());
QCOMPARE(QCalendar(cal.name()).name(), cal.name());
if (cal.hasYearZero()) {
CHECKYEAR(cal, 0);
} else {
QCOMPARE(cal.monthsInYear(0), 0);
QCOMPARE(cal.daysInYear(0), 0);
QVERIFY(!cal.isDateValid(0, 1, 1));
}
if (cal.isProleptic()) {
CHECKYEAR(cal, -1);
} else {
QCOMPARE(cal.monthsInYear(-1), 0);
QCOMPARE(cal.daysInYear(-1), 0);
QVERIFY(!cal.isDateValid(-1, 1, 1));
}
// Look for a leap year in the last decade.
int year = QDate::currentDate().year(cal);
for (int i = 10; i > 0 && !cal.isLeapYear(year); --i)
--year;
if (cal.isLeapYear(year)) {
// ... and a non-leap year within a decade before it.
int leap = year--;
for (int i = 10; i > 0 && cal.isLeapYear(year); --i)
year--;
if (!cal.isLeapYear(year))
QVERIFY(cal.daysInYear(year) < cal.daysInYear(leap));
CHECKYEAR(cal, leap);
}
// 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);
}
void tst_QCalendar::unspecified()
{
QFETCH(QCalendar::System, system);
QCalendar cal(system);
const QDate today = QDate::currentDate();
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;
// 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++;
}
// Require a majority of the years tested:
QVERIFY2(count > 9, "Default daysInMonth() should be for a normal year");
}
}
void tst_QCalendar::nameCase()
{
QVERIFY(QCalendar::availableCalendars().contains(QStringLiteral("Gregorian")));
}
void tst_QCalendar::specific_data()
{
QTest::addColumn<QCalendar::System>("system");
// Date in that system:
QTest::addColumn<int>("sysyear");
QTest::addColumn<int>("sysmonth");
QTest::addColumn<int>("sysday");
// Gregorian equivalent:
QTest::addColumn<int>("gregyear");
QTest::addColumn<int>("gregmonth");
QTest::addColumn<int>("gregday");
#define ADDROW(cal, year, month, day, gy, gm, gd) \
QTest::newRow(#cal) << QCalendar::System::cal << year << month << day << gy << gm << gd
ADDROW(Gregorian, 1970, 1, 1, 1970, 1, 1);
// One known specific date, for each calendar
#ifndef QT_BOOTSTRAPPED
// Julian 1582-10-4 was followed by Gregorian 1582-10-15
ADDROW(Julian, 1582, 10, 4, 1582, 10, 14);
// Milankovic matches Gregorian for a few centuries
ADDROW(Milankovic, 1923, 3, 20, 1923, 3, 20);
#endif
#if QT_CONFIG(jalalicalendar)
// Jalali year 1355 started on Gregorian 1976-3-21:
ADDROW(Jalali, 1355, 1, 1, 1976, 3, 21);
#endif // jalali
#if QT_CONFIG(islamiccivilcalendar)
// TODO: confirm this is correct
ADDROW(IslamicCivil, 1, 1, 1, 622, 7, 19);
#endif
#undef ADDROW
}
void tst_QCalendar::specific()
{
QFETCH(QCalendar::System, system);
QFETCH(int, sysyear);
QFETCH(int, sysmonth);
QFETCH(int, sysday);
QFETCH(int, gregyear);
QFETCH(int, gregmonth);
QFETCH(int, gregday);
const QCalendar cal(system);
const QDate date(sysyear, sysmonth, sysday, cal), gregory(gregyear, gregmonth, gregday);
QCOMPARE(date, gregory);
QCOMPARE(gregory.year(cal), sysyear);
QCOMPARE(gregory.month(cal), sysmonth);
QCOMPARE(gregory.day(cal), sysday);
QCOMPARE(date.year(), gregyear);
QCOMPARE(date.month(), gregmonth);
QCOMPARE(date.day(), gregday);
}
void tst_QCalendar::daily()
{
QFETCH(QCalendar::System, system);
QCalendar calendar(system);
const quint64 startJDN = 0, endJDN = 2488070;
// Iterate from -4713-01-01 (Julian calendar) to 2100-01-01
for (quint64 expect = startJDN; expect <= endJDN; ++expect)
{
QDate date = QDate::fromJulianDay(expect);
auto parts = calendar.partsFromDate(date);
if (!parts.isValid())
continue;
const int year = date.year(calendar);
QCOMPARE(year, parts.year);
const int month = date.month(calendar);
QCOMPARE(month, parts.month);
const int day = date.day(calendar);
QCOMPARE(day, parts.day);
const quint64 actual = QDate(year, month, day, calendar).toJulianDay();
QCOMPARE(actual, expect);
}
}
QTEST_APPLESS_MAIN(tst_QCalendar)
#include "tst_qcalendar.moc"