diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/corelib/time/qcalendarmath_p.h | 34 | ||||
| -rw-r--r-- | src/corelib/time/qgregoriancalendar.cpp | 44 | ||||
| -rw-r--r-- | src/corelib/time/qislamiccivilcalendar.cpp | 30 | ||||
| -rw-r--r-- | src/corelib/time/qjuliancalendar.cpp | 29 | ||||
| -rw-r--r-- | src/corelib/time/qmilankoviccalendar.cpp | 41 |
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 |
