summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib
diff options
context:
space:
mode:
authorJon Severinsson <jon@severinsson.net>2012-10-08 06:46:28 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-11-02 12:16:49 +0100
commit53e6cb3ff676f91a51d55e5740c7174f0c15e390 (patch)
tree22f10a63b714f0a0d9f49071916ab5d4f7f6963b /tests/auto/corelib
parentf01b498310ea7c05ec73669b34cb83b9f159006f (diff)
Fix the gregorian date <-> julian day calculations in QDate
The old code is just plain wrong for negative julian days. Replaced with plain math from The Calendar FAQ [1], which is correct for all julian days, provided you use mathematical integer division (round to negative infinity) rather than c++11 integer division (round to zero). [1] http://www.tondering.dk/claus/cal/julperiod.php While the conversion code works for up to around JD +/- (2^63/4), we only use an int for the year in the API, so this patch limits minJd() and maxJd() to 1 Jan (2^31) BC and 31 Dec (2^31-1) AD, respectively. Note that while the new conversion code looks like it would be more expensive than the old, gcc will in fact be able to optimize it to be slightly faster (probably because x86 hardware implements round to negative infinity, and so GCC manages to optimize floordiv to a single instruction, compared to the three instuctions needed for operator/). In the following test application, run with a release mode Qt and redirecting stderr to /dev/null, I measured an improvement from 6.81s +/- 0.08s to 6.26s +/- 0.16s user time over five runs on an otherwise idle x86_64 system. int main(int, char *[]) { int year, month, day; qint64 jd; for (qint64 i = Q_INT64_C(-1048576) ; i < Q_INT64_C(1048576); ++i) { QDate::fromJulianDay(i).getDate(&year, &month, &day); jd = QDate(year, month, day).toJulianDay(); qDebug() << jd << year << month << day; } } Change-Id: Ifd0dd01f0027f260401f7f9b4f1201d2b7a3b087 Reviewed-by: David Faure (KDE) <faure@kde.org> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'tests/auto/corelib')
-rw-r--r--tests/auto/corelib/tools/qdate/tst_qdate.cpp34
1 files changed, 23 insertions, 11 deletions
diff --git a/tests/auto/corelib/tools/qdate/tst_qdate.cpp b/tests/auto/corelib/tools/qdate/tst_qdate.cpp
index 01805e1271..9978a252bf 100644
--- a/tests/auto/corelib/tools/qdate/tst_qdate.cpp
+++ b/tests/auto/corelib/tools/qdate/tst_qdate.cpp
@@ -117,8 +117,8 @@ void tst_QDate::isNull_data()
QTest::addColumn<qint64>("jd");
QTest::addColumn<bool>("null");
- qint64 minJd = std::numeric_limits<qint64>::min() / 2;
- qint64 maxJd = std::numeric_limits<qint64>::max() / 2;
+ qint64 minJd = Q_INT64_C(-784350574879);
+ qint64 maxJd = Q_INT64_C( 784354017364);
QTest::newRow("qint64 min") << std::numeric_limits<qint64>::min() << true;
QTest::newRow("minJd - 1") << minJd - 1 << true;
@@ -448,8 +448,8 @@ void tst_QDate::julianDaysLimits()
{
qint64 min = std::numeric_limits<qint64>::min();
qint64 max = std::numeric_limits<qint64>::max();
- qint64 minJd = std::numeric_limits<qint64>::min() / 2;
- qint64 maxJd = std::numeric_limits<qint64>::max() / 2;
+ qint64 minJd = Q_INT64_C(-784350574879);
+ qint64 maxJd = Q_INT64_C( 784354017364);
QDate maxDate = QDate::fromJulianDay(maxJd);
QDate minDate = QDate::fromJulianDay(minJd);
@@ -492,7 +492,7 @@ void tst_QDate::julianDaysLimits()
dt = minDate.addDays(min);
QCOMPARE(dt.isValid(), false);
dt = minDate.addDays(max);
- QCOMPARE(dt.isValid(), true);
+ QCOMPARE(dt.isValid(), false);
dt = zeroDate.addDays(-1);
QCOMPARE(dt.isValid(), true);
@@ -664,8 +664,8 @@ void tst_QDate::addYears_data()
void tst_QDate::daysTo()
{
- qint64 minJd = std::numeric_limits<qint64>::min() / 2;
- qint64 maxJd = std::numeric_limits<qint64>::max() / 2;
+ qint64 minJd = Q_INT64_C(-784350574879);
+ qint64 maxJd = Q_INT64_C( 784354017364);
QDate dt1(2000, 1, 1);
QDate dt2(2000, 1, 5);
@@ -1356,9 +1356,10 @@ void tst_QDate::roundtrip() const
// year(), month(), day(), julianDayFromDate(), and getDateFromJulianDay()
// to ensure they are internally consistent (but doesn't guarantee correct)
- // Test Julian round trip around JD 0 and current low end of valid range
+ // Test Julian round trip around JD 0 and the c++ integer division rounding
+ // problem point (eg. negative numbers) in the conversion functions.
QDate testDate;
- QDate loopDate = QDate::fromJulianDay(-31738); // 1 Jan 4800 BC
+ QDate loopDate = QDate::fromJulianDay(-50001); // 1 Jan 4850 BC
while (loopDate.toJulianDay() <= 5150) { // 31 Dec 4700 BC
testDate.setDate(loopDate.year(), loopDate.month(), loopDate.day());
QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay());
@@ -1389,9 +1390,20 @@ void tst_QDate::roundtrip() const
loopDate = loopDate.addDays(1);
}
+ qint64 minJd = Q_INT64_C(-784350574879);
+ qint64 maxJd = Q_INT64_C( 784354017364);
+
// Test Gregorian round trip at top end of conversion range
- loopDate = QDate::fromJulianDay(513024036); // 1 Jan 1399900 AD
- while (loopDate.toJulianDay() <= 513060925) { // 31 Dec 1400000 AD
+ loopDate = QDate::fromJulianDay(maxJd);
+ while (loopDate.toJulianDay() >= maxJd - 146397) {
+ testDate.setDate(loopDate.year(), loopDate.month(), loopDate.day());
+ QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay());
+ loopDate = loopDate.addDays(-1);
+ }
+
+ // Test Gregorian round trip at low end of conversion range
+ loopDate = QDate::fromJulianDay(minJd);
+ while (loopDate.toJulianDay() <= minJd + 146397) {
testDate.setDate(loopDate.year(), loopDate.month(), loopDate.day());
QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay());
loopDate = loopDate.addDays(1);