File indexing completed on 2025-01-05 03:52:00
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2014-11-03 0007 * Description : calendar system. 0008 * 0009 * SPDX-FileCopyrightText: 2014 by John Layt <john at layt dot net> 0010 * SPDX-FileCopyrightText: 2016-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "calsystem.h" 0017 0018 // Local includes 0019 0020 #include "digikam_debug.h" 0021 0022 namespace DigikamGenericCalendarPlugin 0023 { 0024 0025 class Q_DECL_HIDDEN CalSystemPrivate : public QSharedData 0026 { 0027 public: 0028 0029 explicit CalSystemPrivate(CalSystem::CalendarSystem calendar); 0030 0031 CalSystem::CalendarSystem calendarSystem() const; 0032 qint64 epoch() const; 0033 qint64 earliestValidDate() const; 0034 int earliestValidYear() const; 0035 qint64 latestValidDate() const; 0036 int latestValidYear() const; 0037 int yearOffset() const; 0038 int maxMonthsInYear() const; 0039 int monthsInYear(int year) const; 0040 int maxDaysInYear() const; 0041 int daysInYear(int year) const; 0042 int maxDaysInMonth() const; 0043 int daysInMonth(int year, int month) const; 0044 bool hasYearZero() const; 0045 bool hasLeapMonths() const; 0046 0047 int quarter(int month) const; 0048 bool isLeapYear(int year) const; 0049 void julianDayToDate(qint64 jd, int* year, int* month, int* day) const; 0050 qint64 julianDayFromDate(int yr, int mth, int dy) const; 0051 0052 bool isValidYear(int year) const; 0053 bool isValidMonth(int year, int month) const; 0054 int addYears(int y1, int years) const; 0055 int diffYears(int y1, int y2) const; 0056 0057 public: 0058 0059 CalSystem::CalendarSystem m_calendarSystem; 0060 }; 0061 0062 static const char julianMonths[] = 0063 { 0064 0, 0065 31, 0066 28, 0067 31, 0068 30, 0069 31, 0070 30, 0071 31, 0072 31, 0073 30, 0074 31, 0075 30, 0076 31 0077 }; 0078 0079 CalSystemPrivate::CalSystemPrivate(CalSystem::CalendarSystem calendar) 0080 : QSharedData (), 0081 m_calendarSystem(calendar) 0082 { 0083 } 0084 0085 CalSystem::CalendarSystem CalSystemPrivate::calendarSystem() const 0086 { 0087 if (m_calendarSystem == CalSystem::DefaultCalendar) 0088 { 0089 return CalSystem::GregorianCalendar; 0090 } 0091 else 0092 { 0093 return m_calendarSystem; 0094 } 0095 } 0096 0097 qint64 CalSystemPrivate::epoch() const 0098 { 0099 switch (calendarSystem()) 0100 { 0101 case CalSystem::GregorianCalendar: 0102 return 1721426; // 0001-01-01 Gregorian 0103 0104 case CalSystem::CopticCalendar: 0105 return 1825030; // 0001-01-01 == 0284-08-29 Gregorian 0106 0107 case CalSystem::EthiopicCalendar: 0108 return 1724221; // 0001-01-01 == 0008-08-29 Gregorian 0109 0110 case CalSystem::EthiopicAmeteAlemCalendar: 0111 return -284655; // 0001-01-01 == -5492-08-29 Gregorian 0112 0113 case CalSystem::IndianNationalCalendar: 0114 return 1749994; // 0000-01-01 == 0078-03-22 Gregorian 0115 0116 case CalSystem::IslamicCivilCalendar: 0117 return 1948440; // 0001-01-01 == 0622-07-19 Gregorian 0118 0119 case CalSystem::ISO8601Calendar: 0120 return 1721060; // 0000-01-01 Gregorian 0121 0122 case CalSystem::JapaneseCalendar: 0123 return 1721426; // 0001-01-01 Gregorian 0124 0125 case CalSystem::JulianCalendar: 0126 return 1721424; // 0001-01-01 == Gregorian 0127 0128 case CalSystem::ROCCalendar: 0129 return 2419403; // 0001-01-01 == 1912-01-01 Gregorian 0130 0131 case CalSystem::ThaiCalendar: 0132 return 1522734; // 0000-01-01 == -0544-01-01 Gregorian 0133 0134 default: 0135 return 0; 0136 } 0137 } 0138 0139 qint64 CalSystemPrivate::earliestValidDate() const 0140 { 0141 switch (calendarSystem()) 0142 { 0143 case CalSystem::GregorianCalendar: 0144 return -31738; // -4800-01-01 Gregorian 0145 0146 case CalSystem::CopticCalendar: 0147 return 1825030; // 0001-01-01 == 0284-08-29 Gregorian 0148 0149 case CalSystem::EthiopicCalendar: 0150 return 1724221; // 0001-01-01 == 0008-08-29 Gregorian 0151 0152 case CalSystem::EthiopicAmeteAlemCalendar: 0153 return -284655; // 0001-01-01 == -5492-08-29 Gregorian 0154 0155 case CalSystem::IndianNationalCalendar: 0156 return 1749994; // 0000-01-01 == 0078-03-22 Gregorian 0157 0158 case CalSystem::IslamicCivilCalendar: 0159 return 1948440; // 0001-01-01 == 0622-07-19 Gregorian 0160 0161 case CalSystem::ISO8601Calendar: 0162 return 1721060; // 0000-01-01 Gregorian 0163 0164 case CalSystem::JapaneseCalendar: 0165 return -31738; // -4800-01-01 Gregorian 0166 0167 case CalSystem::JulianCalendar: 0168 return -31776; // -4800-01-01 Julian 0169 0170 case CalSystem::ROCCalendar: 0171 return 2419403; // 0001-01-01 == 1912-01-01 Gregorian 0172 0173 case CalSystem::ThaiCalendar: 0174 return 1522734; // 0000-01-01 == -0544-01-01 Gregorian 0175 0176 default: 0177 return 0; 0178 } 0179 } 0180 0181 int CalSystemPrivate::earliestValidYear() const 0182 { 0183 switch (calendarSystem()) 0184 { 0185 case CalSystem::GregorianCalendar: 0186 case CalSystem::JapaneseCalendar: 0187 case CalSystem::JulianCalendar: 0188 return -4800; 0189 0190 case CalSystem::IndianNationalCalendar: 0191 case CalSystem::ISO8601Calendar: 0192 case CalSystem::ThaiCalendar: 0193 return 0; 0194 0195 default: 0196 return 1; 0197 } 0198 } 0199 0200 qint64 CalSystemPrivate::latestValidDate() const 0201 { 0202 switch (calendarSystem()) 0203 { 0204 case CalSystem::GregorianCalendar: 0205 return 5373484; // 9999-12-31 Gregorian 0206 0207 case CalSystem::CopticCalendar: 0208 return 5477164; // 9999-13-05 == 10283-11-12 Gregorian 0209 0210 case CalSystem::EthiopicCalendar: 0211 return 5376721; // 9999-13-05 == 10008-11-10 Gregorian 0212 0213 case CalSystem::EthiopicAmeteAlemCalendar: 0214 return 3367114; // 9999-13-05 == 4506-09-29 Gregorian 0215 0216 case CalSystem::IndianNationalCalendar: 0217 return 5402054; // 9999-12-30 == 10078-03-21 Gregorian 0218 0219 case CalSystem::IslamicCivilCalendar: 0220 return 5491751; // 9999-12-29 == 10323-10-21 Gregorian 0221 0222 case CalSystem::ISO8601Calendar: 0223 return 5373484; // 9999-12-31 Gregorian 0224 0225 case CalSystem::JapaneseCalendar: 0226 return 5373484; // 9999-12-31 Gregorian 0227 0228 case CalSystem::JulianCalendar: 0229 return 5373557; // 9999-12-31 == 10000-03-13 Gregorian 0230 0231 case CalSystem::ROCCalendar: 0232 return 6071462; // 9999-12-31 == 11910-12-31 Gregorian 0233 0234 case CalSystem::ThaiCalendar: 0235 return 5175158; // 9999-12-31 == 9456-12-31 Gregorian 0236 0237 default: 0238 return 0; 0239 } 0240 } 0241 0242 int CalSystemPrivate::latestValidYear() const 0243 { 0244 return 9999; 0245 } 0246 0247 int CalSystemPrivate::yearOffset() const 0248 { 0249 switch (calendarSystem()) 0250 { 0251 case CalSystem::ROCCalendar: 0252 return 1911; // 0001-01-01 == 1912-01-01 Gregorian 0253 0254 case CalSystem::ThaiCalendar: 0255 return -543; // 0000-01-01 == -544-01-01 Gregorian 0256 0257 default: 0258 return 0; 0259 } 0260 } 0261 0262 int CalSystemPrivate::maxMonthsInYear() const 0263 { 0264 switch (calendarSystem()) 0265 { 0266 case CalSystem::CopticCalendar: 0267 case CalSystem::EthiopicCalendar: 0268 case CalSystem::EthiopicAmeteAlemCalendar: 0269 return 13; 0270 0271 default: 0272 return 12; 0273 } 0274 } 0275 0276 int CalSystemPrivate::monthsInYear(int /*year*/) const 0277 { 0278 switch (calendarSystem()) 0279 { 0280 case CalSystem::CopticCalendar: 0281 case CalSystem::EthiopicCalendar: 0282 case CalSystem::EthiopicAmeteAlemCalendar: 0283 return 13; 0284 0285 default: 0286 return 12; 0287 } 0288 } 0289 0290 int CalSystemPrivate::maxDaysInYear() const 0291 { 0292 switch (calendarSystem()) 0293 { 0294 case CalSystem::IslamicCivilCalendar: 0295 return 355; 0296 0297 default: 0298 return 366; 0299 } 0300 } 0301 0302 int CalSystemPrivate::daysInYear(int year) const 0303 { 0304 switch (calendarSystem()) 0305 { 0306 case CalSystem::IslamicCivilCalendar: 0307 return (isLeapYear(year) ? 355 : 354); 0308 0309 default: 0310 return (isLeapYear(year) ? 366 : 365); 0311 } 0312 } 0313 0314 int CalSystemPrivate::maxDaysInMonth() const 0315 { 0316 switch (calendarSystem()) 0317 { 0318 case CalSystem::CopticCalendar: 0319 case CalSystem::EthiopicCalendar: 0320 case CalSystem::EthiopicAmeteAlemCalendar: 0321 case CalSystem::IslamicCivilCalendar: 0322 return 30; 0323 0324 default: 0325 return 31; 0326 } 0327 } 0328 0329 int CalSystemPrivate::daysInMonth(int year, int month) const 0330 { 0331 if (month < 1 || month > monthsInYear(year)) 0332 { 0333 return 0; 0334 } 0335 0336 switch (calendarSystem()) 0337 { 0338 case CalSystem::GregorianCalendar: 0339 case CalSystem::ISO8601Calendar: 0340 case CalSystem::JapaneseCalendar: 0341 case CalSystem::ROCCalendar: 0342 case CalSystem::ThaiCalendar: 0343 case CalSystem::JulianCalendar: 0344 { 0345 if ((month == 2) && isLeapYear(year)) 0346 { 0347 return 29; 0348 } 0349 else 0350 { 0351 return julianMonths[month]; 0352 } 0353 } 0354 0355 case CalSystem::CopticCalendar: 0356 case CalSystem::EthiopicCalendar: 0357 case CalSystem::EthiopicAmeteAlemCalendar: 0358 { 0359 if (month == 13) 0360 { 0361 return (isLeapYear(year) ? 6 : 5); 0362 } 0363 else 0364 { 0365 return 30; 0366 } 0367 } 0368 0369 case CalSystem::IndianNationalCalendar: 0370 { 0371 if (month >= 7) 0372 { 0373 return 30; 0374 } 0375 else if (month >= 2) 0376 { 0377 return 31; 0378 } 0379 else if (isLeapYear(year)) 0380 { 0381 return 31; 0382 } 0383 else 0384 { 0385 return 30; 0386 } 0387 } 0388 0389 case CalSystem::IslamicCivilCalendar: 0390 { 0391 if ((month == 12) && isLeapYear(year)) 0392 { 0393 return 30; 0394 } 0395 else if ((month % 2) == 0) 0396 { 0397 return 29; 0398 } 0399 else 0400 { 0401 return 30; 0402 } 0403 } 0404 0405 default: 0406 return 0; 0407 } 0408 } 0409 0410 bool CalSystemPrivate::hasYearZero() const 0411 { 0412 switch (calendarSystem()) 0413 { 0414 case CalSystem::IndianNationalCalendar: 0415 case CalSystem::ISO8601Calendar: 0416 case CalSystem::ThaiCalendar: 0417 return true; 0418 0419 default: 0420 return false; 0421 } 0422 } 0423 0424 bool CalSystemPrivate::hasLeapMonths() const 0425 { 0426 return false; 0427 } 0428 0429 int CalSystemPrivate::quarter(int month) const 0430 { 0431 switch (calendarSystem()) 0432 { 0433 case CalSystem::CopticCalendar: 0434 case CalSystem::EthiopicCalendar: 0435 case CalSystem::EthiopicAmeteAlemCalendar: 0436 { 0437 if (month == 13) 0438 { 0439 // Consider the short epagomenal month as part of the 4th quarter 0440 return 4; 0441 } 0442 0443 #if __GNUC__ >= 7 // krazy:exclude=cpp 0444 0445 [[fallthrough]]; 0446 0447 #endif 0448 } 0449 0450 default: 0451 { 0452 return (((month - 1) / 3) + 1); 0453 } 0454 } 0455 } 0456 0457 bool CalSystemPrivate::isLeapYear(int year) const 0458 { 0459 year = year + yearOffset(); 0460 0461 // Uses same rule as Gregorian and in same years as Gregorian to keep in sync 0462 // Can't use yearOffset() as this offset only applies for isLeapYear() 0463 0464 if (calendarSystem() == CalSystem::IndianNationalCalendar) 0465 { 0466 year = year + 78; 0467 } 0468 0469 if ((year < 1) && !hasYearZero()) 0470 { 0471 ++year; 0472 } 0473 0474 switch (calendarSystem()) 0475 { 0476 case CalSystem::GregorianCalendar: 0477 case CalSystem::IndianNationalCalendar: 0478 case CalSystem::ISO8601Calendar: 0479 case CalSystem::JapaneseCalendar: 0480 case CalSystem::ROCCalendar: 0481 case CalSystem::ThaiCalendar: 0482 return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)); 0483 0484 case CalSystem::CopticCalendar: 0485 case CalSystem::EthiopicCalendar: 0486 case CalSystem::EthiopicAmeteAlemCalendar: 0487 return ((year % 4) == 3); 0488 0489 case CalSystem::JulianCalendar: 0490 return ((year % 4) == 0); 0491 0492 case CalSystem::IslamicCivilCalendar: 0493 return ((((11 * year) + 14) % 30) < 11); 0494 0495 default: 0496 return false; 0497 } 0498 } 0499 0500 void CalSystemPrivate::julianDayToDate(qint64 jd, int* year, int* month, int* day) const 0501 { 0502 int yy = 0; 0503 int mm = 0; 0504 int dd = 0; 0505 0506 switch (calendarSystem()) 0507 { 0508 case CalSystem::GregorianCalendar: 0509 case CalSystem::ISO8601Calendar: 0510 case CalSystem::JapaneseCalendar: 0511 case CalSystem::ROCCalendar: 0512 case CalSystem::ThaiCalendar: 0513 { 0514 // Formula from The Calendar FAQ by Claus Tondering 0515 // www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000 0516 0517 qint64 a = jd + 32044; 0518 qint64 b = ((4 * a) + 3) / 146097; 0519 qint64 c = a - ((146097 * b) / 4); 0520 qint64 d = ((4 * c) + 3) / 1461; 0521 qint64 e = c - ((1461 * d) / 4); 0522 qint64 m = ((5 * e) + 2) / 153; 0523 dd = e - (((153 * m) + 2) / 5) + 1; 0524 mm = m + 3 - (12 * (m / 10)); 0525 yy = (100 * b) + d - 4800 + (m / 10); 0526 break; 0527 } 0528 0529 case CalSystem::CopticCalendar: 0530 case CalSystem::EthiopicCalendar: 0531 case CalSystem::EthiopicAmeteAlemCalendar: 0532 { 0533 // Formula derived from first principles by John Layt 0534 0535 qint64 s = jd - (epoch() - 365); 0536 qint64 l = s / 1461; 0537 yy = (l * 4) + qMin((qint64)3, (s % 1461) / 365); 0538 qint64 diy = s - (yy * 365) + (yy / 4); 0539 mm = (diy / 30) + 1; 0540 dd = (diy % 30) + 1; 0541 break; 0542 } 0543 0544 case CalSystem::IndianNationalCalendar: 0545 { 0546 // Formula from the "Explanatory Supplement to the Astronomical Almanac" 0547 // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory 0548 // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government 0549 0550 qint64 l = jd + 68518; 0551 qint64 n = (4 * l) / 146097; 0552 l = l - (146097 * n + 3) / 4; 0553 qint64 i = (4000 * (l + 1)) / 1461001; 0554 l = l - (1461 * i) / 4 + 1; 0555 qint64 j = ((l - 1) / 31) * (1 - l / 185) + (l / 185) * ((l - 156) / 30 + 5) - l / 366; 0556 dd = l - 31 * j + ((j + 2) / 8) * (j - 5); 0557 l = j / 11; 0558 mm = j + 2 - 12 * l; 0559 yy = 100 * (n - 49) + l + i - 78; 0560 break; 0561 } 0562 0563 case CalSystem::IslamicCivilCalendar: 0564 { 0565 // Formula from the "Explanatory Supplement to the Astronomical Almanac" 0566 // Revised Edition 2006 section ??? pp ???, US Naval Observatory 0567 // Derived from Fliegel & Van Flandern 1968 0568 0569 qint64 l = jd - epoch() + 10632; 0570 qint64 n = (l - 1) / 10631; 0571 l = l - 10631 * n + 354; 0572 int j = ((10985 - l) / 5316) * ((50 * l) / 17719) + (l / 5670) * ((43 * l) / 15238); 0573 l = l - ((30 - j) / 15) * ((17719 * j) / 50) - (j / 16) * ((15238 * j) / 43) + 29; 0574 yy = (30 * n) + j - 30; 0575 mm = (24 * l) / 709; 0576 dd = l - ((709 * mm) / 24); 0577 break; 0578 } 0579 0580 case CalSystem::JulianCalendar: 0581 { 0582 // Formula from The Calendar FAQ by Claus Tondering 0583 // www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000 0584 0585 qint64 b = 0; 0586 qint64 c = jd + 32082; 0587 qint64 d = ((4 * c) + 3) / 1461; 0588 qint64 e = c - ((1461 * d) / 4); 0589 qint64 m = ((5 * e) + 2) / 153; 0590 dd = e - (((153 * m) + 2) / 5) + 1; 0591 mm = m + 3 - (12 * (m / 10)); 0592 yy = (100 * b) + d - 4800 + (m / 10); 0593 break; 0594 } 0595 0596 default: 0597 break; 0598 } 0599 0600 if (!hasYearZero() && (yy < 1)) 0601 { 0602 yy -= 1; 0603 } 0604 0605 yy = yy - yearOffset(); 0606 0607 if (year) 0608 { 0609 *year = yy; 0610 } 0611 0612 if (month) 0613 { 0614 *month = mm; 0615 } 0616 0617 if (day) 0618 { 0619 *day = dd; 0620 } 0621 } 0622 0623 qint64 CalSystemPrivate::julianDayFromDate(int yr, int mth, int dy) const 0624 { 0625 qint64 jd = 0; 0626 qint64 year = yr + yearOffset(); 0627 qint64 month = mth; 0628 qint64 day = dy; 0629 0630 if (year < 1 && !hasYearZero()) 0631 { 0632 year = year + 1; 0633 } 0634 0635 switch (calendarSystem()) 0636 { 0637 case CalSystem::GregorianCalendar: 0638 case CalSystem::ISO8601Calendar: 0639 case CalSystem::JapaneseCalendar: 0640 case CalSystem::ROCCalendar: 0641 case CalSystem::ThaiCalendar: 0642 { 0643 // Formula from The Calendar FAQ by Claus Tondering 0644 // www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000 0645 0646 int a = (14 - month) / 12; 0647 year = year + 4800 - a; 0648 int m = month + (12 * a) - 3; 0649 jd = day 0650 + (((153 * m) + 2) / 5) 0651 + (365 * year) 0652 + (year / 4) 0653 - (year / 100) 0654 + (year / 400) 0655 - 32045; 0656 break; 0657 } 0658 0659 case CalSystem::CopticCalendar: 0660 case CalSystem::EthiopicCalendar: 0661 case CalSystem::EthiopicAmeteAlemCalendar: 0662 { 0663 // Formula derived from first principles by John Layt 0664 0665 jd = epoch() - 1 0666 + ((year - 1) * 365) 0667 + (year / 4) 0668 + ((month - 1) * 30) 0669 + day; 0670 break; 0671 } 0672 0673 case CalSystem::IndianNationalCalendar: 0674 { 0675 // Formula from the "Explanatory Supplement to the Astronomical Almanac" 0676 // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory 0677 // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government 0678 0679 jd = 365 * year 0680 + (year + 78 - 1 / month) / 4 0681 + 31 * month 0682 - (month + 9) / 11 0683 - (month / 7) * (month - 7) 0684 - (3 * ((year + 78 - 1 / month) / 100 + 1)) / 4 0685 + day 0686 + 1749579; 0687 break; 0688 } 0689 0690 case CalSystem::IslamicCivilCalendar: 0691 { 0692 // Formula from the "Explanatory Supplement to the Astronomical Almanac" 0693 // Revised Edition 2006 section ??? pp ???, US Naval Observatory 0694 // Derived from Fliegel & Van Flandern 1968 0695 0696 jd = (3 + (11 * year)) / 30 0697 + 354 * year 0698 + 30 * month 0699 - (month - 1) / 2 0700 + day 0701 + epoch() 0702 - 385; 0703 break; 0704 } 0705 0706 case CalSystem::JulianCalendar: 0707 { 0708 // Formula from The Calendar FAQ by Claus Tondering 0709 // www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000 0710 0711 int a = (14 - month) / 12; 0712 year = year + 4800 - a; 0713 int m = month + (12 * a) - 3; 0714 jd = day 0715 + (((153 * m) + 2) / 5) 0716 + (365 * year) 0717 + (year / 4) 0718 - 32083; 0719 break; 0720 } 0721 0722 default: 0723 break; 0724 } 0725 0726 return jd; 0727 } 0728 0729 // Some private utility rules 0730 0731 bool CalSystemPrivate::isValidYear(int year) const 0732 { 0733 return ( 0734 (year >= earliestValidYear()) && 0735 (year <= latestValidYear()) && 0736 ((year == 0) ? hasYearZero() : true) 0737 ); 0738 } 0739 0740 bool CalSystemPrivate::isValidMonth(int year, int month) const 0741 { 0742 return ( 0743 isValidYear(year) && 0744 (month >= 1) && 0745 (month <= monthsInYear(year)) 0746 ); 0747 } 0748 0749 int CalSystemPrivate::addYears(int y1, int years) const 0750 { 0751 int y2 = y1 + years; 0752 0753 if (!hasYearZero()) 0754 { 0755 if ((y1 > 0) && (y2 <= 0)) 0756 { 0757 --y2; 0758 } 0759 else if ((y1 < 0) && (y2 >= 0)) 0760 { 0761 ++y2; 0762 } 0763 } 0764 0765 return y2; 0766 } 0767 0768 int CalSystemPrivate::diffYears(int y1, int y2) const 0769 { 0770 int dy = y2 - y1; 0771 0772 if (!hasYearZero()) 0773 { 0774 if ((y2 > 0) && (y1 < 0)) 0775 { 0776 dy -= 1; 0777 } 0778 else if ((y2 < 0) && (y1 > 0)) 0779 { 0780 dy += 1; 0781 } 0782 } 0783 0784 return dy; 0785 } 0786 0787 // --------------------------------------------------------------- 0788 0789 CalSystem::CalSystem(CalSystem::CalendarSystem calendar) 0790 : d(new CalSystemPrivate(calendar)) 0791 { 0792 } 0793 0794 CalSystem::~CalSystem() 0795 { 0796 } 0797 0798 CalSystem &CalSystem::operator=(const CalSystem &other) 0799 { 0800 d = other.d; 0801 0802 return *this; 0803 } 0804 0805 CalSystem::CalendarSystem CalSystem::calendarSystem() const 0806 { 0807 return d->calendarSystem(); 0808 } 0809 0810 QDate CalSystem::epoch() const 0811 { 0812 return QDate::fromJulianDay(d->epoch()); 0813 } 0814 0815 QDate CalSystem::earliestValidDate() const 0816 { 0817 return QDate::fromJulianDay(d->earliestValidDate()); 0818 } 0819 0820 QDate CalSystem::latestValidDate() const 0821 { 0822 return QDate::fromJulianDay(d->latestValidDate()); 0823 } 0824 0825 int CalSystem::maximumMonthsInYear() const 0826 { 0827 return d->maxMonthsInYear(); 0828 } 0829 0830 int CalSystem::maximumDaysInYear() const 0831 { 0832 return d->maxDaysInYear(); 0833 } 0834 0835 int CalSystem::maximumDaysInMonth() const 0836 { 0837 return d->maxDaysInMonth(); 0838 } 0839 0840 bool CalSystem::isValid(const QDate& date) const 0841 { 0842 return ( 0843 date.isValid() && 0844 (date >= earliestValidDate()) && 0845 (date <= latestValidDate()) 0846 ); 0847 } 0848 0849 bool CalSystem::isValid(int year, int month, int day) const 0850 { 0851 return ( 0852 d->isValidMonth(year, month) && 0853 (day >= 1) && 0854 (day <= d->daysInMonth(year, month)) 0855 ); 0856 } 0857 0858 bool CalSystem::isValid(int year, int dayOfYear) const 0859 { 0860 return ( 0861 d->isValidYear(year) && 0862 (dayOfYear > 0) && 0863 (dayOfYear <= d->daysInYear(year)) 0864 ); 0865 } 0866 0867 QDate CalSystem::date(int year, int month, int day) const 0868 { 0869 if (isValid(year, month, day)) 0870 { 0871 return QDate::fromJulianDay(d->julianDayFromDate(year, month, day)); 0872 } 0873 else 0874 { 0875 return QDate(); 0876 } 0877 } 0878 0879 QDate CalSystem::date(int year, int dayOfYear) const 0880 { 0881 if (isValid(year, dayOfYear)) 0882 { 0883 return QDate::fromJulianDay(d->julianDayFromDate(year, 1, 1) + dayOfYear - 1); 0884 } 0885 else 0886 { 0887 return QDate(); 0888 } 0889 } 0890 0891 void CalSystem::getDate(const QDate& date, int* year, int* month, int* day) const 0892 { 0893 int yy = 0; 0894 int mm = 0; 0895 int dd = 0; 0896 0897 if (isValid(date)) 0898 { 0899 d->julianDayToDate(date.toJulianDay(), &yy, &mm, &dd); 0900 } 0901 0902 if (year) 0903 { 0904 *year = yy; 0905 } 0906 if (month) 0907 { 0908 *month = mm; 0909 } 0910 if (day) 0911 { 0912 *day = dd; 0913 } 0914 } 0915 0916 int CalSystem::year(const QDate& date) const 0917 { 0918 int y = 0; 0919 0920 if (isValid(date)) 0921 { 0922 d->julianDayToDate(date.toJulianDay(), &y, nullptr, nullptr); 0923 } 0924 0925 return y; 0926 } 0927 0928 int CalSystem::month(const QDate& date) const 0929 { 0930 int m = 0; 0931 0932 if (isValid(date)) 0933 { 0934 d->julianDayToDate(date.toJulianDay(), nullptr, &m, nullptr); 0935 } 0936 0937 return m; 0938 } 0939 0940 int CalSystem::day(const QDate& date) const 0941 { 0942 int dd = 0; 0943 0944 if (isValid(date)) 0945 { 0946 d->julianDayToDate(date.toJulianDay(), nullptr, nullptr, &dd); 0947 } 0948 0949 return dd; 0950 } 0951 0952 int CalSystem::quarter(const QDate& date) const 0953 { 0954 if (isValid(date)) 0955 { 0956 int month; 0957 d->julianDayToDate(date.toJulianDay(), nullptr, &month, nullptr); 0958 0959 return d->quarter(month); 0960 } 0961 else 0962 { 0963 return 0; 0964 } 0965 } 0966 0967 int CalSystem::quarter(int year, int month, int day) const 0968 { 0969 if (isValid(year, month, day)) 0970 { 0971 return d->quarter(month); 0972 } 0973 else 0974 { 0975 return 0; 0976 } 0977 } 0978 0979 int CalSystem::dayOfYear(const QDate& date) const 0980 { 0981 if (isValid(date)) 0982 { 0983 return date.toJulianDay() - firstDayOfYear(date).toJulianDay() + 1; 0984 } 0985 else 0986 { 0987 return 0; 0988 } 0989 } 0990 0991 int CalSystem::dayOfYear(int year, int month, int day) const 0992 { 0993 return dayOfYear(date(year, month, day)); 0994 } 0995 0996 int CalSystem::dayOfWeek(const QDate& date) const 0997 { 0998 //jd 0 = Monday = weekday 1. We've never skipped weekdays. 0999 1000 if (isValid(date)) 1001 { 1002 if (date.toJulianDay() >= 0) 1003 { 1004 return (date.toJulianDay() % daysInWeek()) + 1; 1005 } 1006 else 1007 { 1008 return ((date.toJulianDay() + 1) % daysInWeek()) + daysInWeek(); 1009 } 1010 } 1011 else 1012 { 1013 return 0; 1014 } 1015 } 1016 1017 int CalSystem::dayOfWeek(int year, int month, int day) const 1018 { 1019 return dayOfWeek(date(year, month, day)); 1020 } 1021 1022 // TODO: These are ISO weeks, may need to localise 1023 int CalSystem::weekNumber(const QDate& date, int* yearNum) const 1024 { 1025 if (isValid(date)) 1026 { 1027 int year, month, day; 1028 d->julianDayToDate(date.toJulianDay(), &year, &month, &day); 1029 1030 return weekNumber(year, month, day, yearNum); 1031 } 1032 else 1033 { 1034 return 0; 1035 } 1036 } 1037 1038 /** 1039 * TODO: These are ISO weeks, may need to localise 1040 */ 1041 int CalSystem::weekNumber(int year, int month, int day, int* yearNum) const 1042 { 1043 if (!isValid(year, month, day)) 1044 { 1045 if (yearNum) 1046 { 1047 *yearNum = 0; 1048 } 1049 1050 return 0; 1051 } 1052 1053 int yday = dayOfYear(year, month, day) - 1; 1054 int wday = dayOfWeek(year, month, day); 1055 1056 if (wday == 7) 1057 { 1058 wday = 0; 1059 } 1060 1061 int w = 0; 1062 1063 for ( ; ; ) 1064 { 1065 int len = d->daysInYear(year); 1066 1067 // What yday (-3 ... 3) does the ISO year begin on? 1068 1069 int bot = ((yday + 11 - wday) % 7) - 3; 1070 1071 // What yday does the NEXT ISO year begin on? 1072 1073 int top = bot - (len % 7); 1074 1075 if (top < -3) 1076 { 1077 top += 7; 1078 } 1079 1080 top += len; 1081 1082 if (yday >= top) 1083 { 1084 ++year; 1085 w = 1; 1086 break; 1087 } 1088 1089 if (yday >= bot) 1090 { 1091 w = 1 + ((yday - bot) / 7); 1092 break; 1093 } 1094 1095 --year; 1096 yday += d->daysInYear(year); 1097 } 1098 1099 if (yearNum) 1100 { 1101 *yearNum = year; 1102 } 1103 1104 return w; 1105 } 1106 1107 int CalSystem::monthsInYear(const QDate& date) const 1108 { 1109 if (isValid(date)) 1110 { 1111 return d->monthsInYear(year(date)); 1112 } 1113 else 1114 { 1115 return 0; 1116 } 1117 } 1118 1119 int CalSystem::monthsInYear(int year) const 1120 { 1121 if (d->isValidYear(year)) 1122 { 1123 return d->monthsInYear(year); 1124 } 1125 else 1126 { 1127 return 0; 1128 } 1129 } 1130 1131 int CalSystem::weeksInYear(const QDate& date) const 1132 { 1133 if (isValid(date)) 1134 { 1135 return weeksInYear(year(date)); 1136 } 1137 else 1138 { 1139 return 0; 1140 } 1141 } 1142 1143 /** 1144 * TODO: This is ISO weeks, may need to localise 1145 */ 1146 int CalSystem::weeksInYear(int year) const 1147 { 1148 if (d->isValidYear(year)) 1149 { 1150 int weekYear = year; 1151 int lastWeek = weekNumber(lastDayOfYear(year), &weekYear); 1152 1153 if ((lastWeek < 1) || (weekYear != year)) 1154 { 1155 lastWeek = weekNumber(addDays(lastDayOfYear(year), -7), &weekYear); 1156 } 1157 1158 return lastWeek; 1159 } 1160 else 1161 { 1162 return 0; 1163 } 1164 } 1165 1166 int CalSystem::daysInYear(const QDate& date) const 1167 { 1168 if (isValid(date)) 1169 { 1170 return d->daysInYear(year(date)); 1171 } 1172 else 1173 { 1174 return 0; 1175 } 1176 } 1177 1178 int CalSystem::daysInYear(int year) const 1179 { 1180 if (d->isValidYear(year)) 1181 { 1182 return d->daysInYear(year); 1183 } 1184 else 1185 { 1186 return 0; 1187 } 1188 } 1189 1190 int CalSystem::daysInMonth(const QDate& date) const 1191 { 1192 if (isValid(date)) 1193 { 1194 int year, month; 1195 d->julianDayToDate(date.toJulianDay(), &year, &month, nullptr); 1196 1197 return d->daysInMonth(year, month); 1198 } 1199 else 1200 { 1201 return 0; 1202 } 1203 } 1204 1205 int CalSystem::daysInMonth(int year, int month) const 1206 { 1207 if (d->isValidMonth(year, month)) 1208 { 1209 return d->daysInMonth(year, month); 1210 } 1211 else 1212 { 1213 return 0; 1214 } 1215 } 1216 1217 int CalSystem::daysInWeek() const 1218 { 1219 return 7; 1220 } 1221 1222 bool CalSystem::isLeapYear(const QDate& date) const 1223 { 1224 if (isValid(date)) 1225 { 1226 return d->isLeapYear(year(date)); 1227 } 1228 else 1229 { 1230 return false; 1231 } 1232 } 1233 1234 bool CalSystem::isLeapYear(int year) const 1235 { 1236 if (d->isValidYear(year)) 1237 { 1238 return d->isLeapYear(year); 1239 } 1240 else 1241 { 1242 return false; 1243 } 1244 } 1245 1246 QDate CalSystem::addYears(const QDate& dt, int years) const 1247 { 1248 if (isValid(dt)) 1249 { 1250 int year, month, day; 1251 d->julianDayToDate(dt.toJulianDay(), &year, &month, &day); 1252 year = d->addYears(year, years); 1253 month = qMin(month, d->monthsInYear(year)); 1254 1255 return date(year, month, qMin(day, d->daysInMonth(year, month))); 1256 } 1257 else 1258 { 1259 return QDate(); 1260 } 1261 } 1262 1263 QDate CalSystem::addMonths(const QDate& dt, int months) const 1264 { 1265 if (isValid(dt)) 1266 { 1267 int year, month, day; 1268 d->julianDayToDate(dt.toJulianDay(), &year, &month, &day); 1269 1270 while (months != 0) 1271 { 1272 if (months < 0) 1273 { 1274 if ((month + months) >= 1) 1275 { 1276 month += months; 1277 months = 0; 1278 } 1279 else if (months < 0) 1280 { 1281 year = d->addYears(year, -1); 1282 months += d->monthsInYear(year); 1283 } 1284 } 1285 else 1286 { 1287 int miy = d->monthsInYear(year); 1288 1289 if ((month + months) <= miy) 1290 { 1291 month += months; 1292 months = 0; 1293 } 1294 else 1295 { 1296 year = d->addYears(year, 1); 1297 months -= miy; 1298 } 1299 } 1300 } 1301 1302 return date(year, month, qMin(day, d->daysInMonth(year, month))); 1303 } 1304 else 1305 { 1306 return QDate(); 1307 } 1308 } 1309 1310 QDate CalSystem::addDays(const QDate& date, int days) const 1311 { 1312 return date.addDays(days); 1313 } 1314 1315 /** 1316 * Caters for Leap Months, but possibly not for Hebrew 1317 */ 1318 int CalSystem::yearsDifference(const QDate& fromDate, const QDate& toDate) const 1319 { 1320 if (!isValid(fromDate) || !isValid(toDate) || (toDate == fromDate)) 1321 { 1322 return 0; 1323 } 1324 1325 if (toDate < fromDate) 1326 { 1327 return - yearsDifference(toDate, fromDate); 1328 } 1329 1330 int y1, m1, d1, y2, m2, d2; 1331 d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); 1332 d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); 1333 1334 if (y2 == y1) 1335 { 1336 return 0; 1337 } 1338 1339 if (m2 > m1) 1340 { 1341 return d->diffYears(y1, y2); 1342 } 1343 1344 if (m2 < m1) 1345 { 1346 return d->diffYears(y1, y2) - 1; 1347 } 1348 1349 // m2 == m1 1350 // Allow for last day of month to last day of month and leap days 1351 // e.g. 2000-02-29 to 2001-02-28 is 1 year not 0 years 1352 1353 if ((d2 >= d1) || (d1 == d->daysInMonth(y1, m1) && (d2 == d->daysInMonth(y2, m2)))) 1354 { 1355 return d->diffYears(y1, y2); 1356 } 1357 else 1358 { 1359 return d->diffYears(y1, y2) - 1; 1360 } 1361 } 1362 1363 /** 1364 * Caters for Leap Months, but possibly not for Hebrew 1365 */ 1366 int CalSystem::monthsDifference(const QDate& fromDate, const QDate& toDate) const 1367 { 1368 if (!isValid(fromDate) || !isValid(toDate) || (toDate == fromDate)) 1369 { 1370 return 0; 1371 } 1372 1373 if (toDate < fromDate) 1374 { 1375 return - monthsDifference(toDate, fromDate); 1376 } 1377 1378 int y1, m1, d1, y2, m2, d2, my; 1379 d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); 1380 d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); 1381 1382 // Calculate number of months in full years preceding y2 1383 1384 if (y2 == y1) 1385 { 1386 my = 0; 1387 } 1388 else if (d->hasLeapMonths()) 1389 { 1390 my = 0; 1391 1392 for (int y = y1 ; y < y2 ; y = d->addYears(y, 1)) 1393 { 1394 my = my + monthsInYear(y); 1395 } 1396 } 1397 else 1398 { 1399 my = d->diffYears(y1, y2) * monthsInYear(y2); 1400 } 1401 1402 // Allow for last day of month to last day of month and leap days 1403 // e.g. 2010-03-31 to 2010-04-30 is 1 month not 0 months 1404 // also 2000-02-29 to 2001-02-28 is 12 months not 11 months 1405 1406 if ((d2 >= d1) || (d1 == d->daysInMonth(y1, m1) && (d2 == d->daysInMonth(y2, m2)))) 1407 { 1408 return (my + m2 - m1); 1409 } 1410 else 1411 { 1412 return (my + m2 - m1 - 1); 1413 } 1414 } 1415 1416 qint64 CalSystem::daysDifference(const QDate& fromDate, const QDate& toDate) const 1417 { 1418 if (isValid(fromDate) && isValid(toDate)) 1419 { 1420 return toDate.toJulianDay() - fromDate.toJulianDay(); 1421 } 1422 else 1423 { 1424 return 0; 1425 } 1426 } 1427 1428 /** 1429 * Caters for Leap Months, but possibly not for Hebrew 1430 */ 1431 bool CalSystem::dateDifference(const QDate& fromDate, const QDate& toDate, 1432 int* years, int* months, int* days, int* direction) const 1433 { 1434 int dy = 0; 1435 int dm = 0; 1436 int dd = 0; 1437 int dir = 1; 1438 1439 if (isValid(fromDate) && isValid(toDate) && fromDate != toDate) 1440 { 1441 if (toDate < fromDate) 1442 { 1443 dateDifference(toDate, fromDate, &dy, &dm, &dd, nullptr); 1444 dir = -1; 1445 } 1446 else 1447 { 1448 int y1, m1, d1, y2, m2, d2; 1449 d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); 1450 d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); 1451 1452 dy = yearsDifference(fromDate, toDate); 1453 1454 // Calculate months and days difference 1455 1456 int miy0 = d->monthsInYear(d->addYears(y2, -1)); 1457 1458 if (miy0 == 0) 1459 { 1460 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Cannot compute date difference"; 1461 return false; 1462 } 1463 1464 if (d2 >= d1) 1465 { 1466 dm = (miy0 + m2 - m1) % miy0; 1467 dd = d2 - d1; 1468 } 1469 else 1470 { 1471 // d2 < d1 1472 // Allow for last day of month to last day of month and leap days 1473 // e.g. 2010-03-31 to 2010-04-30 is 1 month 1474 // 2000-02-29 to 2001-02-28 is 1 year 1475 // 2000-02-29 to 2001-03-01 is 1 year 1 day 1476 1477 int dim0 = daysInMonth(addMonths(toDate, -1)); 1478 int dim1 = d->daysInMonth(y1, m1); 1479 1480 if ((dim0 == 0) || (dim1 == 0)) 1481 { 1482 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Cannot compute date difference"; 1483 return false; 1484 } 1485 1486 if (d1 == dim1 && d2 == d->daysInMonth(y2, m2)) 1487 { 1488 dm = (miy0 + m2 - m1) % miy0; 1489 dd = 0; 1490 } 1491 else if (month(addMonths(toDate, -1)) == m1 && dim0 < dim1) 1492 { 1493 // Special case where fromDate = leap day and toDate in month following but non-leap year 1494 // e.g. 2000-02-29 to 2001-03-01 needs to use 29 to calculate day number not 28 1495 1496 dm = (miy0 + m2 - m1 - 1) % miy0; 1497 dd = (dim1 + d2 - d1) % dim1; 1498 } 1499 else 1500 { 1501 dm = (miy0 + m2 - m1 - 1) % miy0; 1502 dd = (dim0 + d2 - d1) % dim0; 1503 } 1504 } 1505 } 1506 } 1507 1508 if (years) 1509 { 1510 *years = dy; 1511 } 1512 1513 if (months) 1514 { 1515 *months = dm; 1516 } 1517 1518 if (days) 1519 { 1520 *days = dd; 1521 } 1522 1523 if (direction) 1524 { 1525 *direction = dir; 1526 } 1527 1528 return true; 1529 } 1530 1531 QDate CalSystem::firstDayOfYear(const QDate& dt) const 1532 { 1533 if (isValid(dt)) 1534 { 1535 return date(year(dt), 1, 1); 1536 } 1537 else 1538 { 1539 return QDate(); 1540 } 1541 } 1542 1543 QDate CalSystem::firstDayOfYear(int year) const 1544 { 1545 return date(year, 1, 1); 1546 } 1547 1548 QDate CalSystem::lastDayOfYear(const QDate& dt) const 1549 { 1550 if (isValid(dt)) 1551 { 1552 int y = year(dt); 1553 1554 return date(y, d->daysInYear(y)); 1555 } 1556 else 1557 { 1558 return QDate(); 1559 } 1560 } 1561 1562 QDate CalSystem::lastDayOfYear(int year) const 1563 { 1564 if (d->isValidYear(year)) 1565 { 1566 return date(year, d->daysInYear(year)); 1567 } 1568 else 1569 { 1570 return QDate(); 1571 } 1572 } 1573 1574 QDate CalSystem::firstDayOfMonth(const QDate& dt) const 1575 { 1576 int year, month; 1577 getDate(dt, &year, &month, nullptr); 1578 1579 return date(year, month, 1); 1580 } 1581 1582 QDate CalSystem::firstDayOfMonth(int year, int month) const 1583 { 1584 return date(year, month, 1); 1585 } 1586 1587 QDate CalSystem::lastDayOfMonth(const QDate& dt) const 1588 { 1589 int year, month; 1590 getDate(dt, &year, &month, nullptr); 1591 1592 return date(year, month, daysInMonth(year, month)); 1593 } 1594 1595 QDate CalSystem::lastDayOfMonth(int year, int month) const 1596 { 1597 return date(year, month, daysInMonth(year, month)); 1598 } 1599 1600 } // Namespace Digikam