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 }