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