[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kholidays] src: KF5: Add QCalendarSystem class
From:       John Layt <jlayt () kde ! org>
Date:       2015-09-02 18:07:27
Message-ID: E1ZXCRT-0003LI-4E () scm ! kde ! org
[Download RAW message or body]

Git commit fe5b5d1ca46e39f5ed1d13e050e97089e1b405ee by John Layt.
Committed on 02/09/2015 at 17:53.
Pushed by jlayt into branch 'master'.

KF5: Add QCalendarSystem class

Add a temp copy of QCalendarSystem to allow removal of kdelibs4support
dependency. This will be replaced once merged into Qt.

M  +2    -1    src/CMakeLists.txt
A  +1228 -0    src/parsers/qcalendarsystem.cpp     [License: LGPL (v2+)]
A  +135  -0    src/parsers/qcalendarsystem_p.h     [License: LGPL (v2+)]

http://commits.kde.org/kholidays/fe5b5d1ca46e39f5ed1d13e050e97089e1b405ee

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a86afd4..2754acb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,8 +14,9 @@ set(kholidays_SRCS
   lunarphase.cpp
   sunriseset.cpp
   zodiac.cpp
-  parsers/plan2/holidayscannerplan.cpp
   parsers/holidayparserdriver.cpp
+  parsers/qcalendarsystem.cpp
+  parsers/plan2/holidayscannerplan.cpp
   parsers/plan2/holidayparserdriverplan.cpp
   parsers/plan2/holidayparserplan.cpp
 )
