/* This file is part of the kholidays library. SPDX-FileCopyrightText: 2014 John Layt SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qcalendarsystem_p.h" #include #include #include class QCalendarSystemPrivate : public QSharedData { public: explicit QCalendarSystemPrivate(QCalendarSystem::CalendarSystem calendar); QCalendarSystem::CalendarSystem calendarSystem() const; qint64 epoch() const; qint64 earliestValidDate() const; int earliestValidYear() const; qint64 latestValidDate() const; int latestValidYear() const; int yearOffset() const; int maxMonthsInYear() const; int monthsInYear(int year) const; int maxDaysInYear() const; int daysInYear(int year) const; int maxDaysInMonth() const; int daysInMonth(int year, int month) const; bool hasYearZero() const; bool hasLeapMonths() const; int quarter(int month) const; bool isLeapYear(int year) const; void julianDayToDate(qint64 jd, int *year, int *month, int *day) const; qint64 julianDayFromDate(int year, int month, int day) const; bool isValidYear(int year) const; bool isValidMonth(int year, int month) const; int addYears(int y1, int years) const; int diffYears(int y1, int y2) const; QCalendarSystem::CalendarSystem m_calendarSystem; }; static const char julianMonths[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; QCalendarSystemPrivate::QCalendarSystemPrivate(QCalendarSystem::CalendarSystem calendar) : QSharedData() , m_calendarSystem(calendar) { } QCalendarSystem::CalendarSystem QCalendarSystemPrivate::calendarSystem() const { if (m_calendarSystem == QCalendarSystem::DefaultCalendar) { return QCalendarSystem::GregorianCalendar; } else { return m_calendarSystem; } } qint64 QCalendarSystemPrivate::epoch() const { switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: return 1721426; // 0001-01-01 Gregorian case QCalendarSystem::CopticCalendar: return 1825030; // 0001-01-01 == 0284-08-29 Gregorian case QCalendarSystem::EthiopicCalendar: return 1724221; // 0001-01-01 == 0008-08-29 Gregorian case QCalendarSystem::EthiopicAmeteAlemCalendar: return -284655; // 0001-01-01 == -5492-08-29 Gregorian case QCalendarSystem::IndianNationalCalendar: return 1749994; // 0000-01-01 == 0078-03-22 Gregorian case QCalendarSystem::IslamicCivilCalendar: return 1948440; // 0001-01-01 == 0622-07-19 Gregorian case QCalendarSystem::ISO8601Calendar: return 1721060; // 0000-01-01 Gregorian case QCalendarSystem::JapaneseCalendar: return 1721426; // 0001-01-01 Gregorian case QCalendarSystem::JulianCalendar: return 1721424; // 0001-01-01 == Gregorian case QCalendarSystem::ROCCalendar: return 2419403; // 0001-01-01 == 1912-01-01 Gregorian case QCalendarSystem::ThaiCalendar: return 1522734; // 0000-01-01 == -0544-01-01 Gregorian default: return 0; } } qint64 QCalendarSystemPrivate::earliestValidDate() const { switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: return -31738; // -4800-01-01 Gregorian case QCalendarSystem::CopticCalendar: return 1825030; // 0001-01-01 == 0284-08-29 Gregorian case QCalendarSystem::EthiopicCalendar: return 1724221; // 0001-01-01 == 0008-08-29 Gregorian case QCalendarSystem::EthiopicAmeteAlemCalendar: return -284655; // 0001-01-01 == -5492-08-29 Gregorian case QCalendarSystem::IndianNationalCalendar: return 1749994; // 0000-01-01 == 0078-03-22 Gregorian case QCalendarSystem::IslamicCivilCalendar: return 1948440; // 0001-01-01 == 0622-07-19 Gregorian case QCalendarSystem::ISO8601Calendar: return 1721060; // 0000-01-01 Gregorian case QCalendarSystem::JapaneseCalendar: return -31738; // -4800-01-01 Gregorian case QCalendarSystem::JulianCalendar: return -31776; // -4800-01-01 Julian case QCalendarSystem::ROCCalendar: return 2419403; // 0001-01-01 == 1912-01-01 Gregorian case QCalendarSystem::ThaiCalendar: return 1522734; // 0000-01-01 == -0544-01-01 Gregorian default: return 0; } } int QCalendarSystemPrivate::earliestValidYear() const { switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: case QCalendarSystem::JapaneseCalendar: case QCalendarSystem::JulianCalendar: return -4800; case QCalendarSystem::IndianNationalCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::ThaiCalendar: return 0; default: return 1; } } qint64 QCalendarSystemPrivate::latestValidDate() const { switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: return 5373484; // 9999-12-31 Gregorian case QCalendarSystem::CopticCalendar: return 5477164; // 9999-13-05 == 10283-11-12 Gregorian case QCalendarSystem::EthiopicCalendar: return 5376721; // 9999-13-05 == 10008-11-10 Gregorian case QCalendarSystem::EthiopicAmeteAlemCalendar: return 3367114; // 9999-13-05 == 4506-09-29 Gregorian case QCalendarSystem::IndianNationalCalendar: return 5402054; // 9999-12-30 == 10078-03-21 Gregorian case QCalendarSystem::IslamicCivilCalendar: return 5491751; // 9999-12-29 == 10323-10-21 Gregorian case QCalendarSystem::ISO8601Calendar: return 5373484; // 9999-12-31 Gregorian case QCalendarSystem::JapaneseCalendar: return 5373484; // 9999-12-31 Gregorian case QCalendarSystem::JulianCalendar: return 5373557; // 9999-12-31 == 10000-03-13 Gregorian case QCalendarSystem::ROCCalendar: return 6071462; // 9999-12-31 == 11910-12-31 Gregorian case QCalendarSystem::ThaiCalendar: return 5175158; // 9999-12-31 == 9456-12-31 Gregorian default: return 0; } } int QCalendarSystemPrivate::latestValidYear() const { switch (calendarSystem()) { default: return 9999; } } int QCalendarSystemPrivate::yearOffset() const { switch (calendarSystem()) { case QCalendarSystem::ROCCalendar: return 1911; // 0001-01-01 == 1912-01-01 Gregorian case QCalendarSystem::ThaiCalendar: return -543; // 0000-01-01 == -544-01-01 Gregorian default: return 0; } } int QCalendarSystemPrivate::maxMonthsInYear() const { switch (calendarSystem()) { case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: return 13; default: return 12; } } int QCalendarSystemPrivate::monthsInYear(int year) const { // year = year + yearOffset(); Q_UNUSED(year); switch (calendarSystem()) { case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: return 13; default: return 12; } } int QCalendarSystemPrivate::maxDaysInYear() const { switch (calendarSystem()) { case QCalendarSystem::IslamicCivilCalendar: return 355; default: return 366; } } int QCalendarSystemPrivate::daysInYear(int year) const { switch (calendarSystem()) { case QCalendarSystem::IslamicCivilCalendar: return isLeapYear(year) ? 355 : 354; default: return isLeapYear(year) ? 366 : 365; } } int QCalendarSystemPrivate::maxDaysInMonth() const { switch (calendarSystem()) { case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: case QCalendarSystem::IslamicCivilCalendar: return 30; default: return 31; } } int QCalendarSystemPrivate::daysInMonth(int year, int month) const { if (month < 1 || month > monthsInYear(year)) { return 0; } switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::JapaneseCalendar: case QCalendarSystem::ROCCalendar: case QCalendarSystem::ThaiCalendar: case QCalendarSystem::JulianCalendar: { if (month == 2 && isLeapYear(year)) { return 29; } else { return julianMonths[month]; } } case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: { if (month == 13) { return isLeapYear(year) ? 6 : 5; } else { return 30; } } case QCalendarSystem::IndianNationalCalendar: { if (month >= 7) { return 30; } else if (month >= 2) { return 31; } else if (isLeapYear(year)) { return 31; } else { return 30; } } case QCalendarSystem::IslamicCivilCalendar: { if (month == 12 && isLeapYear(year)) { return 30; } else if (month % 2 == 0) { return 29; } else { return 30; } } default: return 0; } } bool QCalendarSystemPrivate::hasYearZero() const { switch (calendarSystem()) { case QCalendarSystem::IndianNationalCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::ThaiCalendar: return true; default: return false; } } bool QCalendarSystemPrivate::hasLeapMonths() const { switch (calendarSystem()) { default: return false; } } int QCalendarSystemPrivate::quarter(int month) const { switch (calendarSystem()) { case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: if (month == 13) { // Consider the short epagomenal month as part of the 4th quarter return 4; } Q_FALLTHROUGH(); default: return (((month - 1) / 3) + 1); } } bool QCalendarSystemPrivate::isLeapYear(int year) const { year = year + yearOffset(); // Uses same rule as Gregorian and in same years as Gregorian to keep in sync. // Can't use yearOffset() as this offset only applies for isLeapYear(). if (calendarSystem() == QCalendarSystem::IndianNationalCalendar) { year = year + 78; } if (year < 1 && !hasYearZero()) { ++year; } switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: case QCalendarSystem::IndianNationalCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::JapaneseCalendar: case QCalendarSystem::ROCCalendar: case QCalendarSystem::ThaiCalendar: return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: return (year % 4 == 3); case QCalendarSystem::JulianCalendar: return (year % 4 == 0); case QCalendarSystem::IslamicCivilCalendar: return ((((11 * year) + 14) % 30) < 11); default: return false; } } void QCalendarSystemPrivate::julianDayToDate(qint64 jd, int *year, int *month, int *day) const { int yy = 0; int mm = 0; int dd = 0; switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::JapaneseCalendar: case QCalendarSystem::ROCCalendar: case QCalendarSystem::ThaiCalendar: { // Formula from The Calendar FAQ by Claus Tondering // https://www.tondering.dk/claus/calendar.html qint64 a = jd + 32044; qint64 b = ((4 * a) + 3) / 146097; qint64 c = a - ((146097 * b) / 4); qint64 d = ((4 * c) + 3) / 1461; qint64 e = c - ((1461 * d) / 4); qint64 m = ((5 * e) + 2) / 153; dd = e - (((153 * m) + 2) / 5) + 1; mm = m + 3 - (12 * (m / 10)); yy = (100 * b) + d - 4800 + (m / 10); break; } case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: { // Formula derived from first principles by John Layt qint64 s = jd - (epoch() - 365); qint64 l = s / 1461; yy = (l * 4) + qMin(static_cast(3), (s % 1461) / 365); qint64 diy = s - (yy * 365) + (yy / 4); mm = (diy / 30) + 1; dd = (diy % 30) + 1; break; } case QCalendarSystem::IndianNationalCalendar: { // Formula from the "Explanatory Supplement to the Astronomical Almanac" // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government qint64 l = jd + 68518; qint64 n = (4 * l) / 146097; l = l - (146097 * n + 3) / 4; qint64 i = (4000 * (l + 1)) / 1461001; l = l - (1461 * i) / 4 + 1; qint64 j = ((l - 1) / 31) * (1 - l / 185) + (l / 185) * ((l - 156) / 30 + 5) - l / 366; dd = l - 31 * j + ((j + 2) / 8) * (j - 5); l = j / 11; mm = j + 2 - 12 * l; yy = 100 * (n - 49) + l + i - 78; break; } case QCalendarSystem::IslamicCivilCalendar: { // Formula from the "Explanatory Supplement to the Astronomical Almanac" // Revised Edition 2006 section ??? pp ???, US Naval Observatory // Derived from Fliegel & Van Flandern 1968 qint64 l = jd - epoch() + 10632; qint64 n = (l - 1) / 10631; l = l - 10631 * n + 354; int j = ((10985 - l) / 5316) * ((50 * l) / 17719) + (l / 5670) * ((43 * l) / 15238); l = l - ((30 - j) / 15) * ((17719 * j) / 50) - (j / 16) * ((15238 * j) / 43) + 29; yy = (30 * n) + j - 30; mm = (24 * l) / 709; dd = l - ((709 * mm) / 24); break; } case QCalendarSystem::JulianCalendar: { // Formula from The Calendar FAQ by Claus Tondering // https://www.tondering.dk/claus/calendar.html qint64 b = 0; qint64 c = jd + 32082; qint64 d = ((4 * c) + 3) / 1461; qint64 e = c - ((1461 * d) / 4); qint64 m = ((5 * e) + 2) / 153; dd = e - (((153 * m) + 2) / 5) + 1; mm = m + 3 - (12 * (m / 10)); yy = (100 * b) + d - 4800 + (m / 10); break; } default: break; } if (!hasYearZero() && yy < 1) { yy -= 1; } yy = yy - yearOffset(); if (year) { *year = yy; } if (month) { *month = mm; } if (day) { *day = dd; } } qint64 QCalendarSystemPrivate::julianDayFromDate(int year, int month, int day) const { qint64 jd = 0; year = year + yearOffset(); if (year < 1 && !hasYearZero()) { year = year + 1; } switch (calendarSystem()) { case QCalendarSystem::GregorianCalendar: case QCalendarSystem::ISO8601Calendar: case QCalendarSystem::JapaneseCalendar: case QCalendarSystem::ROCCalendar: case QCalendarSystem::ThaiCalendar: { // Formula from The Calendar FAQ by Claus Tondering // https://www.tondering.dk/claus/calendar.html int a = (14 - month) / 12; year = year + 4800 - a; int m = month + (12 * a) - 3; jd = day + (((153 * m) + 2) / 5) // + (365 * year) // + (year / 4) // - (year / 100) // + (year / 400) // - 32045; break; } case QCalendarSystem::CopticCalendar: case QCalendarSystem::EthiopicCalendar: case QCalendarSystem::EthiopicAmeteAlemCalendar: { // Formula derived from first principles by John Layt jd = epoch() - 1 // + ((year - 1) * 365) // + (year / 4) // + ((month - 1) * 30) // + day; break; } case QCalendarSystem::IndianNationalCalendar: { // Formula from the "Explanatory Supplement to the Astronomical Almanac" // Revised Edition 2006 section 12.94 pp 605-606, US Naval Observatory // Originally from the "Report of the Calendar Reform Committee" 1955, Indian Government jd = 365 * year + (year + 78 - 1 / month) / 4 // + 31 * month // - (month + 9) / 11 // - (month / 7) * (month - 7) // - (3 * ((year + 78 - 1 / month) / 100 + 1)) / 4 // + day // + 1749579; break; } case QCalendarSystem::IslamicCivilCalendar: { // Formula from the "Explanatory Supplement to the Astronomical Almanac" // Revised Edition 2006 section ??? pp ???, US Naval Observatory // Derived from Fliegel & Van Flandern 1968 jd = (3 + (11 * year)) / 30 // + 354 * year // + 30 * month // - (month - 1) / 2 // + day // + epoch() // - 385; break; } case QCalendarSystem::JulianCalendar: { // Formula from The Calendar FAQ by Claus Tondering // https://www.tondering.dk/claus/calendar.html int a = (14 - month) / 12; year = year + 4800 - a; int m = month + (12 * a) - 3; jd = day // + (((153 * m) + 2) / 5) // + (365 * year) // + (year / 4) // - 32083; break; } default: break; } return jd; } // Some private utility rules bool QCalendarSystemPrivate::isValidYear(int year) const { return year >= earliestValidYear() // && year <= latestValidYear() // && (year == 0 ? hasYearZero() : true); } bool QCalendarSystemPrivate::isValidMonth(int year, int month) const { return isValidYear(year) && month >= 1 && month <= monthsInYear(year); } int QCalendarSystemPrivate::addYears(int y1, int years) const { int y2 = y1 + years; if (!hasYearZero()) { if (y1 > 0 && y2 <= 0) { --y2; } else if (y1 < 0 && y2 >= 0) { ++y2; } } return y2; } int QCalendarSystemPrivate::diffYears(int y1, int y2) const { int dy = y2 - y1; if (!hasYearZero()) { if (y2 > 0 && y1 < 0) { dy -= 1; } else if (y2 < 0 && y1 > 0) { dy += 1; } } return dy; } // QCalendarSystem public api QCalendarSystem::QCalendarSystem(QCalendarSystem::CalendarSystem calendar) : d(new QCalendarSystemPrivate(calendar)) { } QCalendarSystem::~QCalendarSystem() { } QCalendarSystem &QCalendarSystem::operator=(const QCalendarSystem &other) { d = other.d; return *this; } QCalendarSystem::CalendarSystem QCalendarSystem::calendarSystem() const { return d->calendarSystem(); } QDate QCalendarSystem::epoch() const { return QDate::fromJulianDay(d->epoch()); } QDate QCalendarSystem::earliestValidDate() const { return QDate::fromJulianDay(d->earliestValidDate()); } QDate QCalendarSystem::latestValidDate() const { return QDate::fromJulianDay(d->latestValidDate()); } int QCalendarSystem::maximumMonthsInYear() const { return d->maxMonthsInYear(); } int QCalendarSystem::maximumDaysInYear() const { return d->maxDaysInYear(); } int QCalendarSystem::maximumDaysInMonth() const { return d->maxDaysInMonth(); } bool QCalendarSystem::isValid(const QDate &date) const { return date.isValid() && date >= earliestValidDate() && date <= latestValidDate(); } bool QCalendarSystem::isValid(int year, int month, int day) const { return d->isValidMonth(year, month) && day >= 1 && day <= d->daysInMonth(year, month); } bool QCalendarSystem::isValid(int year, int dayOfYear) const { return d->isValidYear(year) && dayOfYear > 0 && dayOfYear <= d->daysInYear(year); } QDate QCalendarSystem::date(int year, int month, int day) const { if (isValid(year, month, day)) { return QDate::fromJulianDay(d->julianDayFromDate(year, month, day)); } else { return QDate(); } } QDate QCalendarSystem::date(int year, int dayOfYear) const { if (isValid(year, dayOfYear)) { return QDate::fromJulianDay(d->julianDayFromDate(year, 1, 1) + dayOfYear - 1); } else { return QDate(); } } void QCalendarSystem::getDate(const QDate &date, int *year, int *month, int *day) const { int yy = 0; int mm = 0; int dd = 0; if (isValid(date)) { d->julianDayToDate(date.toJulianDay(), &yy, &mm, &dd); } if (year) { *year = yy; } if (month) { *month = mm; } if (day) { *day = dd; } } int QCalendarSystem::year(const QDate &date) const { int y = 0; if (isValid(date)) { d->julianDayToDate(date.toJulianDay(), &y, nullptr, nullptr); } return y; } int QCalendarSystem::month(const QDate &date) const { int m = 0; if (isValid(date)) { d->julianDayToDate(date.toJulianDay(), nullptr, &m, nullptr); } return m; } int QCalendarSystem::day(const QDate &date) const { int dd = 0; if (isValid(date)) { d->julianDayToDate(date.toJulianDay(), nullptr, nullptr, &dd); } return dd; } int QCalendarSystem::quarter(const QDate &date) const { if (isValid(date)) { int month; d->julianDayToDate(date.toJulianDay(), nullptr, &month, nullptr); return d->quarter(month); } else { return 0; } } int QCalendarSystem::quarter(int year, int month, int day) const { if (isValid(year, month, day)) { return d->quarter(month); } else { return 0; } } int QCalendarSystem::dayOfYear(const QDate &date) const { if (isValid(date)) { return date.toJulianDay() - firstDayOfYear(date).toJulianDay() + 1; } else { return 0; } } int QCalendarSystem::dayOfYear(int year, int month, int day) const { return dayOfYear(date(year, month, day)); } int QCalendarSystem::dayOfWeek(const QDate &date) const { // jd 0 = Monday = weekday 1. We've never skipped weekdays. if (isValid(date)) { if (date.toJulianDay() >= 0) { return (date.toJulianDay() % daysInWeek()) + 1; } else { return ((date.toJulianDay() + 1) % daysInWeek()) + daysInWeek(); } } else { return 0; } } int QCalendarSystem::dayOfWeek(int year, int month, int day) const { return dayOfWeek(date(year, month, day)); } // TODO These are ISO weeks, may need to localise int QCalendarSystem::weekNumber(const QDate &date, int *yearNum) const { if (isValid(date)) { int year; int month; int day; d->julianDayToDate(date.toJulianDay(), &year, &month, &day); return weekNumber(year, month, day, yearNum); } else { return 0; } } /* The following method is based on code from tzcode's strftime.c, which is turn said to be "Based on the UCB version with the ID appearing below." SPDX-FileCopyrightText: 1989 The Regents of the University of California. All rights reserved. SPDX-License-Identifier: BSD-3-Clause */ // TODO These are ISO weeks, may need to localise // TODO Replace with cleanly licensed routine int QCalendarSystem::weekNumber(int year, int month, int day, int *yearNum) const { if (!isValid(year, month, day)) { if (yearNum) { *yearNum = 0; } return 0; } int yday = dayOfYear(year, month, day) - 1; int wday = dayOfWeek(year, month, day); if (wday == 7) { wday = 0; } int w; for (;;) { int len; int bot; int top; len = d->daysInYear(year); /* ** What yday (-3 ... 3) does ** the ISO year begin on? */ bot = ((yday + 11 - wday) % 7) - 3; /* ** What yday does the NEXT ** ISO year begin on? */ top = bot - (len % 7); if (top < -3) { top += 7; } top += len; if (yday >= top) { ++year; w = 1; break; } if (yday >= bot) { w = 1 + ((yday - bot) / 7); break; } --year; yday += d->daysInYear(year); } if (yearNum) { *yearNum = year; } return w; } int QCalendarSystem::monthsInYear(const QDate &date) const { if (isValid(date)) { return d->monthsInYear(year(date)); } else { return 0; } } int QCalendarSystem::monthsInYear(int year) const { if (d->isValidYear(year)) { return d->monthsInYear(year); } else { return 0; } } int QCalendarSystem::weeksInYear(const QDate &date) const { if (isValid(date)) { return weeksInYear(year(date)); } else { return 0; } } // TODO This is ISO weeks, may need to localise int QCalendarSystem::weeksInYear(int year) const { if (d->isValidYear(year)) { int weekYear = year; int lastWeek = weekNumber(lastDayOfYear(year), &weekYear); if (lastWeek < 1 || weekYear != year) { lastWeek = weekNumber(addDays(lastDayOfYear(year), -7), &weekYear); } return lastWeek; } else { return 0; } } int QCalendarSystem::daysInYear(const QDate &date) const { if (isValid(date)) { return d->daysInYear(year(date)); } else { return 0; } } int QCalendarSystem::daysInYear(int year) const { if (d->isValidYear(year)) { return d->daysInYear(year); } else { return 0; } } int QCalendarSystem::daysInMonth(const QDate &date) const { if (isValid(date)) { int year; int month; d->julianDayToDate(date.toJulianDay(), &year, &month, nullptr); return d->daysInMonth(year, month); } else { return 0; } } int QCalendarSystem::daysInMonth(int year, int month) const { if (d->isValidMonth(year, month)) { return d->daysInMonth(year, month); } else { return 0; } } int QCalendarSystem::daysInWeek() const { return 7; } bool QCalendarSystem::isLeapYear(const QDate &date) const { if (isValid(date)) { return d->isLeapYear(year(date)); } else { return false; } } bool QCalendarSystem::isLeapYear(int year) const { if (d->isValidYear(year)) { return d->isLeapYear(year); } else { return false; } } QDate QCalendarSystem::addYears(const QDate &dt, int years) const { if (isValid(dt)) { int year; int month; int day; d->julianDayToDate(dt.toJulianDay(), &year, &month, &day); year = d->addYears(year, years); month = qMin(month, d->monthsInYear(year)); return date(year, month, qMin(day, d->daysInMonth(year, month))); } else { return QDate(); } } QDate QCalendarSystem::addMonths(const QDate &dt, int months) const { if (isValid(dt)) { int year; int month; int day; d->julianDayToDate(dt.toJulianDay(), &year, &month, &day); while (months != 0) { if (months < 0) { if (month + months >= 1) { month += months; months = 0; } else { year = d->addYears(year, -1); months += d->monthsInYear(year); } } else { int miy = d->monthsInYear(year); if (month + months <= miy) { month += months; months = 0; } else { year = d->addYears(year, 1); months -= miy; } } } return date(year, month, qMin(day, d->daysInMonth(year, month))); } else { return QDate(); } } QDate QCalendarSystem::addDays(const QDate &date, int days) const { return date.addDays(days); } // Caters for Leap Months, but possibly not for Hebrew int QCalendarSystem::yearsDifference(const QDate &fromDate, const QDate &toDate) const { if (!isValid(fromDate) || !isValid(toDate) || toDate == fromDate) { return 0; } if (toDate < fromDate) { return -yearsDifference(toDate, fromDate); } int y1; int m1; int d1; int y2; int m2; int d2; d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); if (y2 == y1) { return 0; } if (m2 > m1) { return d->diffYears(y1, y2); } if (m2 < m1) { return d->diffYears(y1, y2) - 1; } // m2 == m1 // Allow for last day of month to last day of month and leap days // e.g. 2000-02-29 to 2001-02-28 is 1 year not 0 years if (d2 >= d1 // || (d1 == d->daysInMonth(y1, m1) && d2 == d->daysInMonth(y2, m2))) { return d->diffYears(y1, y2); } else { return d->diffYears(y1, y2) - 1; } } // Caters for Leap Months, but possibly not for Hebrew int QCalendarSystem::monthsDifference(const QDate &fromDate, const QDate &toDate) const { if (!isValid(fromDate) || !isValid(toDate) || toDate == fromDate) { return 0; } if (toDate < fromDate) { return -monthsDifference(toDate, fromDate); } int y1; int m1; int d1; int y2; int m2; int d2; int my; d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); // Calculate the number of months in full years preceding y2. if (y2 == y1) { my = 0; } else if (d->hasLeapMonths()) { my = 0; for (int y = y1; y < y2; y = d->addYears(y, 1)) { my = my + monthsInYear(y); } } else { my = d->diffYears(y1, y2) * monthsInYear(y2); } // Allow for last day of month to last day of month and leap days // e.g. 2010-03-31 to 2010-04-30 is 1 month not 0 months // also 2000-02-29 to 2001-02-28 is 12 months not 11 months if (d2 >= d1 // || (d1 == d->daysInMonth(y1, m1) && d2 == d->daysInMonth(y2, m2))) { return my + m2 - m1; } else { return my + m2 - m1 - 1; } } qint64 QCalendarSystem::daysDifference(const QDate &fromDate, const QDate &toDate) const { if (isValid(fromDate) && isValid(toDate)) { return toDate.toJulianDay() - fromDate.toJulianDay(); } else { return 0; } } // Caters for Leap Months, but possibly not for Hebrew void QCalendarSystem::dateDifference(const QDate &fromDate, const QDate &toDate, int *years, int *months, int *days, int *direction) const { int dy = 0; int dm = 0; int dd = 0; int dir = 1; if (isValid(fromDate) && isValid(toDate) && fromDate != toDate) { if (toDate < fromDate) { dateDifference(toDate, fromDate, &dy, &dm, &dd, nullptr); dir = -1; } else { int y1; int m1; int d1; int y2; int m2; int d2; d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1); d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2); dy = yearsDifference(fromDate, toDate); // Calculate months and days difference int miy0 = d->monthsInYear(d->addYears(y2, -1)); if (d2 >= d1) { dm = (miy0 + m2 - m1) % miy0; dd = d2 - d1; } else { // d2 < d1 // Allow for last day of month to last day of month and leap days // e.g. 2010-03-31 to 2010-04-30 is 1 month // 2000-02-29 to 2001-02-28 is 1 year // 2000-02-29 to 2001-03-01 is 1 year 1 day int dim0 = daysInMonth(addMonths(toDate, -1)); int dim1 = d->daysInMonth(y1, m1); if (d1 == dim1 && d2 == d->daysInMonth(y2, m2)) { dm = (miy0 + m2 - m1) % miy0; dd = 0; } else if (month(addMonths(toDate, -1)) == m1 && dim0 < dim1) { // Special case where fromDate = leap day and toDate in month following but non-leap year // e.g. 2000-02-29 to 2001-03-01 needs to use 29 to calculate day number not 28 dm = (miy0 + m2 - m1 - 1) % miy0; dd = (dim1 + d2 - d1) % dim1; } else { dm = (miy0 + m2 - m1 - 1) % miy0; dd = (dim0 + d2 - d1) % dim0; } } } } if (years) { *years = dy; } if (months) { *months = dm; } if (days) { *days = dd; } if (direction) { *direction = dir; } } QDate QCalendarSystem::firstDayOfYear(const QDate &dt) const { if (isValid(dt)) { return date(year(dt), 1, 1); } else { return QDate(); } } QDate QCalendarSystem::firstDayOfYear(int year) const { return date(year, 1, 1); } QDate QCalendarSystem::lastDayOfYear(const QDate &dt) const { if (isValid(dt)) { int y = year(dt); return date(y, d->daysInYear(y)); } else { return QDate(); } } QDate QCalendarSystem::lastDayOfYear(int year) const { if (d->isValidYear(year)) { return date(year, d->daysInYear(year)); } else { return QDate(); } } QDate QCalendarSystem::firstDayOfMonth(const QDate &dt) const { int year; int month; getDate(dt, &year, &month, nullptr); return date(year, month, 1); } QDate QCalendarSystem::firstDayOfMonth(int year, int month) const { return date(year, month, 1); } QDate QCalendarSystem::lastDayOfMonth(const QDate &dt) const { int year; int month; getDate(dt, &year, &month, nullptr); return date(year, month, daysInMonth(year, month)); } QDate QCalendarSystem::lastDayOfMonth(int year, int month) const { return date(year, month, daysInMonth(year, month)); }