diff options
-rw-r--r-- | dist/changes-5.0.0 | 4 | ||||
-rw-r--r-- | src/corelib/tools/qdatetime.cpp | 165 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qdate/tst_qdate.cpp | 65 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp | 8 |
4 files changed, 62 insertions, 180 deletions
diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index ed5ddc240d..e5f57732ba 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -241,6 +241,10 @@ QtCore in Qt 4 they returned a null QString or a null QStringRef. * QDate, QTime, and QDateTime have undergone important behavioural changes: + * QDate only implements the Gregorian calendar, the switch to the Julian + calendar before 1582 has been removed. This means all QDate methods will + return different results for dates prior to 15 October 1582, and there is + no longer a gap between 4 October 1582 and 15 October 1582. * QDate::setYMD() is deprecated, use QDate::setDate() instead * Most methods now apply strict validity checks and will return appropriate and consistent values when invalid. For example, QDate::year() will return diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index e05eb371dd..8649690c72 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -82,7 +82,7 @@ enum { MSECS_PER_HOUR = 3600000, SECS_PER_MIN = 60, MSECS_PER_MIN = 60000, - JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromGregorianDate(1970, 1, 1) + JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1) }; static inline QDate fixedDate(int y, int m, int d) @@ -92,66 +92,41 @@ static inline QDate fixedDate(int y, int m, int d) return result; } -static inline qint64 julianDayFromGregorianDate(qint64 year, int month, int day) +static inline qint64 julianDayFromDate(qint64 year, int month, int day) { - // Gregorian calendar starting from October 15, 1582 + // Gregorian calendar // Algorithm from Henry F. Fliegel and Thomas C. Van Flandern + + if (year < 0) + ++year; + return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075; } -static qint64 julianDayFromDate(qint64 year, int month, int day) -{ - if (year > 1582 || (year == 1582 && (month > 10 || (month == 10 && day >= 15)))) { - return julianDayFromGregorianDate(year, month, day); - } else if (year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day <= 4)))) { - // Julian calendar until October 4, 1582 - // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering - if (year < 0) - ++year; - int a = (14 - month) / 12; - return (153 * (month + (12 * a) - 3) + 2) / 5 - + (1461 * (year + 4800 - a)) / 4 - + day - 32083; - } else { - // the day following October 4, 1582 is October 15, 1582 - return std::numeric_limits<qint64>::min(); // i.e. nullJd() - } -} - static void getDateFromJulianDay(qint64 julianDay, int *year, int *month, int *day) { int y, m, d; - if (julianDay >= 2299161) { - // Gregorian calendar starting from October 15, 1582 - // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern - qint64 ell, n, i, j; //TODO These will need to be bigger to prevent overflow!!! - ell = julianDay + 68569; - n = (4 * ell) / 146097; - ell = ell - (146097 * n + 3) / 4; - i = (4000 * (ell + 1)) / 1461001; - ell = ell - (1461 * i) / 4 + 31; - j = (80 * ell) / 2447; - d = ell - (2447 * j) / 80; - ell = j / 11; - m = j + 2 - (12 * ell); - y = 100 * (n - 49) + i + ell; - } else { - // Julian calendar until October 4, 1582 - // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering - julianDay += 32082; - qint64 dd = (4 * julianDay + 3) / 1461; //TODO These may need to be bigger to prevent overflow!!! - qint64 ee = julianDay - (1461 * dd) / 4; //TODO These may need to be bigger to prevent overflow!!! - qint64 mm = ((5 * ee) + 2) / 153; //TODO These may need to be bigger to prevent overflow!!! - d = ee - (153 * mm + 2) / 5 + 1; - m = mm + 3 - 12 * (mm / 10); - y = dd - 4800 + (mm / 10); - if (y <= 0) - --y; - } + // Gregorian calendar + // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern + qint64 ell, n, i, j; //TODO These will need to be bigger to prevent overflow!!! + ell = julianDay + 68569; + n = (4 * ell) / 146097; + ell = ell - (146097 * n + 3) / 4; + i = (4000 * (ell + 1)) / 1461001; + ell = ell - (1461 * i) / 4 + 31; + j = (80 * ell) / 2447; + d = ell - (2447 * j) / 80; + ell = j / 11; + m = j + 2 - (12 * ell); + y = 100 * (n - 49) + i + ell; + + if (y<= 0) + --y; + if (year) *year = y; if (month) @@ -197,12 +172,10 @@ static QString fmtDateTime(const QString& f, const QTime* dt = 0, const QDate* d A QDate object contains a calendar date, i.e. year, month, and day - numbers, in the Gregorian calendar. (see \l{QDate G and J} {Use of - Gregorian and Julian Calendars} for dates prior to 15 October - 1582). It can read the current date from the system clock. It - provides functions for comparing dates, and for manipulating - dates. For example, it is possible to add and subtract days, - months, and years to dates. + numbers, in the Gregorian calendar. It can read the current date + from the system clock. It provides functions for comparing dates, + and for manipulating dates. For example, it is possible to add + and subtract days, months, and years to dates. A QDate object is typically created either by giving the year, month, and day numbers explicitly. Note that QDate interprets two @@ -233,26 +206,6 @@ static QString fmtDateTime(const QString& f, const QTime* dt = 0, const QDate* d \section1 - \target QDate G and J - \section2 Use of Gregorian and Julian Calendars - - QDate uses the Gregorian calendar in all locales, beginning - on the date 15 October 1582. For dates up to and including 4 - October 1582, the Julian calendar is used. This means there is a - 10-day gap in the internal calendar between the 4th and the 15th - of October 1582. When you use QDateTime for dates in that epoch, - the day after 4 October 1582 is 15 October 1582, and the dates in - the gap are invalid. - - The Julian to Gregorian changeover date used here is the date when - the Gregorian calendar was first introduced, by Pope Gregory - XIII. That change was not universally accepted and some localities - only executed it at a later date (if at all). QDateTime - doesn't take any of these historical facts into account. If an - application must support a locale-specific dating system, it must - do so on its own, remembering to convert the dates using the - Julian day. - \section2 No Year 0 There is no year 0. Dates in that year are considered invalid. The @@ -985,11 +938,6 @@ QDate QDate::addDays(qint64 ndays) const resulting month/year, this function will return a date that is the latest valid date. - \warning QDate has a date hole around the days introducing the - Gregorian calendar (the days 5 to 14 October 1582, inclusive, do - not exist). If the calculation ends in one of those days, QDate - will return either October 4 or October 15. - \sa addDays() addYears() */ @@ -1039,10 +987,6 @@ QDate QDate::addMonths(int nmonths) const // yes, adjust the date by +1 or -1 years y += increasing ? +1 : -1; - // did we end up in the Gregorian/Julian conversion hole? - if (y == 1582 && m == 10 && d > 4 && d < 15) - d = increasing ? 15 : 4; - return fixedDate(y, m, d); } @@ -1332,14 +1276,10 @@ QDate QDate::fromString(const QString &string, const QString &format) bool QDate::isValid(int year, int month, int day) { - // there is no year 0 in the Julian calendar + // there is no year 0 in the Gregorian calendar if (year == 0) return false; - // passage from Julian to Gregorian calendar - if (year == 1582 && month == 10 && day > 4 && day < 15) - return false; - return (day > 0 && month > 0 && month <= 12) && (day <= monthDays[month] || (day == 29 && month == 2 && isLeapYear(year))); } @@ -1353,14 +1293,11 @@ bool QDate::isValid(int year, int month, int day) bool QDate::isLeapYear(int y) { - if (y < 1582) { - 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; - } + // No year 0 in Gregorian calendar, so -1, -5, -9 etc are leap years + if ( y < 1) + ++y; + + return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; } /*! \fn static QDate QDate::fromJulianDay(int jd) @@ -2088,26 +2025,6 @@ int QTime::elapsed() const \section1 - \target QDateTime G and J - \section2 Use of Gregorian and Julian Calendars - - QDate uses the Gregorian calendar in all locales, beginning - on the date 15 October 1582. For dates up to and including 4 - October 1582, the Julian calendar is used. This means there is a - 10-day gap in the internal calendar between the 4th and the 15th - of October 1582. When you use QDateTime for dates in that epoch, - the day after 4 October 1582 is 15 October 1582, and the dates in - the gap are invalid. - - The Julian to Gregorian changeover date used here is the date when - the Gregorian calendar was first introduced, by Pope Gregory - XIII. That change was not universally accepted and some localities - only executed it at a later date (if at all). QDateTime - doesn't take any of these historical facts into account. If an - application must support a locale-specific dating system, it must - do so on its own, remembering to convert the dates using the - Julian day. - \section2 No Year 0 There is no year 0. Dates in that year are considered invalid. The @@ -2134,16 +2051,6 @@ int QTime::elapsed() const shortcomings in the available conversion formulas. Conversions outside this range are not guaranteed to be correct. This may change in the future. - The Gregorian calendar was introduced in different places around - the world on different dates. QDateTime uses QDate to store the - date, so it uses the Gregorian calendar for all locales, beginning - on the date 15 October 1582. For dates up to and including 4 - October 1582, QDateTime uses the Julian calendar. This means - there is a 10-day gap in the QDateTime calendar between the 4th - and the 15th of October 1582. When you use QDateTime for dates in - that epoch, the day after 4 October 1582 is 15 October 1582, and - the dates in the gap are invalid. - \section2 Use of System Timezone @@ -3052,8 +2959,8 @@ qint64 QDateTime::currentMSecsSinceEpoch() GetSystemTime(&st); return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) + - qint64(julianDayFromGregorianDate(st.wYear, st.wMonth, st.wDay) - - julianDayFromGregorianDate(1970, 1, 1)) * Q_INT64_C(86400000); + qint64(julianDayFromDate(st.wYear, st.wMonth, st.wDay) + - julianDayFromDate(1970, 1, 1)) * Q_INT64_C(86400000); } #elif defined(Q_OS_UNIX) diff --git a/tests/auto/corelib/tools/qdate/tst_qdate.cpp b/tests/auto/corelib/tools/qdate/tst_qdate.cpp index 4ce2a51902..4921a7e734 100644 --- a/tests/auto/corelib/tools/qdate/tst_qdate.cpp +++ b/tests/auto/corelib/tools/qdate/tst_qdate.cpp @@ -185,10 +185,10 @@ void tst_QDate::isValid_data() QTest::newRow("idec") << 2000 << 12 << 32 << nullJd << false; // the beginning of the Julian Day calendar: - QTest::newRow("jd earliest formula") << -4800 << 1 << 1 << qint64( -31776) << true; - QTest::newRow("jd -1") << -4714 << 12 << 31 << qint64( -1) << true; - QTest::newRow("jd 0") << -4713 << 1 << 1 << qint64( 0) << true; - QTest::newRow("jd 1") << -4713 << 1 << 2 << qint64( 1) << true; + QTest::newRow("jd earliest formula") << -4800 << 1 << 1 << qint64( -31738) << true; + QTest::newRow("jd -1") << -4714 << 11 << 23 << qint64( -1) << true; + QTest::newRow("jd 0") << -4714 << 11 << 24 << qint64( 0) << true; + QTest::newRow("jd 1") << -4714 << 11 << 25 << qint64( 1) << true; QTest::newRow("jd latest formula") << 1400000 << 12 << 31 << qint64(513060925) << true; } @@ -259,19 +259,14 @@ void tst_QDate::dayOfWeek_data() QTest::newRow("data5") << 2000 << 1 << 7 << 5; QTest::newRow("data6") << 2000 << 1 << 8 << 6; QTest::newRow("data7") << 2000 << 1 << 9 << 7; - QTest::newRow("data8") << 1815 << 6 << 15 << 4; - QTest::newRow("data9") << 1815 << 6 << 15 << 4; - QTest::newRow("data10") << 1500 << 1 << 1 << 3; - QTest::newRow("data11") << -1500 << 1 << 1 << 7; - QTest::newRow("data12") << -4800 << 1 << 1 << 5; - QTest::newRow("data13") << -4800 << 1 << 4 << 1; - QTest::newRow("data14") << -4800 << 1 << 5 << 2; - QTest::newRow("data15") << -4800 << 1 << 6 << 3; - QTest::newRow("data16") << -4800 << 1 << 7 << 4; - QTest::newRow("data17") << -4800 << 1 << 8 << 5; - QTest::newRow("data18") << -4800 << 1 << 9 << 6; - QTest::newRow("data19") << -4800 << 1 << 10 << 7; - QTest::newRow("data20") << -4800 << 1 << 11 << 1; + QTest::newRow("data8") << -4800 << 1 << 1 << 1; + QTest::newRow("data9") << -4800 << 1 << 2 << 2; + QTest::newRow("data10") << -4800 << 1 << 3 << 3; + QTest::newRow("data12") << -4800 << 1 << 4 << 4; + QTest::newRow("data12") << -4800 << 1 << 5 << 5; + QTest::newRow("data13") << -4800 << 1 << 6 << 6; + QTest::newRow("data14") << -4800 << 1 << 7 << 7; + QTest::newRow("data15") << -4800 << 1 << 8 << 1; } void tst_QDate::dayOfWeek() @@ -300,9 +295,9 @@ void tst_QDate::dayOfYear_data() QTest::newRow("data5") << 2001 << 12 << 31 << 365; QTest::newRow("data6") << 1815 << 1 << 1 << 1; QTest::newRow("data7") << 1815 << 12 << 31 << 365; - QTest::newRow("data8") << 1582 << 1 << 1 << 1; - QTest::newRow("data9") << 1582 << 12 << 31 << 355; - QTest::newRow("data10") << 1500 << 1 << 1 << 1; + QTest::newRow("data8") << 1500 << 1 << 1 << 1; + QTest::newRow("data9") << 1500 << 12 << 31 << 365; + QTest::newRow("data10") << -1500 << 1 << 1 << 1; QTest::newRow("data11") << -1500 << 12 << 31 << 365; QTest::newRow("data12") << -4800 << 1 << 1 << 1; QTest::newRow("data13") << -4800 << 12 << 31 << 365; @@ -589,14 +584,6 @@ void tst_QDate::addMonths_data() QTest::newRow( "data15" ) << 1 << 1 << 1 << -12 << -1 << 1 << 1; QTest::newRow( "data16" ) << -1 << 12 << 1 << 1 << 1 << 1 << 1; QTest::newRow( "data17" ) << -1 << 1 << 1 << 12 << 1 << 1 << 1; - - // Gregorian/Julian switchover - QTest::newRow( "data18" ) << 1582 << 9 << 4 << 1 << 1582 << 10 << 4; - QTest::newRow( "data19" ) << 1582 << 9 << 10 << 1 << 1582 << 10 << 15; - QTest::newRow( "data20" ) << 1582 << 9 << 20 << 1 << 1582 << 10 << 20; - QTest::newRow( "data21" ) << 1582 << 11 << 4 << -1 << 1582 << 10 << 4; - QTest::newRow( "data22" ) << 1582 << 11 << 10 << -1 << 1582 << 10 << 4; - QTest::newRow( "data23" ) << 1582 << 11 << 20 << -1 << 1582 << 10 << 20; } void tst_QDate::addYears() @@ -930,10 +917,10 @@ void tst_QDate::isLeapYear() QVERIFY(QDate::isLeapYear(4)); QVERIFY(!QDate::isLeapYear(7)); QVERIFY(QDate::isLeapYear(8)); - QVERIFY(QDate::isLeapYear(100)); + QVERIFY(!QDate::isLeapYear(100)); QVERIFY(QDate::isLeapYear(400)); - QVERIFY(QDate::isLeapYear(700)); - QVERIFY(QDate::isLeapYear(1500)); + QVERIFY(!QDate::isLeapYear(700)); + QVERIFY(!QDate::isLeapYear(1500)); QVERIFY(QDate::isLeapYear(1600)); QVERIFY(!QDate::isLeapYear(1700)); QVERIFY(!QDate::isLeapYear(1800)); @@ -1184,24 +1171,16 @@ void tst_QDate::roundtrip() const // Test Julian round trip around JD 0 and current low end of valid range QDate testDate; - QDate loopDate = QDate::fromJulianDay(-31776); // 1 Jan 4800 BC - while (loopDate.toJulianDay() <= 5113) { // 31 Dec 4700 AD + QDate loopDate = QDate::fromJulianDay(-31738); // 1 Jan 4800 BC + while (loopDate.toJulianDay() <= 5150) { // 31 Dec 4700 BC testDate.setDate(loopDate.year(), loopDate.month(), loopDate.day()); QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay()); loopDate = loopDate.addDays(1); } // Test Julian round trip in both BC and AD - 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 + loopDate = QDate::fromJulianDay(1684901); // 1 Jan 100 BC + while (loopDate.toJulianDay() <= 1757949) { // 31 Dec 100 AD testDate.setDate(loopDate.year(), loopDate.month(), loopDate.day()); QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay()); loopDate = loopDate.addDays(1); diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp index 2cb3db533e..af8deefa4c 100644 --- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp @@ -726,14 +726,6 @@ void tst_QDateTime::addSecs_data() QTest::newRow("toPositive") << QDateTime(QDate(-1, 12, 31), QTime(23, 59, 59), Qt::UTC) << 1 << QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC); - - // Gregorian/Julian switchover - QTest::newRow("toGregorian") << QDateTime(QDate(1582, 10, 4), QTime(23, 59, 59)) - << 1 - << QDateTime(QDate(1582, 10, 15), QTime(0, 0, 0)); - QTest::newRow("toJulian") << QDateTime(QDate(1582, 10, 15), QTime(0, 0, 0)) - << -1 - << QDateTime(QDate(1582, 10, 4), QTime(23, 59, 59)); } void tst_QDateTime::addSecs() |