diff --git a/src/parsers/qcalendarsystem.cpp b/src/parsers/qcalendarsystem.cpp
new file mode 100644
index 0000000..82169b1
--- /dev/null
+++ b/src/parsers/qcalendarsystem.cpp
@@ -0,0 +1,1228 @@
+/*
+  This file is part of the kholidays library.
+
+  Copyright 2014 John Layt <john@layt.net>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "qcalendarsystem_p.h"
+
+#include <QtCore/QDate>
+#include <QtCore/QSharedData>
+
+#include "qdebug.h"
+
+class QCalendarSystemPrivate : public QSharedData
+{
+public:
+    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();
+
+    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;
+    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, mm, dd;
+
+    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
+        // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
+        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((qint64)3, (s % 1461) / 365);
+        qint64 diy = s -  (yy * 365) + (yy / 4);
+        mm = (diy / 30) + 1;
+        dd = (diy % 30) + 1;
+    }
+
+    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;
+    }
+
+    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);
+    }
+
+    case QCalendarSystem::JulianCalendar:
+    {
+        // Formula from The Calendar FAQ by Claus Tondering
+        // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
+        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
+        // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
+        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;
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    case QCalendarSystem::JulianCalendar:
+    {
+        // Formula from The Calendar FAQ by Claus Tondering
+        // http://www.tondering.dk/claus/cal/node3.html#SECTION003161000000000000000
+        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, 0, 0);
+
+    return y;
+}
+
+int QCalendarSystem::month(const QDate &date) const
+{
+    int m = 0;
+
+    if (isValid(date))
+        d->julianDayToDate(date.toJulianDay(), 0, &m, 0);
+
+    return m;
+}
+
+int QCalendarSystem::day(const QDate &date) const
+{
+    int dd = 0;
+
+    if (isValid(date))
+        d->julianDayToDate(date.toJulianDay(), 0, 0, &dd);
+
+    return dd;
+}
+
+int QCalendarSystem::quarter(const QDate &date) const
+{
+    if (isValid(date)) {
+        int month;
+        d->julianDayToDate(date.toJulianDay(), 0, &month, 0);
+        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, month, day;
+        d->julianDayToDate(date.toJulianDay(), &year, &month, &day);
+        return weekNumber(year, month, day, yearNum);
+    } else {
+        return 0;
+    }
+}
+
+/*
+    \legalese
+    Copyright (c) 1989 The Regents of the University of California.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms are permitted
+    provided that the above copyright notice and this paragraph are
+    duplicated in all such forms and that any documentation,
+    advertising materials, and other materials related to such
+    distribution and use acknowledge that the software was developed
+    by the University of California, Berkeley.  The name of the
+    University may not be used to endorse or promote products derived
+    from this software without specific prior written permission.
+    THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+//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, bot, 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, month;
+        d->julianDayToDate(date.toJulianDay(), &year, &month, 0);
+        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, month, 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, month, day;
+        d->julianDayToDate(dt.toJulianDay(), &year, &month, &day);
+        while (months != 0) {
+            if (months < 0) {
+                if (month + months >= 1) {
+                    month += months;
+                    months = 0;
+                } else if (months < 0) {
+                    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, m1, d1, y2, m2, 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, m1, d1, y2, m2, d2, my;
+    d->julianDayToDate(fromDate.toJulianDay(), &y1, &m1, &d1);
+    d->julianDayToDate(toDate.toJulianDay(), &y2, &m2, &d2);
+
+    // Calculate 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, 0);
+            dir = -1;
+        } else {
+            int y1, m1, d1, y2, m2, 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, month;
+    getDate(dt, &year, &month, 0);
+    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, month;
+    getDate(dt, &year, &month, 0);
+    return date(year, month, daysInMonth(year, month));
+}
+
+QDate QCalendarSystem::lastDayOfMonth(int year, int month) const
+{
+    return date(year, month, daysInMonth(year, month));
+}
diff --git a/src/parsers/qcalendarsystem_p.h b/src/parsers/qcalendarsystem_p.h
new file mode 100644
index 0000000..165f819
--- /dev/null
+++ b/src/parsers/qcalendarsystem_p.h
@@ -0,0 +1,135 @@
+/*
+  This file is part of the kholidays library.
+
+  Copyright 2014 John Layt <john@layt.net>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+
+#ifndef QCALENDARSYSTEM_H
+#define QCALENDARSYSTEM_H
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QDate>
+
+class QCalendarSystemPrivate;
+
+class QCalendarSystem
+{
+public:
+    enum CalendarSystem {
+        DefaultCalendar = 0,
+        GregorianCalendar = 1,
+        ChineseCalendar = 2,
+        CopticCalendar = 3,
+        EthiopicCalendar = 4,
+        EthiopicAmeteAlemCalendar = 5,
+        HebrewCalendar = 6,
+        IndianNationalCalendar = 7,
+        IslamicCalendar = 8,
+        IslamicCivilCalendar = 9,
+        ISO8601Calendar = 10,
+        JapaneseCalendar = 11,
+        JulianCalendar = 12,
+        PersianCalendar = 13,
+        ROCCalendar = 14,
+        ThaiCalendar = 15,
+        LastCalendar = ThaiCalendar
+    };
+
+    explicit QCalendarSystem(QCalendarSystem::CalendarSystem calendar = \
QCalendarSystem::DefaultCalendar); +    ~QCalendarSystem();
+
+    QCalendarSystem &operator=(const QCalendarSystem &other);
+
+    QCalendarSystem::CalendarSystem calendarSystem() const;
+
+    QDate epoch() const;
+    QDate earliestValidDate() const;
+    QDate latestValidDate() const;
+    int maximumMonthsInYear() const;
+    int maximumDaysInYear() const;
+    int maximumDaysInMonth() const;
+
+    bool isValid(const QDate &date) const;
+    bool isValid(int year, int month, int day) const;
+    bool isValid(int year, int dayOfYear) const;
+
+    QDate date(int year, int month, int day) const;
+    QDate date(int year, int dayOfYear) const;
+
+    void getDate(const QDate &date, int *year, int *month, int *day) const;
+
+    int year(const QDate &date) const;
+    int month(const QDate &date) const;
+    int day(const QDate &date) const;
+
+    int quarter(const QDate &date) const;
+    int quarter(int year, int month, int day) const;
+
+    int dayOfYear(const QDate &date) const;
+    int dayOfYear(int year, int month, int day) const;
+
+    int dayOfWeek(const QDate &date) const;
+    int dayOfWeek(int year, int month, int day) const;
+
+    int weekNumber(const QDate &date, int *yearNum = 0) const;
+    int weekNumber(int year, int month, int day, int *yearNum = 0) const;
+
+    int monthsInYear(const QDate &date) const;
+    int monthsInYear(int year) const;
+
+    int weeksInYear(const QDate &date) const;
+    int weeksInYear(int year) const;
+
+    int daysInYear(const QDate &date) const;
+    int daysInYear(int year) const;
+
+    int daysInMonth(const QDate &date) const;
+    int daysInMonth(int year, int month) const;
+
+    int daysInWeek() const;
+
+    bool isLeapYear(const QDate &date) const;
+    bool isLeapYear(int year) const;
+
+    QDate addYears(const QDate &date, int years) const;
+    QDate addMonths(const QDate &date, int months) const;
+    QDate addDays(const QDate &date, int days) const;
+
+    int yearsDifference(const QDate &fromDate, const QDate &toDate) const;
+    int monthsDifference(const QDate &fromDate, const QDate &toDate) const;
+    qint64 daysDifference(const QDate &fromDate, const QDate &toDate) const;
+
+    void dateDifference(const QDate &fromDate, const QDate &toDate,
+                        int *years, int *months, int *days, int *direction) const;
+
+    QDate firstDayOfYear(const QDate &date) const;
+    QDate firstDayOfYear(int year) const;
+    QDate lastDayOfYear(const QDate &date) const;
+    QDate lastDayOfYear(int year) const;
+
+    QDate firstDayOfMonth(const QDate &date) const;
+    QDate firstDayOfMonth(int year, int month) const;
+    QDate lastDayOfMonth(const QDate &date) const;
+    QDate lastDayOfMonth(int year, int month) const;
+
+private:
+    QSharedDataPointer<QCalendarSystemPrivate> d;
+};
+
+#endif // QCALENDARSYSTEM_H


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic