File indexing completed on 2024-03-24 15:27:13

0001 /*
0002     Copyright 2009, 2010 John Layt <john@layt.net>
0003 
0004     This library is free software; you can redistribute it and/or
0005     modify it under the terms of the GNU Library General Public
0006     License as published by the Free Software Foundation; either
0007     version 2 of the License, or (at your option) any later version.
0008 
0009     This library is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012     Library General Public License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to
0016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017     Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kcalendarsystemcoptic_p.h"
0021 #include "kcalendarsystemprivate_p.h"
0022 #include "kcalendarsystemcopticprivate_p.h"
0023 
0024 #include "klocale.h"
0025 #include "klocalizedstring.h"
0026 
0027 #include <QDate>
0028 #include <QCharRef>
0029 
0030 // Shared d pointer implementations
0031 
0032 KCalendarSystemCopticPrivate::KCalendarSystemCopticPrivate(KCalendarSystemCoptic *q)
0033     : KCalendarSystemPrivate(q)
0034 {
0035 }
0036 
0037 KCalendarSystemCopticPrivate::~KCalendarSystemCopticPrivate()
0038 {
0039 }
0040 
0041 void KCalendarSystemCopticPrivate::loadDefaultEraList()
0042 {
0043     QString name, shortName, format;
0044     // AM for Anno Martyrum or "Year of the Martyrs"
0045     name = i18nc("Calendar Era: Coptic Era of Martyrs, years > 0, LongFormat", "Anno Martyrum");
0046     shortName = i18nc("Calendar Era: Coptic Era of Martyrs, years > 0, ShortFormat", "AM");
0047     format = i18nc("(kdedt-format) Coptic, AM, full era year format used for %EY, e.g. 2000 AM", "%Ey %EC");
0048     addEra('+', 1, q->epoch(), 1, q->latestValidDate(), name, shortName, format);
0049 }
0050 
0051 int KCalendarSystemCopticPrivate::monthsInYear(int year) const
0052 {
0053     Q_UNUSED(year)
0054     return 13;
0055 }
0056 
0057 int KCalendarSystemCopticPrivate::daysInMonth(int year, int month) const
0058 {
0059     if (month == 13) {
0060         if (isLeapYear(year)) {
0061             return 6;
0062         } else {
0063             return 5;
0064         }
0065     }
0066 
0067     return 30;
0068 }
0069 
0070 int KCalendarSystemCopticPrivate::daysInYear(int year) const
0071 {
0072     if (isLeapYear(year)) {
0073         return 366;
0074     } else {
0075         return 365;
0076     }
0077 }
0078 
0079 bool KCalendarSystemCopticPrivate::isLeapYear(int year) const
0080 {
0081     //Uses same rule as Julian but offset by 1 year with year 3 being first leap year
0082     if (year < 1) {
0083         year = year + 2;
0084     } else {
0085         year = year + 1;
0086     }
0087 
0088     if (year % 4 == 0) {
0089         return true;
0090     }
0091     return false;
0092 }
0093 
0094 bool KCalendarSystemCopticPrivate::hasLeapMonths() const
0095 {
0096     return false;
0097 }
0098 
0099 bool KCalendarSystemCopticPrivate::hasYearZero() const
0100 {
0101     return false;
0102 }
0103 
0104 int KCalendarSystemCopticPrivate::maxMonthsInYear() const
0105 {
0106     return 13;
0107 }
0108 
0109 int KCalendarSystemCopticPrivate::earliestValidYear() const
0110 {
0111     return 1;
0112 }
0113 
0114 int KCalendarSystemCopticPrivate::latestValidYear() const
0115 {
0116     return 9999;
0117 }
0118 
0119 // Names taken from Bohairic dialect transliterations in http://www.copticheritage.org/parameters/copticheritage/calendar/The_Coptic_Calendar.pdf
0120 // These differ from the transliterations found on Wikipedia http://en.wikipedia.org/wiki/Coptic_calendar
0121 // These differ from the Sahidic dialect transliterations used in Dershowitz & Reingold which went out of use in the 11th centuary
0122 // These differ from the Arabic transliterations found on Wikipedia
0123 // These differ from the transliterations used in Mac OSX 10.6 Snow Leopard
0124 // The Boharic was initially chosen as this is the dialect apparantly in 'common' use in the Coptic Church.
0125 // But it could be argued the Arabic forms should be used as they are in 'common' usage in Eqypt
0126 // And where did the rest come from?
0127 //
0128 //    Boharic         Wikipedia Copt   D&R Sahidic     Wikipedia Arab  Mac OSX
0129 //    --------------  ---------------  --------------  --------------  --------------
0130 //  * Thoout          Thout            Thoout          Tout            Tout
0131 //  * Paope           Paopi            Paope           Baba            Baba
0132 //  * Hathor          Hathor           Athōr           Hatour          Hatour
0133 //  * Kiahk           Koiak            Koiak           Kiahk           Kiahk
0134 //  * Tobe            Tobi             Tōbe            Touba           Toba
0135 //  * Meshir          Meshir           Meshir          Amshir          Amshir
0136 //  * Paremhotep      Paremhat         Paremotep       Baramhat        Baramhat
0137 //  * Parmoute        Paremoude        Paremoute       Baramouda       Baramouda
0138 //  * Pashons         Pashons          Pashons         Bashans         Bashans
0139 //  * Paone           Paoni            Paōne           Ba'ouna         Paona
0140 //  * Epep            Epip             Epēp            Abib            Epep
0141 //  * Mesore          Mesori           Mesorē          Mesra           Mesra
0142 //  * Kouji nabot     Pi Kogi Enavot   Epagomenē                       Nasie
0143 //  *
0144 QString KCalendarSystemCopticPrivate::monthName(int month, int year, KLocale::DateTimeComponentFormat format, bool possessive) const
0145 {
0146     Q_UNUSED(year);
0147 
0148     QStringList languages = locale()->languageList();
0149 
0150     if (format == KLocale::NarrowName) {
0151         switch (month) {
0152         case 1:
0153             return ki18nc("Coptic month 1 - KLocale::NarrowName",  "T").toString(languages);
0154         case 2:
0155             return ki18nc("Coptic month 2 - KLocale::NarrowName",  "P").toString(languages);
0156         case 3:
0157             return ki18nc("Coptic month 3 - KLocale::NarrowName",  "H").toString(languages);
0158         case 4:
0159             return ki18nc("Coptic month 4 - KLocale::NarrowName",  "K").toString(languages);
0160         case 5:
0161             return ki18nc("Coptic month 5 - KLocale::NarrowName",  "T").toString(languages);
0162         case 6:
0163             return ki18nc("Coptic month 6 - KLocale::NarrowName",  "M").toString(languages);
0164         case 7:
0165             return ki18nc("Coptic month 7 - KLocale::NarrowName",  "P").toString(languages);
0166         case 8:
0167             return ki18nc("Coptic month 8 - KLocale::NarrowName",  "P").toString(languages);
0168         case 9:
0169             return ki18nc("Coptic month 9 - KLocale::NarrowName",  "P").toString(languages);
0170         case 10:
0171             return ki18nc("Coptic month 10 - KLocale::NarrowName", "P").toString(languages);
0172         case 11:
0173             return ki18nc("Coptic month 11 - KLocale::NarrowName", "E").toString(languages);
0174         case 12:
0175             return ki18nc("Coptic month 12 - KLocale::NarrowName", "M").toString(languages);
0176         case 13:
0177             return ki18nc("Coptic month 13 - KLocale::NarrowName", "K").toString(languages);
0178         default:
0179             return QString();
0180         }
0181     }
0182 
0183     if (format == KLocale::ShortName && possessive) {
0184         switch (month) {
0185         case 1:
0186             return ki18nc("Coptic month 1 - KLocale::ShortName Possessive",  "of Tho").toString(languages);
0187         case 2:
0188             return ki18nc("Coptic month 2 - KLocale::ShortName Possessive",  "of Pao").toString(languages);
0189         case 3:
0190             return ki18nc("Coptic month 3 - KLocale::ShortName Possessive",  "of Hat").toString(languages);
0191         case 4:
0192             return ki18nc("Coptic month 4 - KLocale::ShortName Possessive",  "of Kia").toString(languages);
0193         case 5:
0194             return ki18nc("Coptic month 5 - KLocale::ShortName Possessive",  "of Tob").toString(languages);
0195         case 6:
0196             return ki18nc("Coptic month 6 - KLocale::ShortName Possessive",  "of Mes").toString(languages);
0197         case 7:
0198             return ki18nc("Coptic month 7 - KLocale::ShortName Possessive",  "of Par").toString(languages);
0199         case 8:
0200             return ki18nc("Coptic month 8 - KLocale::ShortName Possessive",  "of Pam").toString(languages);
0201         case 9:
0202             return ki18nc("Coptic month 9 - KLocale::ShortName Possessive",  "of Pas").toString(languages);
0203         case 10:
0204             return ki18nc("Coptic month 10 - KLocale::ShortName Possessive", "of Pan").toString(languages);
0205         case 11:
0206             return ki18nc("Coptic month 11 - KLocale::ShortName Possessive", "of Epe").toString(languages);
0207         case 12:
0208             return ki18nc("Coptic month 12 - KLocale::ShortName Possessive", "of Meo").toString(languages);
0209         case 13:
0210             return ki18nc("Coptic month 13 - KLocale::ShortName Possessive", "of Kou").toString(languages);
0211         default:
0212             return QString();
0213         }
0214     }
0215 
0216     if (format == KLocale::ShortName && !possessive) {
0217         switch (month) {
0218         case 1:
0219             return ki18nc("Coptic month 1 - KLocale::ShortName",  "Tho").toString(languages);
0220         case 2:
0221             return ki18nc("Coptic month 2 - KLocale::ShortName",  "Pao").toString(languages);
0222         case 3:
0223             return ki18nc("Coptic month 3 - KLocale::ShortName",  "Hat").toString(languages);
0224         case 4:
0225             return ki18nc("Coptic month 4 - KLocale::ShortName",  "Kia").toString(languages);
0226         case 5:
0227             return ki18nc("Coptic month 5 - KLocale::ShortName",  "Tob").toString(languages);
0228         case 6:
0229             return ki18nc("Coptic month 6 - KLocale::ShortName",  "Mes").toString(languages);
0230         case 7:
0231             return ki18nc("Coptic month 7 - KLocale::ShortName",  "Par").toString(languages);
0232         case 8:
0233             return ki18nc("Coptic month 8 - KLocale::ShortName",  "Pam").toString(languages);
0234         case 9:
0235             return ki18nc("Coptic month 9 - KLocale::ShortName",  "Pas").toString(languages);
0236         case 10:
0237             return ki18nc("Coptic month 10 - KLocale::ShortName", "Pan").toString(languages);
0238         case 11:
0239             return ki18nc("Coptic month 11 - KLocale::ShortName", "Epe").toString(languages);
0240         case 12:
0241             return ki18nc("Coptic month 12 - KLocale::ShortName", "Meo").toString(languages);
0242         case 13:
0243             return ki18nc("Coptic month 12 - KLocale::ShortName", "Kou").toString(languages);
0244         default:
0245             return QString();
0246         }
0247     }
0248 
0249     if (format == KLocale::LongName && possessive) {
0250         switch (month) {
0251         case 1:
0252             return ki18nc("Coptic month 1 - KLocale::LongName Possessive",  "of Thoout").toString(languages);
0253         case 2:
0254             return ki18nc("Coptic month 2 - KLocale::LongName Possessive",  "of Paope").toString(languages);
0255         case 3:
0256             return ki18nc("Coptic month 3 - KLocale::LongName Possessive",  "of Hathor").toString(languages);
0257         case 4:
0258             return ki18nc("Coptic month 4 - KLocale::LongName Possessive",  "of Kiahk").toString(languages);
0259         case 5:
0260             return ki18nc("Coptic month 5 - KLocale::LongName Possessive",  "of Tobe").toString(languages);
0261         case 6:
0262             return ki18nc("Coptic month 6 - KLocale::LongName Possessive",  "of Meshir").toString(languages);
0263         case 7:
0264             return ki18nc("Coptic month 7 - KLocale::LongName Possessive",  "of Paremhotep").toString(languages);
0265         case 8:
0266             return ki18nc("Coptic month 8 - KLocale::LongName Possessive",  "of Parmoute").toString(languages);
0267         case 9:
0268             return ki18nc("Coptic month 9 - KLocale::LongName Possessive",  "of Pashons").toString(languages);
0269         case 10:
0270             return ki18nc("Coptic month 10 - KLocale::LongName Possessive", "of Paone").toString(languages);
0271         case 11:
0272             return ki18nc("Coptic month 11 - KLocale::LongName Possessive", "of Epep").toString(languages);
0273         case 12:
0274             return ki18nc("Coptic month 12 - KLocale::LongName Possessive", "of Mesore").toString(languages);
0275         case 13:
0276             return ki18nc("Coptic month 12 - KLocale::LongName Possessive", "of Kouji nabot").toString(languages);
0277         default:
0278             return QString();
0279         }
0280     }
0281 
0282     // Default to LongName
0283     switch (month) {
0284     case 1:
0285         return ki18nc("Coptic month 1 - KLocale::LongName",  "Thoout").toString(languages);
0286     case 2:
0287         return ki18nc("Coptic month 2 - KLocale::LongName",  "Paope").toString(languages);
0288     case 3:
0289         return ki18nc("Coptic month 3 - KLocale::LongName",  "Hathor").toString(languages);
0290     case 4:
0291         return ki18nc("Coptic month 4 - KLocale::LongName",  "Kiahk").toString(languages);
0292     case 5:
0293         return ki18nc("Coptic month 5 - KLocale::LongName",  "Tobe").toString(languages);
0294     case 6:
0295         return ki18nc("Coptic month 6 - KLocale::LongName",  "Meshir").toString(languages);
0296     case 7:
0297         return ki18nc("Coptic month 7 - KLocale::LongName",  "Paremhotep").toString(languages);
0298     case 8:
0299         return ki18nc("Coptic month 8 - KLocale::LongName",  "Parmoute").toString(languages);
0300     case 9:
0301         return ki18nc("Coptic month 9 - KLocale::LongName",  "Pashons").toString(languages);
0302     case 10:
0303         return ki18nc("Coptic month 10 - KLocale::LongName", "Paone").toString(languages);
0304     case 11:
0305         return ki18nc("Coptic month 11 - KLocale::LongName", "Epep").toString(languages);
0306     case 12:
0307         return ki18nc("Coptic month 12 - KLocale::LongName", "Mesore").toString(languages);
0308     case 13:
0309         return ki18nc("Coptic month 12 - KLocale::LongName", "Kouji nabot").toString(languages);
0310     default:
0311         return QString();
0312     }
0313 }
0314 
0315 // Names taken from from the Sahidic dialect transliterations used in Dershowitz & Reingold which went out of use in the 11th centuary
0316 // Boharic or Arabic transliterations would be preferred but none could be found
0317 QString KCalendarSystemCopticPrivate::weekDayName(int weekDay, KLocale::DateTimeComponentFormat format) const
0318 {
0319     QStringList languages = locale()->languageList();
0320 
0321     if (format == KLocale::NarrowName) {
0322         switch (weekDay) {
0323         case 1:
0324             return ki18nc("Coptic weekday 1 - KLocale::NarrowName", "P").toString(languages);
0325         case 2:
0326             return ki18nc("Coptic weekday 2 - KLocale::NarrowName", "P").toString(languages);
0327         case 3:
0328             return ki18nc("Coptic weekday 3 - KLocale::NarrowName", "P").toString(languages);
0329         case 4:
0330             return ki18nc("Coptic weekday 4 - KLocale::NarrowName", "P").toString(languages);
0331         case 5:
0332             return ki18nc("Coptic weekday 5 - KLocale::NarrowName", "P").toString(languages);
0333         case 6:
0334             return ki18nc("Coptic weekday 6 - KLocale::NarrowName", "P").toString(languages);
0335         case 7:
0336             return ki18nc("Coptic weekday 7 - KLocale::NarrowName", "T").toString(languages);
0337         default:
0338             return QString();
0339         }
0340     }
0341 
0342     if (format == KLocale::ShortName  || format == KLocale:: ShortNumber) {
0343         switch (weekDay) {
0344         case 1:
0345             return ki18nc("Coptic weekday 1 - KLocale::ShortName", "Pes").toString(languages);
0346         case 2:
0347             return ki18nc("Coptic weekday 2 - KLocale::ShortName", "Psh").toString(languages);
0348         case 3:
0349             return ki18nc("Coptic weekday 3 - KLocale::ShortName", "Pef").toString(languages);
0350         case 4:
0351             return ki18nc("Coptic weekday 4 - KLocale::ShortName", "Pti").toString(languages);
0352         case 5:
0353             return ki18nc("Coptic weekday 5 - KLocale::ShortName", "Pso").toString(languages);
0354         case 6:
0355             return ki18nc("Coptic weekday 6 - KLocale::ShortName", "Psa").toString(languages);
0356         case 7:
0357             return ki18nc("Coptic weekday 7 - KLocale::ShortName", "Tky").toString(languages);
0358         default:
0359             return QString();
0360         }
0361     }
0362 
0363     switch (weekDay) {
0364     case 1:
0365         return ki18nc("Coptic weekday 1 - KLocale::LongName", "Pesnau").toString(languages);
0366     case 2:
0367         return ki18nc("Coptic weekday 2 - KLocale::LongName", "Pshoment").toString(languages);
0368     case 3:
0369         return ki18nc("Coptic weekday 3 - KLocale::LongName", "Peftoou").toString(languages);
0370     case 4:
0371         return ki18nc("Coptic weekday 4 - KLocale::LongName", "Ptiou").toString(languages);
0372     case 5:
0373         return ki18nc("Coptic weekday 5 - KLocale::LongName", "Psoou").toString(languages);
0374     case 6:
0375         return ki18nc("Coptic weekday 6 - KLocale::LongName", "Psabbaton").toString(languages);
0376     case 7:
0377         return ki18nc("Coptic weekday 7 - KLocale::LongName", "Tkyriakē").toString(languages);
0378     default:
0379         return QString();
0380     }
0381 }
0382 
0383 KCalendarSystemCoptic::KCalendarSystemCoptic(const KSharedConfig::Ptr config, const KLocale *locale)
0384     : KCalendarSystem(*new KCalendarSystemCopticPrivate(this), config, locale)
0385 {
0386     d_ptr->loadConfig(calendarType());
0387 }
0388 
0389 KCalendarSystemCoptic::KCalendarSystemCoptic(KCalendarSystemCopticPrivate &dd,
0390         const KSharedConfig::Ptr config, const KLocale *locale)
0391     : KCalendarSystem(dd, config, locale)
0392 {
0393     d_ptr->loadConfig(calendarType());
0394 }
0395 
0396 KCalendarSystemCoptic::~KCalendarSystemCoptic()
0397 {
0398 }
0399 
0400 QString KCalendarSystemCoptic::calendarType() const
0401 {
0402     return QLatin1String("coptic");
0403 }
0404 
0405 KLocale::CalendarSystem KCalendarSystemCoptic::calendarSystem() const
0406 {
0407     return KLocale::CopticCalendar;
0408 }
0409 
0410 QDate KCalendarSystemCoptic::epoch() const
0411 {
0412     //0001-01-01, no Year 0.
0413     //0284-08-29 AD Julian
0414     return QDate::fromJulianDay(1825030);
0415 }
0416 
0417 QDate KCalendarSystemCoptic::earliestValidDate() const
0418 {
0419     //0001-01-01, no Year 0.
0420     //0284-08-29 AD Julian
0421     return QDate::fromJulianDay(1825030);
0422 }
0423 
0424 QDate KCalendarSystemCoptic::latestValidDate() const
0425 {
0426     // Set to last day of year 9999 until confirm date formats & widgets support > 9999
0427     //9999-12-30
0428     //10283-08-29 AD Julian
0429     return QDate::fromJulianDay(5477164);
0430 }
0431 
0432 QString KCalendarSystemCoptic::monthName(int month, int year, MonthNameFormat format) const
0433 {
0434     return KCalendarSystem::monthName(month, year, format);
0435 }
0436 
0437 QString KCalendarSystemCoptic::monthName(const QDate &date, MonthNameFormat format) const
0438 {
0439     return KCalendarSystem::monthName(date, format);
0440 }
0441 
0442 QString KCalendarSystemCoptic::weekDayName(int weekDay, WeekDayNameFormat format) const
0443 {
0444     return KCalendarSystem::weekDayName(weekDay, format);
0445 }
0446 
0447 QString KCalendarSystemCoptic::weekDayName(const QDate &date, WeekDayNameFormat format) const
0448 {
0449     return KCalendarSystem::weekDayName(date, format);
0450 }
0451 
0452 bool KCalendarSystemCoptic::isLunar() const
0453 {
0454     return false;
0455 }
0456 
0457 bool KCalendarSystemCoptic::isLunisolar() const
0458 {
0459     return false;
0460 }
0461 
0462 bool KCalendarSystemCoptic::isSolar() const
0463 {
0464     return true;
0465 }
0466 
0467 bool KCalendarSystemCoptic::isProleptic() const
0468 {
0469     return false;
0470 }
0471 
0472 bool KCalendarSystemCoptic::julianDayToDate(qint64 jd, int &year, int &month, int &day) const
0473 {
0474     //The Coptic calendar has 12 months of 30 days, a 13th month of 5 or 6 days,
0475     //and a leap year every 4th year without fail that falls on the last day of
0476     //the year, starting from year 3.
0477 
0478     //Use a fake year 0 for our epoch instead of the real epoch in year 1. This is because year 3
0479     //is the first leap year and a pattern of 365/365/366/365 is hard to calculate, instead a
0480     //pattern of 365/365/365/366 with the leap day the very last day makes the maths easier.
0481 
0482     //Day number in the fake epoch, 0 indexed
0483     int dayInEpoch = jd - (epoch().toJulianDay() - 365);
0484     //How many full 4 year leap cycles have been completed, 1461 = (365*3)+366
0485     int leapCyclesCompleted = dayInEpoch / 1461;
0486     //Which year are we in the current 4 year leap cycle, 0 indexed
0487     //Need the qMin as day 366 of 4th year of cycle returns following year (max 3 as 0 index)
0488     int yearInCurrentLeapCycle = qMin(3, (dayInEpoch % 1461) / 365);
0489     //Calculate the year
0490     year = (leapCyclesCompleted * 4) + yearInCurrentLeapCycle;
0491     //Days since the fake epoch up to 1st day of this year
0492     int daysBeforeThisYear = (year * 365) + (year / 4);
0493     //Gives the day number in this year, 0 indexed
0494     int dayOfThisYear = dayInEpoch -  daysBeforeThisYear;
0495     //Then just calculate month and day from that based on regular 30 day months
0496     month = ((dayOfThisYear) / 30) + 1;
0497     day = dayOfThisYear - ((month - 1) * 30) + 1;
0498 
0499     // If year is -ve then is BC.  In Coptic there is no year 0, but the maths
0500     // is easier if we pretend there is, so internally year of 0 = 1BC = -1 outside
0501     if (year < 1) {
0502         year = year - 1;
0503     }
0504 
0505     return true;
0506 }
0507 
0508 bool KCalendarSystemCoptic::dateToJulianDay(int year, int month, int day, qint64 &jd) const
0509 {
0510     //The Coptic calendar has 12 months of 30 days, a 13th month of 5 or 6 days,
0511     //and a leap year every 4th year without fail that falls on the last day of
0512     //the year, starting from year 3.  This simple repeating pattern makes calculating
0513     // a jd the simple process taking the epoch jd and adding on the years months and
0514     //days required.
0515 
0516     // If year is -ve then is 'BC'.  In Coptic there is no year 0, but the maths
0517     // is easier if we pretend there is, so internally year of -1 = 1BC = 0 internally
0518     int y;
0519     if (year < 1) {
0520         y = year + 1;
0521     } else {
0522         y = year;
0523     }
0524 
0525     jd = epoch().toJulianDay() - 1    // jd of day before Epoch
0526          + ((y - 1) * 365)              // Add all normal days in years preceding
0527          + (y / 4)                      // Add all leap days in years preceding
0528          + ((month - 1) * 30)           // Add days this year in months preceding
0529          + day;                         // Add days in this month
0530 
0531     return true;
0532 }