File indexing completed on 2025-01-05 04:49:36

0001 /*
0002   This file is part of the kholidays library.
0003 
0004   SPDX-FileCopyrightText: 2014 John Layt <john@layt.net>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "qcalendarsystem_p.h"
0010 
0011 #include <QDate>
0012 #include <QSharedData>
0013 
0014 class QCalendarSystemPrivate : public QSharedData
0015 {
0016 public:
0017     explicit QCalendarSystemPrivate(QCalendarSystem::CalendarSystem calendar);
0018 
0019     QCalendarSystem::CalendarSystem calendarSystem() const;
0020     qint64 epoch() const;
0021     qint64 earliestValidDate() const;
0022     int earliestValidYear() const;
0023     qint64 latestValidDate() const;
0024     int latestValidYear() const;
0025     int yearOffset() const;
0026     int maxMonthsInYear() const;
0027     int monthsInYear(int year) const;
0028     int maxDaysInYear() const;
0029     int daysInYear(int year) const;
0030     int maxDaysInMonth() const;
0031     int daysInMonth(int year, int month) const;
0032     bool hasYearZero() const;
0033     bool hasLeapMonths() const;
0034 
0035     int quarter(int month) const;
0036     bool isLeapYear(int year) const;
0037     void julianDayToDate(qint64 jd, int *year, int *month, int *day) const;
0038     qint64 julianDayFromDate(int year, int month, int day) const;
0039 
0040     bool isValidYear(int year) const;
0041     bool isValidMonth(int year, int month) const;
0042     int addYears(int y1, int years) const;
0043     int diffYears(int y1, int y2) const;
0044 
0045     QCalendarSystem::CalendarSystem m_calendarSystem;
0046 };
0047 
0048 static const char julianMonths[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
0049 
0050 QCalendarSystemPrivate::QCalendarSystemPrivate(QCalendarSystem::CalendarSystem calendar)
0051     : QSharedData()
0052     , m_calendarSystem(calendar)
0053 {
0054 }
0055 
0056 QCalendarSystem::CalendarSystem QCalendarSystemPrivate::calendarSystem() const
0057 {
0058     if (m_calendarSystem == QCalendarSystem::DefaultCalendar) {
0059         return QCalendarSystem::GregorianCalendar;
0060     } else {
0061         return m_calendarSystem;
0062     }
0063 }
0064 
0065 qint64 QCalendarSystemPrivate::epoch() const
0066 {
0067     switch (calendarSystem()) {
0068     case QCalendarSystem::GregorianCalendar:
0069         return 1721426; //  0001-01-01 Gregorian
0070     case QCalendarSystem::CopticCalendar:
0071         return 1825030; //  0001-01-01 ==  0284-08-29 Gregorian
0072     case QCalendarSystem::EthiopicCalendar:
0073         return 1724221; //  0001-01-01 ==  0008-08-29 Gregorian
0074     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0075         return -284655; //  0001-01-01 == -5492-08-29 Gregorian
0076     case QCalendarSystem::IndianNationalCalendar:
0077         return 1749994; //  0000-01-01 == 0078-03-22 Gregorian
0078     case QCalendarSystem::IslamicCivilCalendar:
0079         return 1948440; //  0001-01-01 == 0622-07-19 Gregorian
0080     case QCalendarSystem::ISO8601Calendar:
0081         return 1721060; //  0000-01-01 Gregorian
0082     case QCalendarSystem::JapaneseCalendar:
0083         return 1721426; //  0001-01-01 Gregorian
0084     case QCalendarSystem::JulianCalendar:
0085         return 1721424; //  0001-01-01 ==  Gregorian
0086     case QCalendarSystem::ROCCalendar:
0087         return 2419403; //  0001-01-01 ==  1912-01-01 Gregorian
0088     case QCalendarSystem::ThaiCalendar:
0089         return 1522734; //  0000-01-01 == -0544-01-01 Gregorian
0090     default:
0091         return 0;
0092     }
0093 }
0094 
0095 qint64 QCalendarSystemPrivate::earliestValidDate() const
0096 {
0097     switch (calendarSystem()) {
0098     case QCalendarSystem::GregorianCalendar:
0099         return -31738; // -4800-01-01 Gregorian
0100     case QCalendarSystem::CopticCalendar:
0101         return 1825030; //  0001-01-01 == 0284-08-29 Gregorian
0102     case QCalendarSystem::EthiopicCalendar:
0103         return 1724221; //  0001-01-01 == 0008-08-29 Gregorian
0104     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0105         return -284655; //  0001-01-01 == -5492-08-29 Gregorian
0106     case QCalendarSystem::IndianNationalCalendar:
0107         return 1749994; //  0000-01-01 == 0078-03-22 Gregorian
0108     case QCalendarSystem::IslamicCivilCalendar:
0109         return 1948440; //  0001-01-01 == 0622-07-19 Gregorian
0110     case QCalendarSystem::ISO8601Calendar:
0111         return 1721060; //  0000-01-01 Gregorian
0112     case QCalendarSystem::JapaneseCalendar:
0113         return -31738; // -4800-01-01 Gregorian
0114     case QCalendarSystem::JulianCalendar:
0115         return -31776; // -4800-01-01 Julian
0116     case QCalendarSystem::ROCCalendar:
0117         return 2419403; //  0001-01-01 == 1912-01-01 Gregorian
0118     case QCalendarSystem::ThaiCalendar:
0119         return 1522734; //  0000-01-01 == -0544-01-01 Gregorian
0120     default:
0121         return 0;
0122     }
0123 }
0124 
0125 int QCalendarSystemPrivate::earliestValidYear() const
0126 {
0127     switch (calendarSystem()) {
0128     case QCalendarSystem::GregorianCalendar:
0129     case QCalendarSystem::JapaneseCalendar:
0130     case QCalendarSystem::JulianCalendar:
0131         return -4800;
0132     case QCalendarSystem::IndianNationalCalendar:
0133     case QCalendarSystem::ISO8601Calendar:
0134     case QCalendarSystem::ThaiCalendar:
0135         return 0;
0136     default:
0137         return 1;
0138     }
0139 }
0140 
0141 qint64 QCalendarSystemPrivate::latestValidDate() const
0142 {
0143     switch (calendarSystem()) {
0144     case QCalendarSystem::GregorianCalendar:
0145         return 5373484; //  9999-12-31 Gregorian
0146     case QCalendarSystem::CopticCalendar:
0147         return 5477164; //  9999-13-05 == 10283-11-12 Gregorian
0148     case QCalendarSystem::EthiopicCalendar:
0149         return 5376721; //  9999-13-05 == 10008-11-10 Gregorian
0150     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0151         return 3367114; //  9999-13-05 ==  4506-09-29 Gregorian
0152     case QCalendarSystem::IndianNationalCalendar:
0153         return 5402054; //  9999-12-30 == 10078-03-21 Gregorian
0154     case QCalendarSystem::IslamicCivilCalendar:
0155         return 5491751; //  9999-12-29 == 10323-10-21 Gregorian
0156     case QCalendarSystem::ISO8601Calendar:
0157         return 5373484; //  9999-12-31 Gregorian
0158     case QCalendarSystem::JapaneseCalendar:
0159         return 5373484; //  9999-12-31 Gregorian
0160     case QCalendarSystem::JulianCalendar:
0161         return 5373557; //  9999-12-31 == 10000-03-13 Gregorian
0162     case QCalendarSystem::ROCCalendar:
0163         return 6071462; //  9999-12-31 == 11910-12-31 Gregorian
0164     case QCalendarSystem::ThaiCalendar:
0165         return 5175158; //  9999-12-31 ==  9456-12-31 Gregorian
0166     default:
0167         return 0;
0168     }
0169 }
0170 
0171 int QCalendarSystemPrivate::latestValidYear() const
0172 {
0173     switch (calendarSystem()) {
0174     default:
0175         return 9999;
0176     }
0177 }
0178 
0179 int QCalendarSystemPrivate::yearOffset() const
0180 {
0181     switch (calendarSystem()) {
0182     case QCalendarSystem::ROCCalendar:
0183         return 1911; // 0001-01-01 == 1912-01-01 Gregorian
0184     case QCalendarSystem::ThaiCalendar:
0185         return -543; // 0000-01-01 == -544-01-01 Gregorian
0186     default:
0187         return 0;
0188     }
0189 }
0190 
0191 int QCalendarSystemPrivate::maxMonthsInYear() const
0192 {
0193     switch (calendarSystem()) {
0194     case QCalendarSystem::CopticCalendar:
0195     case QCalendarSystem::EthiopicCalendar:
0196     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0197         return 13;
0198     default:
0199         return 12;
0200     }
0201 }
0202 
0203 int QCalendarSystemPrivate::monthsInYear(int year) const
0204 {
0205     // year = year + yearOffset();
0206     Q_UNUSED(year)
0207 
0208     switch (calendarSystem()) {
0209     case QCalendarSystem::CopticCalendar:
0210     case QCalendarSystem::EthiopicCalendar:
0211     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0212         return 13;
0213     default:
0214         return 12;
0215     }
0216 }
0217 
0218 int QCalendarSystemPrivate::maxDaysInYear() const
0219 {
0220     switch (calendarSystem()) {
0221     case QCalendarSystem::IslamicCivilCalendar:
0222         return 355;
0223     default:
0224         return 366;
0225     }
0226 }
0227 
0228 int QCalendarSystemPrivate::daysInYear(int year) const
0229 {
0230     switch (calendarSystem()) {
0231     case QCalendarSystem::IslamicCivilCalendar:
0232         return isLeapYear(year) ? 355 : 354;
0233     default:
0234         return isLeapYear(year) ? 366 : 365;
0235     }
0236 }
0237 
0238 int QCalendarSystemPrivate::maxDaysInMonth() const
0239 {
0240     switch (calendarSystem()) {
0241     case QCalendarSystem::CopticCalendar:
0242     case QCalendarSystem::EthiopicCalendar:
0243     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0244     case QCalendarSystem::IslamicCivilCalendar:
0245         return 30;
0246     default:
0247         return 31;
0248     }
0249 }
0250 
0251 int QCalendarSystemPrivate::daysInMonth(int year, int month) const
0252 {
0253     if (month < 1 || month > monthsInYear(year)) {
0254         return 0;
0255     }
0256 
0257     switch (calendarSystem()) {
0258     case QCalendarSystem::GregorianCalendar:
0259     case QCalendarSystem::ISO8601Calendar:
0260     case QCalendarSystem::JapaneseCalendar:
0261     case QCalendarSystem::ROCCalendar:
0262     case QCalendarSystem::ThaiCalendar:
0263     case QCalendarSystem::JulianCalendar:
0264         if (month == 2 && isLeapYear(year)) {
0265             return 29;
0266         } else {
0267             return julianMonths[month];
0268         }
0269     case QCalendarSystem::CopticCalendar:
0270     case QCalendarSystem::EthiopicCalendar:
0271     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0272         if (month == 13) {
0273             return isLeapYear(year) ? 6 : 5;
0274         } else {
0275             return 30;
0276         }
0277     case QCalendarSystem::IndianNationalCalendar:
0278         if (month >= 7) {
0279             return 30;
0280         } else if (month >= 2) {
0281             return 31;
0282         } else if (isLeapYear(year)) {
0283             return 31;
0284         } else {
0285             return 30;
0286         }
0287     case QCalendarSystem::IslamicCivilCalendar:
0288         if (month == 12 && isLeapYear(year)) {
0289             return 30;
0290         } else if (month % 2 == 0) {
0291             return 29;
0292         } else {
0293             return 30;
0294         }
0295     default:
0296         return 0;
0297     }
0298 }
0299 
0300 bool QCalendarSystemPrivate::hasYearZero() const
0301 {
0302     switch (calendarSystem()) {
0303     case QCalendarSystem::IndianNationalCalendar:
0304     case QCalendarSystem::ISO8601Calendar:
0305     case QCalendarSystem::ThaiCalendar:
0306         return true;
0307     default:
0308         return false;
0309     }
0310 }
0311 
0312 bool QCalendarSystemPrivate::hasLeapMonths() const
0313 {
0314     switch (calendarSystem()) {
0315     default:
0316         return false;
0317     }
0318 }
0319 
0320 int QCalendarSystemPrivate::quarter(int month) const
0321 {
0322     switch (calendarSystem()) {
0323     case QCalendarSystem::CopticCalendar:
0324     case QCalendarSystem::EthiopicCalendar:
0325     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0326         if (month == 13) { // Consider the short epagomenal month as part of the 4th quarter
0327             return 4;
0328         }
0329         [[fallthrough]];
0330     default:
0331         return ((month - 1) / 3) + 1;
0332     }
0333 }
0334 
0335 bool QCalendarSystemPrivate::isLeapYear(int year) const
0336 {
0337     year = year + yearOffset();
0338 
0339     // Uses same rule as Gregorian and in same years as Gregorian to keep in sync
0340     // Can't use yearOffset() as this offset only applies for isLeapYear()
0341     if (calendarSystem() == QCalendarSystem::IndianNationalCalendar) {
0342         year = year + 78;
0343     }
0344 
0345     if (year < 1 && !hasYearZero()) {
0346         ++year;
0347     }
0348 
0349     switch (calendarSystem()) {
0350     case QCalendarSystem::GregorianCalendar:
0351     case QCalendarSystem::IndianNationalCalendar:
0352     case QCalendarSystem::ISO8601Calendar:
0353     case QCalendarSystem::JapaneseCalendar:
0354     case QCalendarSystem::ROCCalendar:
0355     case QCalendarSystem::ThaiCalendar:
0356         return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
0357     case QCalendarSystem::CopticCalendar:
0358     case QCalendarSystem::EthiopicCalendar:
0359     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0360         return year % 4 == 3;
0361     case QCalendarSystem::JulianCalendar:
0362         return year % 4 == 0;
0363     case QCalendarSystem::IslamicCivilCalendar:
0364         return (((11 * year) + 14) % 30) < 11;
0365     default:
0366         return false;
0367     }
0368 }
0369 
0370 void QCalendarSystemPrivate::julianDayToDate(qint64 jd, int *year, int *month, int *day) const
0371 {
0372     int yy = 0, mm = 0, dd = 0;
0373 
0374     switch (calendarSystem()) {
0375     case QCalendarSystem::GregorianCalendar:
0376     case QCalendarSystem::ISO8601Calendar:
0377     case QCalendarSystem::JapaneseCalendar:
0378     case QCalendarSystem::ROCCalendar:
0379     case QCalendarSystem::ThaiCalendar: {
0380         // Formula from The Calendar FAQ by Claus Tondering
0381         // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
0382         qint64 a = jd + 32044;
0383         qint64 b = ((4 * a) + 3) / 146097;
0384         qint64 c = a - ((146097 * b) / 4);
0385         qint64 d = ((4 * c) + 3) / 1461;
0386         qint64 e = c - ((1461 * d) / 4);
0387         qint64 m = ((5 * e) + 2) / 153;
0388         dd = e - (((153 * m) + 2) / 5) + 1;
0389         mm = m + 3 - (12 * (m / 10));
0390         yy = (100 * b) + d - 4800 + (m / 10);
0391         break;
0392     }
0393 
0394     case QCalendarSystem::CopticCalendar:
0395     case QCalendarSystem::EthiopicCalendar:
0396     case QCalendarSystem::EthiopicAmeteAlemCalendar: {
0397         // Formula derived from first principles by John Layt
0398         qint64 s = jd - (epoch() - 365);
0399         qint64 l = s / 1461;
0400         yy = (l * 4) + qMin(static_cast<qint64>(3), (s % 1461) / 365);
0401         qint64 diy = s - (yy * 365) + (yy / 4);
0402         mm = (diy / 30) + 1;
0403         dd = (diy % 30) + 1;
0404         break;
0405     }
0406 
0407     case QCalendarSystem::IndianNationalCalendar: {
0408         // Formula from the "Explanatory Supplement to the Astronomical Almanac"
0409         // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory
0410         // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government
0411         qint64 l = jd + 68518;
0412         qint64 n = (4 * l) / 146097;
0413         l = l - (146097 * n + 3) / 4;
0414         qint64 i = (4000 * (l + 1)) / 1461001;
0415         l = l - (1461 * i) / 4 + 1;
0416         qint64 j = ((l - 1) / 31) * (1 - l / 185) + (l / 185) * ((l - 156) / 30 + 5) - l / 366;
0417         dd = l - 31 * j + ((j + 2) / 8) * (j - 5);
0418         l = j / 11;
0419         mm = j + 2 - 12 * l;
0420         yy = 100 * (n - 49) + l + i - 78;
0421         break;
0422     }
0423 
0424     case QCalendarSystem::IslamicCivilCalendar: {
0425         // Formula from the "Explanatory Supplement to the Astronomical Almanac"
0426         // Revised Edition 2006 section ??? pp ???, US Naval Observatory
0427         // Derived from Fliegel & Van Flandern 1968
0428         qint64 l = jd - epoch() + 10632;
0429         qint64 n = (l - 1) / 10631;
0430         l = l - 10631 * n + 354;
0431         int j = ((10985 - l) / 5316) * ((50 * l) / 17719) + (l / 5670) * ((43 * l) / 15238);
0432         l = l - ((30 - j) / 15) * ((17719 * j) / 50) - (j / 16) * ((15238 * j) / 43) + 29;
0433         yy = (30 * n) + j - 30;
0434         mm = (24 * l) / 709;
0435         dd = l - ((709 * mm) / 24);
0436         break;
0437     }
0438 
0439     case QCalendarSystem::JulianCalendar: {
0440         // Formula from The Calendar FAQ by Claus Tondering
0441         // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
0442         qint64 b = 0;
0443         qint64 c = jd + 32082;
0444         qint64 d = ((4 * c) + 3) / 1461;
0445         qint64 e = c - ((1461 * d) / 4);
0446         qint64 m = ((5 * e) + 2) / 153;
0447         dd = e - (((153 * m) + 2) / 5) + 1;
0448         mm = m + 3 - (12 * (m / 10));
0449         yy = (100 * b) + d - 4800 + (m / 10);
0450         break;
0451     }
0452 
0453     default:
0454         break;
0455     }
0456 
0457     if (!hasYearZero() && yy < 1) {
0458         yy -= 1;
0459     }
0460 
0461     yy = yy - yearOffset();
0462 
0463     if (year) {
0464         *year = yy;
0465     }
0466     if (month) {
0467         *month = mm;
0468     }
0469     if (day) {
0470         *day = dd;
0471     }
0472 }
0473 
0474 qint64 QCalendarSystemPrivate::julianDayFromDate(int year, int month, int day) const
0475 {
0476     qint64 jd = 0;
0477 
0478     year = year + yearOffset();
0479 
0480     if (year < 1 && !hasYearZero()) {
0481         year = year + 1;
0482     }
0483 
0484     switch (calendarSystem()) {
0485     case QCalendarSystem::GregorianCalendar:
0486     case QCalendarSystem::ISO8601Calendar:
0487     case QCalendarSystem::JapaneseCalendar:
0488     case QCalendarSystem::ROCCalendar:
0489     case QCalendarSystem::ThaiCalendar: {
0490         // Formula from The Calendar FAQ by Claus Tondering
0491         // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
0492         int a = (14 - month) / 12;
0493         year = year + 4800 - a;
0494         int m = month + (12 * a) - 3;
0495         jd = day + (((153 * m) + 2) / 5) + (365 * year) + (year / 4) - (year / 100) + (year / 400) - 32045;
0496         break;
0497     }
0498 
0499     case QCalendarSystem::CopticCalendar:
0500     case QCalendarSystem::EthiopicCalendar:
0501     case QCalendarSystem::EthiopicAmeteAlemCalendar:
0502         // Formula derived from first principles by John Layt
0503         jd = epoch() - 1 + ((year - 1) * 365) + (year / 4) + ((month - 1) * 30) + day;
0504         break;
0505 
0506     case QCalendarSystem::IndianNationalCalendar:
0507         // Formula from the "Explanatory Supplement to the Astronomical Almanac"
0508         // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory
0509         // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government
0510         jd = 365 * year + (year + 78 - 1 / month) / 4 + 31 * month - (month + 9) / 11 - (month / 7) * (month - 7)
0511             - (3 * ((year + 78 - 1 / month) / 100 + 1)) / 4 + day + 1749579;
0512         break;
0513 
0514     case QCalendarSystem::IslamicCivilCalendar:
0515         // Formula from the "Explanatory Supplement to the Astronomical Almanac"
0516         // Revised Edition 2006 section ??? pp ???, US Naval Observatory
0517         // Derived from Fliegel & Van Flandern 1968
0518         jd = (3 + (11 * year)) / 30 + 354 * year + 30 * month - (month - 1) / 2 + day + epoch() - 385;
0519         break;
0520 
0521     case QCalendarSystem::JulianCalendar: {
0522         // Formula from The Calendar FAQ by Claus Tondering
0523         // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
0524         int a = (14 - month) / 12;
0525         year = year + 4800 - a;
0526         int m = month + (12 * a) - 3;
0527         jd = day + (((153 * m) + 2) / 5) + (365 * year) + (year / 4) - 32083;
0528         break;
0529     }
0530 
0531     default:
0532         break;
0533     }
0534 
0535     return jd;
0536 }
0537 
0538 // Some private utility rules
0539 
0540 bool QCalendarSystemPrivate::isValidYear(int year) const
0541 {
0542     return year >= earliestValidYear() && year <= latestValidYear() && (year == 0 ? hasYearZero() : true);
0543 }
0544 
0545 bool QCalendarSystemPrivate::isValidMonth(int year, int month) const
0546 {
0547     return isValidYear(year) && month >= 1 && month <= monthsInYear(year);
0548 }
0549 
0550 int QCalendarSystemPrivate::addYears(int y1, int years) const
0551 {
0552     int y2 = y1 + years;
0553 
0554     if (!hasYearZero()) {
0555         if (y1 > 0 && y2 <= 0) {
0556             --y2;
0557         } else if (y1 < 0 && y2 >= 0) {
0558             ++y2;
0559         }
0560     }
0561 
0562     return y2;
0563 }
0564 
0565 int QCalendarSystemPrivate::diffYears(int y1, int y2) const
0566 {
0567     int dy = y2 - y1;
0568 
0569     if (!hasYearZero()) {
0570         if (y2 > 0 && y1 < 0) {
0571             dy -= 1;
0572         } else if (y2 < 0 && y1 > 0) {
0573             dy += 1;
0574         }
0575     }
0576 
0577     return dy;
0578 }
0579 
0580 // QCalendarSystem public api
0581 
0582 QCalendarSystem::QCalendarSystem(QCalendarSystem::CalendarSystem calendar)
0583     : d(new QCalendarSystemPrivate(calendar))
0584 {
0585 }
0586 
0587 QCalendarSystem::~QCalendarSystem()
0588 {
0589 }
0590 
0591 QCalendarSystem &QCalendarSystem::operator=(const QCalendarSystem &other)
0592 {
0593     d = other.d;
0594     return *this;
0595 }
0596 
0597 QCalendarSystem::CalendarSystem QCalendarSystem::calendarSystem() const
0598 {
0599     return d->calendarSystem();
0600 }
0601 
0602 QDate QCalendarSystem::epoch() const
0603 {
0604     return QDate::fromJulianDay(d->epoch());
0605 }
0606 
0607 QDate QCalendarSystem::earliestValidDate() const
0608 {
0609     return QDate::fromJulianDay(d->earliestValidDate());
0610 }
0611 
0612 QDate QCalendarSystem::latestValidDate() const
0613 {
0614     return QDate::fromJulianDay(d->latestValidDate());
0615 }
0616 
0617 int QCalendarSystem::maximumMonthsInYear() const
0618 {
0619     return d->maxMonthsInYear();
0620 }
0621 
0622 int QCalendarSystem::maximumDaysInYear() const
0623 {
0624     return d->maxDaysInYear();
0625 }
0626 
0627 int QCalendarSystem::maximumDaysInMonth() const
0628 {
0629     return d->maxDaysInMonth();
0630 }
0631 
0632 bool QCalendarSystem::isValid(const QDate &date) const
0633 {
0634     return date.isValid() && date >= earliestValidDate() && date <= latestValidDate();
0635 }
0636 
0637 bool QCalendarSystem::isValid(int year, int month, int day) const
0638 {
0639     return d->isValidMonth(year, month) && day >= 1 && day <= d->daysInMonth(year, month);
0640 }
0641 
0642 bool QCalendarSystem::isValid(int year, int dayOfYear) const
0643 {
0644     return d->isValidYear(year) && dayOfYear > 0 && dayOfYear <= d->daysInYear(year);
0645 }
0646 
0647 QDate QCalendarSystem::date(int year, int month, int day) const
0648 {
0649     if (isValid(year, month, day)) {
0650         return QDate::fromJulianDay(d->julianDayFromDate(year, month, day));
0651     } else {
0652         return QDate();
0653     }
0654 }
0655 
0656 QDate QCalendarSystem::date(int year, int dayOfYear) const
0657 {
0658     if (isValid(year, dayOfYear)) {
0659         return QDate::fromJulianDay(d->julianDayFromDate(year, 1, 1) + dayOfYear - 1);
0660     } else {
0661         return QDate();
0662     }
0663 }
0664 
0665 void QCalendarSystem::getDate(const QDate &date, int *year, int *month, int *day) const
0666 {
0667     int yy = 0;
0668     int mm = 0;
0669     int dd = 0;
0670 
0671     if (isValid(date)) {
0672         d->julianDayToDate(date.toJulianDay(), &yy, &mm, &dd);
0673     }
0674 
0675     if (year) {
0676         *year = yy;
0677     }
0678     if (month) {
0679         *month = mm;
0680     }
0681     if (day) {
0682         *day = dd;
0683     }
0684 }
0685 
0686 int QCalendarSystem::year(const QDate &date) const
0687 {
0688     int y = 0;
0689 
0690     if (isValid(date)) {
0691         d->julianDayToDate(date.toJulianDay(), &y, nullptr, nullptr);
0692     }
0693 
0694     return y;
0695 }
0696 
0697 int QCalendarSystem::month(const QDate &date) const
0698 {
0699     int m = 0;
0700 
0701     if (isValid(date)) {
0702         d->julianDayToDate(date.toJulianDay(), nullptr, &m, nullptr);
0703     }
0704 
0705     return m;
0706 }
0707 
0708 int QCalendarSystem::day(const QDate &date) const
0709 {
0710     int dd = 0;
0711 
0712     if (isValid(date)) {
0713         d->julianDayToDate(date.toJulianDay(), nullptr, nullptr, &dd);
0714     }
0715 
0716     return dd;
0717 }
0718 
0719 int QCalendarSystem::quarter(const QDate &date) const
0720 {
0721     if (isValid(date)) {
0722         int month;
0723         d->julianDayToDate(date.toJulianDay(), nullptr, &month, nullptr);
0724         return d->quarter(month);
0725     } else {
0726         return 0;
0727     }
0728 }
0729 
0730 int QCalendarSystem::quarter(int year, int month, int day) const
0731 {
0732     if (isValid(year, month, day)) {
0733         return d->quarter(month);
0734     } else {
0735         return 0;
0736     }
0737 }
0738 
0739 int QCalendarSystem::dayOfYear(const QDate &date) const
0740 {
0741     if (isValid(date)) {
0742         return date.toJulianDay() - firstDayOfYear(date).toJulianDay() + 1;
0743     } else {
0744         return 0;
0745     }
0746 }
0747 
0748 int QCalendarSystem::dayOfYear(int year, int month, int day) const
0749 {
0750     return dayOfYear(date(year, month, day));
0751 }
0752 
0753 int QCalendarSystem::dayOfWeek(const QDate &date) const
0754 {
0755     // jd 0 = Monday = weekday 1.  We've never skipped weekdays.
0756     if (isValid(date)) {
0757         if (date.toJulianDay() >= 0) {
0758             return (date.toJulianDay() % daysInWeek()) + 1;
0759         } else {
0760             return ((date.toJulianDay() + 1) % daysInWeek()) + daysInWeek();
0761         }
0762     } else {
0763         return 0;
0764     }
0765 }
0766 
0767 int QCalendarSystem::dayOfWeek(int year, int month, int day) const
0768 {
0769     return dayOfWeek(date(year, month, day));
0770 }
0771 
0772 // TODO These are ISO weeks, may need to localise
0773 int QCalendarSystem::weekNumber(const QDate &date, int *yearNum) const
0774 {
0775     if (isValid(date)) {
0776         int year, month, day;
0777         d->julianDayToDate(date.toJulianDay(), &year, &month, &day);
0778         return weekNumber(year, month, day, yearNum);
0779     } else {
0780         return 0;
0781     }
0782 }
0783 
0784 /*
0785     \legalese
0786     Copyright (c) 1989 The Regents of the University of California.
0787     All rights reserved.
0788 
0789     Redistribution and use in source and binary forms are permitted
0790     provided that the above copyright notice and this paragraph are
0791     duplicated in all such forms and that any documentation,
0792     advertising materials, and other materials related to such
0793     distribution and use acknowledge that the software was developed
0794     by the University of California, Berkeley.  The name of the
0795     University may not be used to endorse or promote products derived
0796     from this software without specific prior written permission.
0797     THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
0798     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
0799     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
0800 */
0801 // TODO These are ISO weeks, may need to localise
0802 // TODO Replace with cleanly licensed routine
0803 int QCalendarSystem::weekNumber(int year, int month, int day, int *yearNum) const
0804 {
0805     if (!isValid(year, month, day)) {
0806         if (yearNum) {
0807             *yearNum = 0;
0808         }
0809         return 0;
0810     }
0811 
0812     int yday = dayOfYear(year, month, day) - 1;
0813     int wday = dayOfWeek(year, month, day);
0814     if (wday == 7) {
0815         wday = 0;
0816     }
0817     int w;
0818 
0819     for (;;) {
0820         int len, bot, top;
0821 
0822         len = d->daysInYear(year);
0823         /*
0824         ** What yday (-3 ... 3) does
0825         ** the ISO year begin on?
0826         */
0827         bot = ((yday + 11 - wday) % 7) - 3;
0828         /*
0829         ** What yday does the NEXT
0830         ** ISO year begin on?
0831         */
0832         top = bot - (len % 7);
0833         if (top < -3) {
0834             top += 7;
0835         }
0836         top += len;
0837         if (yday >= top) {
0838             ++year;
0839             w = 1;
0840             break;
0841         }
0842         if (yday >= bot) {
0843             w = 1 + ((yday - bot) / 7);
0844             break;
0845         }
0846         --year;
0847         yday += d->daysInYear(year);
0848     }
0849 
0850     if (yearNum) {
0851         *yearNum = year;
0852     }
0853 
0854     return w;
0855 }
0856 
0857 int QCalendarSystem::monthsInYear(const QDate &date) const
0858 {
0859     if (isValid(date)) {
0860         return d->monthsInYear(year(date));
0861     } else {
0862         return 0;
0863     }
0864 }
0865 
0866 int QCalendarSystem::monthsInYear(int year) const
0867 {
0868     if (d->isValidYear(year)) {
0869         return d->monthsInYear(year);
0870     } else {
0871         return 0;
0872     }
0873 }
0874 
0875 int QCalendarSystem::weeksInYear(const QDate &date) const
0876 {
0877     if (isValid(date)) {
0878         return weeksInYear(year(date));
0879     } else {
0880         return 0;
0881     }
0882 }
0883 
0884 // TODO This is ISO weeks, may need to localise
0885 int QCalendarSystem::weeksInYear(int year) const
0886 {
0887     if (d->isValidYear(year)) {
0888         int weekYear = year;
0889         int lastWeek = weekNumber(lastDayOfYear(year), &weekYear);
0890         if (lastWeek < 1 || weekYear != year) {
0891             lastWeek = weekNumber(addDays(lastDayOfYear(year), -7), &weekYear);
0892         }
0893         return lastWeek;
0894     } else {
0895         return 0;
0896     }
0897 }
0898 
0899 int QCalendarSystem::daysInYear(const QDate &date) const
0900 {
0901     if (isValid(date)) {
0902         return d->daysInYear(year(date));
0903     } else {
0904         return 0;
0905     }
0906 }
0907 
0908 int QCalendarSystem::daysInYear(int year) const
0909 {
0910     if (d->isValidYear(year)) {
0911         return d->daysInYear(year);
0912     } else {
0913         return 0;
0914     }
0915 }
0916 
0917 int QCalendarSystem::daysInMonth(const QDate &date) const
0918 {
0919     if (isValid(date)) {
0920         int year, month;
0921         d->julianDayToDate(date.toJulianDay(), &year, &month, nullptr);
0922         return d->daysInMonth(year, month);
0923     } else {
0924         return 0;
0925     }
0926 }
0927 
0928 int QCalendarSystem::daysInMonth(int year, int month) const
0929 {
0930     if (d->isValidMonth(year, month)) {
0931         return d->daysInMonth(year, month);
0932     } else {
0933         return 0;
0934     }
0935 }
0936 
0937 int QCalendarSystem::daysInWeek() const
0938 {
0939     return 7;
0940 }
0941 
0942 bool QCalendarSystem::isLeapYear(const QDate &date) const
0943 {
0944     if (isValid(date)) {
0945         return d->isLeapYear(year(date));
0946     } else {
0947         return false;
0948     }
0949 }
0950 
0951 bool QCalendarSystem::isLeapYear(int year) const
0952 {
0953     if (d->isValidYear(year)) {
0954         return d->isLeapYear(year);
0955     } else {
0956         return false;
0957     }
0958 }
0959 
0960 QDate QCalendarSystem::addYears(const QDate &dt, int years) const
0961 {
0962     if (isValid(dt)) {
0963         int year, month, day;
0964         d->julianDayToDate(dt.toJulianDay(), &year, &month, &day);
0965         year = d->addYears(year, years);
0966         month = qMin(month, d->monthsInYear(year));
0967         return date(year, month, qMin(day, d->daysInMonth(year, month)));
0968     } else {
0969         return QDate();
0970     }
0971 }
0972 
0973 QDate QCalendarSystem::addMonths(const QDate &dt, int months) const
0974 {
0975     if (isValid(dt)) {
0976         int year, month, day;
0977         d->julianDayToDate(dt.toJulianDay(), &year, &month, &day);
0978         while (months != 0) {
0979             if (months < 0) {
0980                 if (month + months >= 1) {
0981                     month += months;
0982                     months = 0;
0983                 } else if (months < 0) {
0984                     year = d->addYears(year, -1);
0985                     months += d->monthsInYear(year);
0986                 }
0987             } else {
0988                 int miy = d->monthsInYear(year);
0989                 if (month + months <= miy) {
0990                     month += months;
0991                     months = 0;
0992                 } else {
0993                     year = d->addYears(year, 1);
0994                     months -= miy;
0995                 }
0996             }
0997         }
0998         return date(year, month, qMin(day, d->daysInMonth(year, month)));
0999     } else {
1000         return QDate();
1001     }
1002 }
1003 
1004 QDate QCalendarSystem::addDays(const QDate &date, int days) const
1005 {
1006     return date.addDays(days);
1007 }
1008 
1009 // Caters for Leap Months, but possibly not for Hebrew
1010 int QCalendarSystem::yearsDifference(const QDate &fromDate, const QDate &toDate) const
1011 {
1012     if (!isValid(fromDate) || !isValid(toDate) || toDate == fromDate) {
1013         return 0;
1014     }
1015 
1016     if (toDate < fromDate) {
1017         return -yearsDifference(toDate, fromDate);
1018     }
1019 
1020     int y1, m1, d1, y2, m2, d2;
1021     d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1);
1022     d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2);
1023 
1024     if (y2 == y1) {
1025         return 0;
1026     }
1027 
1028     if (m2 > m1) {
1029         return d->diffYears(y1, y2);
1030     }
1031 
1032     if (m2 < m1) {
1033         return d->diffYears(y1, y2) - 1;
1034     }
1035 
1036     // m2 == m1
1037     // Allow for last day of month to last day of month and leap days
1038     // e.g. 2000-02-29 to 2001-02-28 is 1 year not 0 years
1039     if ((d2 >= d1) || (d1 == d->daysInMonth(y1, m1) && d2 == d->daysInMonth(y2, m2))) {
1040         return d->diffYears(y1, y2);
1041     } else {
1042         return d->diffYears(y1, y2) - 1;
1043     }
1044 }
1045 
1046 // Caters for Leap Months, but possibly not for Hebrew
1047 int QCalendarSystem::monthsDifference(const QDate &fromDate, const QDate &toDate) const
1048 {
1049     if (!isValid(fromDate) || !isValid(toDate) || toDate == fromDate) {
1050         return 0;
1051     }
1052 
1053     if (toDate < fromDate) {
1054         return -monthsDifference(toDate, fromDate);
1055     }
1056 
1057     int y1, m1, d1, y2, m2, d2, my;
1058     d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1);
1059     d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2);
1060 
1061     // Calculate number of months in full years preceding y2
1062     if (y2 == y1) {
1063         my = 0;
1064     } else if (d->hasLeapMonths()) {
1065         my = 0;
1066         for (int y = y1; y < y2; y = d->addYears(y, 1)) {
1067             my = my + monthsInYear(y);
1068         }
1069     } else {
1070         my = d->diffYears(y1, y2) * monthsInYear(y2);
1071     }
1072 
1073     // Allow for last day of month to last day of month and leap days
1074     // e.g. 2010-03-31 to 2010-04-30 is 1 month not 0 months
1075     // also 2000-02-29 to 2001-02-28 is 12 months not 11 months
1076     if ((d2 >= d1) || (d1 == d->daysInMonth(y1, m1) && d2 == d->daysInMonth(y2, m2))) {
1077         return my + m2 - m1;
1078     } else {
1079         return my + m2 - m1 - 1;
1080     }
1081 }
1082 
1083 qint64 QCalendarSystem::daysDifference(const QDate &fromDate, const QDate &toDate) const
1084 {
1085     if (isValid(fromDate) && isValid(toDate)) {
1086         return toDate.toJulianDay() - fromDate.toJulianDay();
1087     } else {
1088         return 0;
1089     }
1090 }
1091 
1092 // Caters for Leap Months, but possibly not for Hebrew
1093 void QCalendarSystem::dateDifference(const QDate &fromDate, const QDate &toDate, int *years, int *months, int *days, int *direction) const
1094 {
1095     int dy = 0;
1096     int dm = 0;
1097     int dd = 0;
1098     int dir = 1;
1099 
1100     if (isValid(fromDate) && isValid(toDate) && fromDate != toDate) {
1101         if (toDate < fromDate) {
1102             dateDifference(toDate, fromDate, &dy, &dm, &dd, nullptr);
1103             dir = -1;
1104         } else {
1105             int y1, m1, d1, y2, m2, d2;
1106             d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1);
1107             d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2);
1108 
1109             dy = yearsDifference(fromDate, toDate);
1110 
1111             // Calculate months and days difference
1112             int miy0 = d->monthsInYear(d->addYears(y2, -1));
1113             if (d2 >= d1) {
1114                 dm = (miy0 + m2 - m1) % miy0;
1115                 dd = d2 - d1;
1116             } else { // d2 < d1
1117                 // Allow for last day of month to last day of month and leap days
1118                 // e.g. 2010-03-31 to 2010-04-30 is 1 month
1119                 //      2000-02-29 to 2001-02-28 is 1 year
1120                 //      2000-02-29 to 2001-03-01 is 1 year 1 day
1121                 int dim0 = daysInMonth(addMonths(toDate, -1));
1122                 int dim1 = d->daysInMonth(y1, m1);
1123                 if (d1 == dim1 && d2 == d->daysInMonth(y2, m2)) {
1124                     dm = (miy0 + m2 - m1) % miy0;
1125                     dd = 0;
1126                 } else if (month(addMonths(toDate, -1)) == m1 && dim0 < dim1) {
1127                     // Special case where fromDate = leap day and toDate in month following but non-leap year
1128                     // e.g. 2000-02-29 to 2001-03-01 needs to use 29 to calculate day number not 28
1129                     dm = (miy0 + m2 - m1 - 1) % miy0;
1130                     dd = (dim1 + d2 - d1) % dim1;
1131                 } else {
1132                     dm = (miy0 + m2 - m1 - 1) % miy0;
1133                     dd = (dim0 + d2 - d1) % dim0;
1134                 }
1135             }
1136         }
1137     }
1138 
1139     if (years) {
1140         *years = dy;
1141     }
1142     if (months) {
1143         *months = dm;
1144     }
1145     if (days) {
1146         *days = dd;
1147     }
1148     if (direction) {
1149         *direction = dir;
1150     }
1151 }
1152 
1153 QDate QCalendarSystem::firstDayOfYear(const QDate &dt) const
1154 {
1155     if (isValid(dt)) {
1156         return date(year(dt), 1, 1);
1157     } else {
1158         return QDate();
1159     }
1160 }
1161 
1162 QDate QCalendarSystem::firstDayOfYear(int year) const
1163 {
1164     return date(year, 1, 1);
1165 }
1166 
1167 QDate QCalendarSystem::lastDayOfYear(const QDate &dt) const
1168 {
1169     if (isValid(dt)) {
1170         int y = year(dt);
1171         return date(y, d->daysInYear(y));
1172     } else {
1173         return QDate();
1174     }
1175 }
1176 
1177 QDate QCalendarSystem::lastDayOfYear(int year) const
1178 {
1179     if (d->isValidYear(year)) {
1180         return date(year, d->daysInYear(year));
1181     } else {
1182         return QDate();
1183     }
1184 }
1185 
1186 QDate QCalendarSystem::firstDayOfMonth(const QDate &dt) const
1187 {
1188     int year, month;
1189     getDate(dt, &year, &month, nullptr);
1190     return date(year, month, 1);
1191 }
1192 
1193 QDate QCalendarSystem::firstDayOfMonth(int year, int month) const
1194 {
1195     return date(year, month, 1);
1196 }
1197 
1198 QDate QCalendarSystem::lastDayOfMonth(const QDate &dt) const
1199 {
1200     int year, month;
1201     getDate(dt, &year, &month, nullptr);
1202     return date(year, month, daysInMonth(year, month));
1203 }
1204 
1205 QDate QCalendarSystem::lastDayOfMonth(int year, int month) const
1206 {
1207     return date(year, month, daysInMonth(year, month));
1208 }