QDateTime: Remove Julian Calendar
Convert QDate to only use Gregorian calendar and not Julian calendar before 1582. In future the Julian can be used via proper calendar classes. Change-Id: I547a3550332057a0ab1be616706630b6afaceffc Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
8327fa7c11
commit
fc24979e43
4
dist/changes-5.0.0
vendored
4
dist/changes-5.0.0
vendored
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user