diff options
author | John Layt <john@layt.net> | 2010-03-30 08:55:33 (GMT) |
---|---|---|
committer | Olivier Goffart <ogoffart@trolltech.com> | 2010-03-30 08:56:24 (GMT) |
commit | 568475d98d77c3ade027eb88e571ca8b84088002 (patch) | |
tree | 8f139c3b9b7722f572ba65b1281e6c020b59fd4c | |
parent | bc4fe607fe65dca24460d4f612c5815b4767e988 (diff) | |
download | Qt-568475d98d77c3ade027eb88e571ca8b84088002.zip Qt-568475d98d77c3ade027eb88e571ca8b84088002.tar.gz Qt-568475d98d77c3ade027eb88e571ca8b84088002.tar.bz2 |
Fix QDate::isLeapYear() for years < 1
The current formula for isLeapYear() assumes that years -4, -8, etc are
leap years in the Julian Calendar. This is not the case, leap years in
fact fall in -1, -5, -9, etc as there is no year 0 in the Julian
Calendar (count back 4 years from 4AD to confirm). The
julianDayFromDate() and getDateFromJulianDay() functions correctly
calculate this, but isValid() uses isLeapYear() and so causes setDate()
to incorrectly reject valid dates.
Sample test code to round-trip a jd to ymd and back to jd:
QDate testDate;
QDate loopDate = QDate::fromJulianDay(1);
while ( loopDate.toJulianDay() <= 5373484 ) { // <= 9999-12-31
testDate.setDate( loopDate.year(),
loopDate.month(),
loopDate.day() );
if ( !testDate.isValid() || loopDate != testDate ) {
qDebug() << "Round Trip failed : " <<
loopDate.toJulianDay() << " = " <<
loopDate.year() << loopDate.month() <<
loopDate.day() << " = " <<
testDate.toJulianDay();
}
loopDate.addDays(1);
}
Before the fix, 29 Feb 1 BC (year = -1) and all other BC leap days
returned by year() month() day() would result in setDate() failing.
After the fix the round trip works fine.
Merge-request: 2282
Reviewed-by: Olivier Goffart <ogoffart@trolltech.com>
-rw-r--r-- | src/corelib/tools/qdatetime.cpp | 5 | ||||
-rw-r--r-- | tests/auto/qdate/tst_qdate.cpp | 50 |
2 files changed, 46 insertions, 9 deletions
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 54a4205..3e34d62 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -1336,7 +1336,10 @@ bool QDate::isValid(int year, int month, int day) bool QDate::isLeapYear(int y) { if (y < 1582) { - return qAbs(y) % 4 == 0; + if ( y < 1) { // No year 0 in Julian calendar, so -1, -5, -9 etc are leap years + ++y; + } + return y % 4 == 0; } else { return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; } diff --git a/tests/auto/qdate/tst_qdate.cpp b/tests/auto/qdate/tst_qdate.cpp index 7223291..60772eb 100644 --- a/tests/auto/qdate/tst_qdate.cpp +++ b/tests/auto/qdate/tst_qdate.cpp @@ -99,6 +99,7 @@ private slots: void standaloneShortMonthName() const; void longMonthName() const; void standaloneLongMonthName() const; + void roundtrip() const; }; Q_DECLARE_METATYPE(QDate) @@ -668,17 +669,18 @@ void tst_QDate::toString_format() void tst_QDate::isLeapYear() { - QVERIFY(QDate::isLeapYear(-4444)); - QVERIFY(!QDate::isLeapYear(-4443)); - QVERIFY(QDate::isLeapYear(-8)); - QVERIFY(!QDate::isLeapYear(-7)); - QVERIFY(QDate::isLeapYear(-4)); + QVERIFY(QDate::isLeapYear(-4445)); + QVERIFY(!QDate::isLeapYear(-4444)); + QVERIFY(!QDate::isLeapYear(-6)); + QVERIFY(QDate::isLeapYear(-5)); + QVERIFY(!QDate::isLeapYear(-4)); QVERIFY(!QDate::isLeapYear(-3)); QVERIFY(!QDate::isLeapYear(-2)); - QVERIFY(!QDate::isLeapYear(-1)); - QVERIFY(!QDate::isLeapYear(1)); - QVERIFY(!QDate::isLeapYear(1)); + QVERIFY(QDate::isLeapYear(-1)); + QVERIFY(!QDate::isLeapYear(0)); // Doesn't exist QVERIFY(!QDate::isLeapYear(1)); + QVERIFY(!QDate::isLeapYear(2)); + QVERIFY(!QDate::isLeapYear(3)); QVERIFY(QDate::isLeapYear(4)); QVERIFY(!QDate::isLeapYear(7)); QVERIFY(QDate::isLeapYear(8)); @@ -910,5 +912,37 @@ void tst_QDate::standaloneLongMonthName() const } } +void tst_QDate::roundtrip() const +{ + // Test round trip, this exercises setDate(), isValid(), isLeapYear(), + // year(), month(), day(), julianDayFromDate(), and getDateFromJulianDay() + // to ensure they are internally consistent (but doesn't guarantee correct) + + // Test Julian round trip in both BC and AD + QDate testDate; + QDate loopDate = QDate::fromJulianDay(1684899); // 1 Jan 100 BC + while ( loopDate.toJulianDay() <= 1757948 ) { // 31 Dec 100 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } + + // Test Julian and Gregorian round trip during changeover period + loopDate = QDate::fromJulianDay(2298153); // 1 Jan 1580 AD + while ( loopDate.toJulianDay() <= 2300334 ) { // 31 Dec 1585 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } + + // Test Gregorian round trip during current useful period + loopDate = QDate::fromJulianDay(2378497); // 1 Jan 1900 AD + while ( loopDate.toJulianDay() <= 2488433 ) { // 31 Dec 2100 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } +} + QTEST_APPLESS_MAIN(tst_QDate) #include "tst_qdate.moc" |