summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/corelib/time/qcalendarmath_p.h34
-rw-r--r--src/corelib/time/qgregoriancalendar.cpp44
-rw-r--r--src/corelib/time/qislamiccivilcalendar.cpp30
-rw-r--r--src/corelib/time/qjuliancalendar.cpp29
-rw-r--r--src/corelib/time/qmilankoviccalendar.cpp41
5 files changed, 111 insertions, 67 deletions
diff --git a/src/corelib/time/qcalendarmath_p.h b/src/corelib/time/qcalendarmath_p.h
index 6852e2c3446..c785803ce3d 100644
--- a/src/corelib/time/qcalendarmath_p.h
+++ b/src/corelib/time/qcalendarmath_p.h
@@ -106,6 +106,40 @@ template <unsigned b, typename Int> constexpr Int qMod(Int a) { return qDivMod<b
} // QRoundingDown
+namespace QRomanCalendrical {
+// Julian Day number of Gregorian 1 BCE, February 29th:
+constexpr qint64 LeapDayGregorian1Bce = 1721119;
+// Aside from (maybe) some turns of centuries, one year in four is leap:
+constexpr unsigned FourYears = 4 * 365 + 1;
+constexpr unsigned FiveMonths = 31 + 30 + 31 + 30 + 31; // Mar-Jul or Aug-Dec.
+
+constexpr auto yearMonthToYearDays(int year, int month)
+{
+ // Pre-digests year and month to (possibly denormal) year count and day-within-year.
+ struct R { qint64 year; qint64 days; };
+ if (year < 0) // Represent -N BCE as 1-N so year numbering is contiguous.
+ ++year;
+ month -= 3; // Adjust month numbering so March = 0, ...
+ if (month < 0) { // and Jan = 10, Feb = 11, in the previous year.
+ --year;
+ month += 12;
+ }
+ return R { year, QRoundingDown::qDiv<5>(FiveMonths * month + 2) };
+}
+
+constexpr auto dayInYearToYmd(int dayInYear)
+{
+ // The year is an adjustment to the year for which dayInYear may be denormal.
+ struct R { int year; int month; int day; };
+ // Shared code for Julian and Milankovic (at least).
+ using namespace QRoundingDown;
+ const auto month5Day = qDivMod<FiveMonths>(5 * dayInYear + 2);
+ // Its remainder changes by 5 per day, except at roughly monthly quotient steps.
+ const auto yearMonth = qDivMod<12>(month5Day.quotient + 2);
+ return R { yearMonth.quotient, yearMonth.remainder + 1, qDiv<5>(month5Day.remainder) + 1 };
+}
+}
+
QT_END_NAMESPACE
#endif // QCALENDARMATH_P_H
diff --git a/src/corelib/time/qgregoriancalendar.cpp b/src/corelib/time/qgregoriancalendar.cpp
index 904b23434cc..69a3665eeec 100644
--- a/src/corelib/time/qgregoriancalendar.cpp
+++ b/src/corelib/time/qgregoriancalendar.cpp
@@ -3,6 +3,7 @@
#include "qgregoriancalendar_p.h"
#include "qcalendarmath_p.h"
+
#include <QtCore/qdatetime.h>
QT_BEGIN_NAMESPACE
@@ -168,41 +169,44 @@ int QGregorianCalendar::yearSharingWeekDays(QDate date)
* do for Milankovic).
*/
+using namespace QRomanCalendrical;
+// End a Gregorian four-century cycle on 1 BC's leap day:
+constexpr qint64 BaseJd = LeapDayGregorian1Bce;
+// Every four centures there are 97 leap years:
+constexpr unsigned FourCenturies = 400 * 365 + 97;
+
bool QGregorianCalendar::julianFromParts(int year, int month, int day, qint64 *jd)
{
Q_ASSERT(jd);
if (!validParts(year, month, day))
return false;
- if (year < 0)
- ++year;
-
- int a = month < 3 ? 1 : 0;
- qint64 y = qint64(year) - a;
- int m = month + 12 * a - 3;
- *jd = day + qDiv<5>(153 * m + 2) + 1721119
- + 365 * y + qDiv<4>(y) - qDiv<100>(y) + qDiv<400>(y);
+ const auto yearDays = yearMonthToYearDays(year, month);
+ const qint64 y = yearDays.year;
+ const qint64 fromYear = 365 * y + qDiv<4>(y) - qDiv<100>(y) + qDiv<400>(y);
+ *jd = fromYear + yearDays.days + day + BaseJd ;
return true;
}
QCalendar::YearMonthDay QGregorianCalendar::partsFromJulian(qint64 jd)
{
- qint64 a = jd - 1721120;
- qint64 b = qDiv<146097>(4 * a + 3);
- int c = a - qDiv<4>(146097 * b);
+ const qint64 dayNumber = jd - BaseJd;
+ const qint64 century = qDiv<FourCenturies>(4 * dayNumber - 1);
+ const int dayInCentury = dayNumber - qDiv<4>(FourCenturies * century);
- int d = qDiv<1461>(4 * c + 3);
- int e = c - qDiv<4>(1461 * d);
- int m = qDiv<153>(5 * e + 2);
+ const int yearInCentury = qDiv<FourYears>(4 * dayInCentury - 1);
+ const int dayInYear = dayInCentury - qDiv<4>(FourYears * yearInCentury);
+ const int m = qDiv<FiveMonths>(5 * dayInYear - 3);
+ Q_ASSERT(m < 12 && m >= 0);
+ // That m is a month adjusted to March = 0, with Jan = 10, Feb = 11 in the previous year.
+ const int yearOffset = m < 10 ? 0 : 1;
- int y = 100 * b + d + qDiv<10>(m);
+ const int y = 100 * century + yearInCentury + yearOffset;
+ const int month = m + 3 - 12 * yearOffset;
+ const int day = dayInYear - qDiv<5>(FiveMonths * m + 2);
// Adjust for no year 0
- int year = y > 0 ? y : y - 1;
- int month = m + 3 - 12 * qDiv<10>(m);
- int day = e - qDiv<5>(153 * m + 2) + 1;
-
- return QCalendar::YearMonthDay(year, month, day);
+ return QCalendar::YearMonthDay(y > 0 ? y : y - 1, month, day);
}
QT_END_NAMESPACE
diff --git a/src/corelib/time/qislamiccivilcalendar.cpp b/src/corelib/time/qislamiccivilcalendar.cpp
index 48bb0064c8d..ac1f97cb6c5 100644
--- a/src/corelib/time/qislamiccivilcalendar.cpp
+++ b/src/corelib/time/qislamiccivilcalendar.cpp
@@ -4,7 +4,6 @@
#include "qglobal.h"
#include "qislamiccivilcalendar_p.h"
#include "qcalendarmath_p.h"
-#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
@@ -61,27 +60,34 @@ bool QIslamicCivilCalendar::isLeapYear(int year) const
return qMod<30>(year * 11 + 14) < 11;
}
+// First day of first year (Gregorian 622 CE July 19th) is the base date here:
+constexpr qint64 EpochJd = 1948440;
+// Each 30 years has 11 leap years of 355 days and 19 ordinary years of 354:
+constexpr unsigned ThirtyYears = 11 * 355 + 19 * 354;
+// The first eleven months of the year alternate 30, 29, ..., 29, 30 days in length.
+constexpr unsigned ElevenMonths = 6 * 30 + 5 * 29;
+
bool QIslamicCivilCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
{
Q_ASSERT(jd);
if (!isDateValid(year, month, day))
return false;
- if (year <= 0)
- ++year;
- *jd = qDiv<30>(10631 * year - 10617)
- + qDiv<11>(325 * month - 320)
- + day + 1948439;
+
+ *jd = qDiv<30>(qint64(ThirtyYears) * (year > 0 ? year - 1 : year) + 14)
+ + qDiv<11>(ElevenMonths * (month - 1) + 5)
+ + day + EpochJd - 1;
return true;
}
QCalendar::YearMonthDay QIslamicCivilCalendar::julianDayToDate(qint64 jd) const
{
- constexpr qint64 epoch = 1948440;
- const auto k2dm = qDivMod<10631>(30 * (jd - epoch) + 15);
- int y = k2dm.quotient + 1;
- const auto k1dm = qDivMod<325>(11 * qDiv<30>(k2dm.remainder) + 5);
- const int month = k1dm.quotient + 1;
- const int day = qDiv<11>(k1dm.remainder) + 1;
+ const auto year30Day = qDivMod<ThirtyYears>(30 * (jd - EpochJd) + 15);
+ // Its remainder changes by 30 per day, except roughly yearly.
+ const auto month11Day = qDivMod<ElevenMonths>(11 * qDiv<30>(year30Day.remainder) + 5);
+ // Its remainder changes by 11 per day except roughly monthly.
+ const int month = month11Day.quotient + 1;
+ const int day = qDiv<11>(month11Day.remainder) + 1;
+ const int y = year30Day.quotient + 1;
return QCalendar::YearMonthDay(y > 0 ? y : y - 1, month, day);
}
diff --git a/src/corelib/time/qjuliancalendar.cpp b/src/corelib/time/qjuliancalendar.cpp
index 1c63d4e170a..5572dbe19b9 100644
--- a/src/corelib/time/qjuliancalendar.cpp
+++ b/src/corelib/time/qjuliancalendar.cpp
@@ -5,8 +5,7 @@
#include "qjuliancalendar_p.h"
#include "qromancalendar_data_p.h"
#include "qcalendarmath_p.h"
-#include <QtCore/qmath.h>
-#include <QtCore/qlocale.h>
+
#include <QtCore/qdatetime.h>
QT_BEGIN_NAMESPACE
@@ -57,31 +56,29 @@ bool QJulianCalendar::isLeapYear(int year) const
return qMod<4>(year < 0 ? year + 1 : year) == 0;
}
-// Julian Day 0 was January the first in the proleptic Julian calendar's 4713 BC
+// Julian Day 0 was January the first in the proleptic Julian calendar's 4713 BC.
+using namespace QRomanCalendrical;
+// End a Julian four-year cycle on 1 BC's leap day (Gregorian Feb 27th):
+constexpr qint64 BaseJd = LeapDayGregorian1Bce - 2;
bool QJulianCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
{
Q_ASSERT(jd);
if (!isDateValid(year, month, day))
return false;
- if (year < 0)
- ++year;
- const qint64 c0 = month < 3 ? -1 : 0;
- const qint64 j1 = qDiv<4>(1461 * (year + c0));
- const qint64 j2 = qDiv<5>(153 * month - 1836 * c0 - 457);
- *jd = j1 + j2 + day + 1721117;
+
+ const auto yearDays = yearMonthToYearDays(year, month);
+ *jd = qDiv<4>(FourYears * yearDays.year) + yearDays.days + day + BaseJd;
return true;
}
QCalendar::YearMonthDay QJulianCalendar::julianDayToDate(qint64 jd) const
{
- const auto k2dm = qDivMod<1461>(4 * (jd - 1721118) + 3);
- const auto k1dm = qDivMod<153>(5 * qDiv<4>(k2dm.remainder) + 2);
- const auto c0dm = qDivMod<12>(k1dm.quotient + 2);
- const int y = qint16(k2dm.quotient + c0dm.quotient);
- const int month = quint8(c0dm.remainder + 1);
- const int day = qDiv<5>(k1dm.remainder) + 1;
- return QCalendar::YearMonthDay(y > 0 ? y : y - 1, month, day);
+ const auto year4Day = qDivMod<FourYears>(4 * (jd - BaseJd) - 1);
+ // Its remainder changes by 4 per day, except at roughly yearly quotient steps.
+ const auto ymd = dayInYearToYmd(qDiv<4>(year4Day.remainder));
+ const int y = year4Day.quotient + ymd.year;
+ return QCalendar::YearMonthDay(y > 0 ? y : y - 1, ymd.month, ymd.day);
}
QT_END_NAMESPACE
diff --git a/src/corelib/time/qmilankoviccalendar.cpp b/src/corelib/time/qmilankoviccalendar.cpp
index 3d848fb372d..b41bf120ddc 100644
--- a/src/corelib/time/qmilankoviccalendar.cpp
+++ b/src/corelib/time/qmilankoviccalendar.cpp
@@ -4,8 +4,7 @@
#include "qglobal.h"
#include "qmilankoviccalendar_p.h"
#include "qcalendarmath_p.h"
-#include <QtCore/qmath.h>
-#include <QtCore/qlocale.h>
+
#include <QtCore/qdatetime.h>
QT_BEGIN_NAMESPACE
@@ -64,33 +63,37 @@ bool QMilankovicCalendar::isLeapYear(int year) const
return true;
}
+using namespace QRomanCalendrical;
+// End a Milankovic nine-century cycle on 1 BC, Feb 28 (Gregorian Feb 29):
+constexpr qint64 BaseJd = LeapDayGregorian1Bce;
+// Leap years every 4 years, except for 7 turn-of-century years per nine centuries:
+constexpr unsigned NineCenturies = 365 * 900 + 900 / 4 - 7;
+// When the turn-of-century is a leap year, the century has 25 leap years in it:
+constexpr unsigned LeapCentury = 365 * 100 + 25;
+
bool QMilankovicCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
{
Q_ASSERT(jd);
if (!isDateValid(year, month, day))
return false;
- if (year <= 0)
- ++year;
- const qint16 c0 = month < 3 ? -1 : 0;
- const qint16 x1 = month - 12 * c0 - 3;
- const auto x4dm = qDivMod<100>(year + c0);
- *jd = qDiv<9>(328718 * x4dm.quotient + 6)
- + qDiv<100>(36525 * x4dm.remainder)
- + qDiv<5>(153 * x1 + 2)
- + day + 1721119;
+
+ const auto yearDays = yearMonthToYearDays(year, month);
+ const auto centuryYear = qDivMod<100>(yearDays.year);
+ const qint64 fromYear = qDiv<9>(NineCenturies * centuryYear.quotient + 6)
+ + qDiv<100>(LeapCentury * centuryYear.remainder);
+ *jd = fromYear + yearDays.days + day + BaseJd;
return true;
}
QCalendar::YearMonthDay QMilankovicCalendar::julianDayToDate(qint64 jd) const
{
- const auto k3dm = qDivMod<328718>(9 * (jd - 1721120) + 2);
- const auto k2dm = qDivMod<36525>(100 * qDiv<9>(k3dm.remainder) + 99);
- const auto k1dm = qDivMod<153>(qDiv<100>(k2dm.remainder) * 5 + 2);
- const auto c0dm = qDivMod<12>(k1dm.quotient + 2);
- const int y = 100 * k3dm.quotient + k2dm.quotient + c0dm.quotient;
- const int month = c0dm.remainder + 1;
- const int day = qDiv<5>(k1dm.remainder) + 1;
- return QCalendar::YearMonthDay(y > 0 ? y : y - 1, month, day);
+ const auto century9Day = qDivMod<NineCenturies>(9 * (jd - BaseJd) - 7);
+ // Its remainder changes by 9 per day, except roughly once per century.
+ const auto year100Day = qDivMod<LeapCentury>(100 * qDiv<9>(century9Day.remainder) + 99);
+ // Its remainder changes by 100 per day, except roughly once per year.
+ const auto ymd = dayInYearToYmd(qDiv<100>(year100Day.remainder));
+ const int y = 100 * century9Day.quotient + year100Day.quotient + ymd.year;
+ return QCalendar::YearMonthDay(y > 0 ? y : y - 1, ymd.month, ymd.day);
}
QT_END_NAMESPACE