1 /*
2     Copyright 2009, 2010 John Layt <john@layt.net>
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13 
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19 
20 #include "kcalendarsystemcoptic_p.h"
21 #include "kcalendarsystemprivate_p.h"
22 #include "kcalendarsystemcopticprivate_p.h"
23 
24 #include "klocale.h"
25 #include "klocalizedstring.h"
26 
27 #include <QDate>
28 #include <QCharRef>
29 
30 // Shared d pointer implementations
31 
KCalendarSystemCopticPrivate(KCalendarSystemCoptic * q)32 KCalendarSystemCopticPrivate::KCalendarSystemCopticPrivate(KCalendarSystemCoptic *q)
33     : KCalendarSystemPrivate(q)
34 {
35 }
36 
~KCalendarSystemCopticPrivate()37 KCalendarSystemCopticPrivate::~KCalendarSystemCopticPrivate()
38 {
39 }
40 
loadDefaultEraList()41 void KCalendarSystemCopticPrivate::loadDefaultEraList()
42 {
43     QString name, shortName, format;
44     // AM for Anno Martyrum or "Year of the Martyrs"
45     name = i18nc("Calendar Era: Coptic Era of Martyrs, years > 0, LongFormat", "Anno Martyrum");
46     shortName = i18nc("Calendar Era: Coptic Era of Martyrs, years > 0, ShortFormat", "AM");
47     format = i18nc("(kdedt-format) Coptic, AM, full era year format used for %EY, e.g. 2000 AM", "%Ey %EC");
48     addEra('+', 1, q->epoch(), 1, q->latestValidDate(), name, shortName, format);
49 }
50 
monthsInYear(int year) const51 int KCalendarSystemCopticPrivate::monthsInYear(int year) const
52 {
53     Q_UNUSED(year)
54     return 13;
55 }
56 
daysInMonth(int year,int month) const57 int KCalendarSystemCopticPrivate::daysInMonth(int year, int month) const
58 {
59     if (month == 13) {
60         if (isLeapYear(year)) {
61             return 6;
62         } else {
63             return 5;
64         }
65     }
66 
67     return 30;
68 }
69 
daysInYear(int year) const70 int KCalendarSystemCopticPrivate::daysInYear(int year) const
71 {
72     if (isLeapYear(year)) {
73         return 366;
74     } else {
75         return 365;
76     }
77 }
78 
isLeapYear(int year) const79 bool KCalendarSystemCopticPrivate::isLeapYear(int year) const
80 {
81     //Uses same rule as Julian but offset by 1 year with year 3 being first leap year
82     if (year < 1) {
83         year = year + 2;
84     } else {
85         year = year + 1;
86     }
87 
88     if (year % 4 == 0) {
89         return true;
90     }
91     return false;
92 }
93 
hasLeapMonths() const94 bool KCalendarSystemCopticPrivate::hasLeapMonths() const
95 {
96     return false;
97 }
98 
hasYearZero() const99 bool KCalendarSystemCopticPrivate::hasYearZero() const
100 {
101     return false;
102 }
103 
maxMonthsInYear() const104 int KCalendarSystemCopticPrivate::maxMonthsInYear() const
105 {
106     return 13;
107 }
108 
earliestValidYear() const109 int KCalendarSystemCopticPrivate::earliestValidYear() const
110 {
111     return 1;
112 }
113 
latestValidYear() const114 int KCalendarSystemCopticPrivate::latestValidYear() const
115 {
116     return 9999;
117 }
118 
119 // Names taken from Bohairic dialect transliterations in http://www.copticheritage.org/parameters/copticheritage/calendar/The_Coptic_Calendar.pdf
120 // These differ from the transliterations found on Wikipedia http://en.wikipedia.org/wiki/Coptic_calendar
121 // These differ from the Sahidic dialect transliterations used in Dershowitz & Reingold which went out of use in the 11th centuary
122 // These differ from the Arabic transliterations found on Wikipedia
123 // These differ from the transliterations used in Mac OSX 10.6 Snow Leopard
124 // The Boharic was initially chosen as this is the dialect apparantly in 'common' use in the Coptic Church.
125 // But it could be argued the Arabic forms should be used as they are in 'common' usage in Eqypt
126 // And where did the rest come from?
127 //
128 //    Boharic         Wikipedia Copt   D&R Sahidic     Wikipedia Arab  Mac OSX
129 //    --------------  ---------------  --------------  --------------  --------------
130 //  * Thoout          Thout            Thoout          Tout            Tout
131 //  * Paope           Paopi            Paope           Baba            Baba
132 //  * Hathor          Hathor           Athōr           Hatour          Hatour
133 //  * Kiahk           Koiak            Koiak           Kiahk           Kiahk
134 //  * Tobe            Tobi             Tōbe            Touba           Toba
135 //  * Meshir          Meshir           Meshir          Amshir          Amshir
136 //  * Paremhotep      Paremhat         Paremotep       Baramhat        Baramhat
137 //  * Parmoute        Paremoude        Paremoute       Baramouda       Baramouda
138 //  * Pashons         Pashons          Pashons         Bashans         Bashans
139 //  * Paone           Paoni            Paōne           Ba'ouna         Paona
140 //  * Epep            Epip             Epēp            Abib            Epep
141 //  * Mesore          Mesori           Mesorē          Mesra           Mesra
142 //  * Kouji nabot     Pi Kogi Enavot   Epagomenē                       Nasie
143 //  *
monthName(int month,int year,KLocale::DateTimeComponentFormat format,bool possessive) const144 QString KCalendarSystemCopticPrivate::monthName(int month, int year, KLocale::DateTimeComponentFormat format, bool possessive) const
145 {
146     Q_UNUSED(year);
147 
148     QStringList languages = locale()->languageList();
149 
150     if (format == KLocale::NarrowName) {
151         switch (month) {
152         case 1:
153             return ki18nc("Coptic month 1 - KLocale::NarrowName",  "T").toString(languages);
154         case 2:
155             return ki18nc("Coptic month 2 - KLocale::NarrowName",  "P").toString(languages);
156         case 3:
157             return ki18nc("Coptic month 3 - KLocale::NarrowName",  "H").toString(languages);
158         case 4:
159             return ki18nc("Coptic month 4 - KLocale::NarrowName",  "K").toString(languages);
160         case 5:
161             return ki18nc("Coptic month 5 - KLocale::NarrowName",  "T").toString(languages);
162         case 6:
163             return ki18nc("Coptic month 6 - KLocale::NarrowName",  "M").toString(languages);
164         case 7:
165             return ki18nc("Coptic month 7 - KLocale::NarrowName",  "P").toString(languages);
166         case 8:
167             return ki18nc("Coptic month 8 - KLocale::NarrowName",  "P").toString(languages);
168         case 9:
169             return ki18nc("Coptic month 9 - KLocale::NarrowName",  "P").toString(languages);
170         case 10:
171             return ki18nc("Coptic month 10 - KLocale::NarrowName", "P").toString(languages);
172         case 11:
173             return ki18nc("Coptic month 11 - KLocale::NarrowName", "E").toString(languages);
174         case 12:
175             return ki18nc("Coptic month 12 - KLocale::NarrowName", "M").toString(languages);
176         case 13:
177             return ki18nc("Coptic month 13 - KLocale::NarrowName", "K").toString(languages);
178         default:
179             return QString();
180         }
181     }
182 
183     if (format == KLocale::ShortName && possessive) {
184         switch (month) {
185         case 1:
186             return ki18nc("Coptic month 1 - KLocale::ShortName Possessive",  "of Tho").toString(languages);
187         case 2:
188             return ki18nc("Coptic month 2 - KLocale::ShortName Possessive",  "of Pao").toString(languages);
189         case 3:
190             return ki18nc("Coptic month 3 - KLocale::ShortName Possessive",  "of Hat").toString(languages);
191         case 4:
192             return ki18nc("Coptic month 4 - KLocale::ShortName Possessive",  "of Kia").toString(languages);
193         case 5:
194             return ki18nc("Coptic month 5 - KLocale::ShortName Possessive",  "of Tob").toString(languages);
195         case 6:
196             return ki18nc("Coptic month 6 - KLocale::ShortName Possessive",  "of Mes").toString(languages);
197         case 7:
198             return ki18nc("Coptic month 7 - KLocale::ShortName Possessive",  "of Par").toString(languages);
199         case 8:
200             return ki18nc("Coptic month 8 - KLocale::ShortName Possessive",  "of Pam").toString(languages);
201         case 9:
202             return ki18nc("Coptic month 9 - KLocale::ShortName Possessive",  "of Pas").toString(languages);
203         case 10:
204             return ki18nc("Coptic month 10 - KLocale::ShortName Possessive", "of Pan").toString(languages);
205         case 11:
206             return ki18nc("Coptic month 11 - KLocale::ShortName Possessive", "of Epe").toString(languages);
207         case 12:
208             return ki18nc("Coptic month 12 - KLocale::ShortName Possessive", "of Meo").toString(languages);
209         case 13:
210             return ki18nc("Coptic month 13 - KLocale::ShortName Possessive", "of Kou").toString(languages);
211         default:
212             return QString();
213         }
214     }
215 
216     if (format == KLocale::ShortName && !possessive) {
217         switch (month) {
218         case 1:
219             return ki18nc("Coptic month 1 - KLocale::ShortName",  "Tho").toString(languages);
220         case 2:
221             return ki18nc("Coptic month 2 - KLocale::ShortName",  "Pao").toString(languages);
222         case 3:
223             return ki18nc("Coptic month 3 - KLocale::ShortName",  "Hat").toString(languages);
224         case 4:
225             return ki18nc("Coptic month 4 - KLocale::ShortName",  "Kia").toString(languages);
226         case 5:
227             return ki18nc("Coptic month 5 - KLocale::ShortName",  "Tob").toString(languages);
228         case 6:
229             return ki18nc("Coptic month 6 - KLocale::ShortName",  "Mes").toString(languages);
230         case 7:
231             return ki18nc("Coptic month 7 - KLocale::ShortName",  "Par").toString(languages);
232         case 8:
233             return ki18nc("Coptic month 8 - KLocale::ShortName",  "Pam").toString(languages);
234         case 9:
235             return ki18nc("Coptic month 9 - KLocale::ShortName",  "Pas").toString(languages);
236         case 10:
237             return ki18nc("Coptic month 10 - KLocale::ShortName", "Pan").toString(languages);
238         case 11:
239             return ki18nc("Coptic month 11 - KLocale::ShortName", "Epe").toString(languages);
240         case 12:
241             return ki18nc("Coptic month 12 - KLocale::ShortName", "Meo").toString(languages);
242         case 13:
243             return ki18nc("Coptic month 12 - KLocale::ShortName", "Kou").toString(languages);
244         default:
245             return QString();
246         }
247     }
248 
249     if (format == KLocale::LongName && possessive) {
250         switch (month) {
251         case 1:
252             return ki18nc("Coptic month 1 - KLocale::LongName Possessive",  "of Thoout").toString(languages);
253         case 2:
254             return ki18nc("Coptic month 2 - KLocale::LongName Possessive",  "of Paope").toString(languages);
255         case 3:
256             return ki18nc("Coptic month 3 - KLocale::LongName Possessive",  "of Hathor").toString(languages);
257         case 4:
258             return ki18nc("Coptic month 4 - KLocale::LongName Possessive",  "of Kiahk").toString(languages);
259         case 5:
260             return ki18nc("Coptic month 5 - KLocale::LongName Possessive",  "of Tobe").toString(languages);
261         case 6:
262             return ki18nc("Coptic month 6 - KLocale::LongName Possessive",  "of Meshir").toString(languages);
263         case 7:
264             return ki18nc("Coptic month 7 - KLocale::LongName Possessive",  "of Paremhotep").toString(languages);
265         case 8:
266             return ki18nc("Coptic month 8 - KLocale::LongName Possessive",  "of Parmoute").toString(languages);
267         case 9:
268             return ki18nc("Coptic month 9 - KLocale::LongName Possessive",  "of Pashons").toString(languages);
269         case 10:
270             return ki18nc("Coptic month 10 - KLocale::LongName Possessive", "of Paone").toString(languages);
271         case 11:
272             return ki18nc("Coptic month 11 - KLocale::LongName Possessive", "of Epep").toString(languages);
273         case 12:
274             return ki18nc("Coptic month 12 - KLocale::LongName Possessive", "of Mesore").toString(languages);
275         case 13:
276             return ki18nc("Coptic month 12 - KLocale::LongName Possessive", "of Kouji nabot").toString(languages);
277         default:
278             return QString();
279         }
280     }
281 
282     // Default to LongName
283     switch (month) {
284     case 1:
285         return ki18nc("Coptic month 1 - KLocale::LongName",  "Thoout").toString(languages);
286     case 2:
287         return ki18nc("Coptic month 2 - KLocale::LongName",  "Paope").toString(languages);
288     case 3:
289         return ki18nc("Coptic month 3 - KLocale::LongName",  "Hathor").toString(languages);
290     case 4:
291         return ki18nc("Coptic month 4 - KLocale::LongName",  "Kiahk").toString(languages);
292     case 5:
293         return ki18nc("Coptic month 5 - KLocale::LongName",  "Tobe").toString(languages);
294     case 6:
295         return ki18nc("Coptic month 6 - KLocale::LongName",  "Meshir").toString(languages);
296     case 7:
297         return ki18nc("Coptic month 7 - KLocale::LongName",  "Paremhotep").toString(languages);
298     case 8:
299         return ki18nc("Coptic month 8 - KLocale::LongName",  "Parmoute").toString(languages);
300     case 9:
301         return ki18nc("Coptic month 9 - KLocale::LongName",  "Pashons").toString(languages);
302     case 10:
303         return ki18nc("Coptic month 10 - KLocale::LongName", "Paone").toString(languages);
304     case 11:
305         return ki18nc("Coptic month 11 - KLocale::LongName", "Epep").toString(languages);
306     case 12:
307         return ki18nc("Coptic month 12 - KLocale::LongName", "Mesore").toString(languages);
308     case 13:
309         return ki18nc("Coptic month 12 - KLocale::LongName", "Kouji nabot").toString(languages);
310     default:
311         return QString();
312     }
313 }
314 
315 // Names taken from from the Sahidic dialect transliterations used in Dershowitz & Reingold which went out of use in the 11th centuary
316 // Boharic or Arabic transliterations would be preferred but none could be found
weekDayName(int weekDay,KLocale::DateTimeComponentFormat format) const317 QString KCalendarSystemCopticPrivate::weekDayName(int weekDay, KLocale::DateTimeComponentFormat format) const
318 {
319     QStringList languages = locale()->languageList();
320 
321     if (format == KLocale::NarrowName) {
322         switch (weekDay) {
323         case 1:
324             return ki18nc("Coptic weekday 1 - KLocale::NarrowName", "P").toString(languages);
325         case 2:
326             return ki18nc("Coptic weekday 2 - KLocale::NarrowName", "P").toString(languages);
327         case 3:
328             return ki18nc("Coptic weekday 3 - KLocale::NarrowName", "P").toString(languages);
329         case 4:
330             return ki18nc("Coptic weekday 4 - KLocale::NarrowName", "P").toString(languages);
331         case 5:
332             return ki18nc("Coptic weekday 5 - KLocale::NarrowName", "P").toString(languages);
333         case 6:
334             return ki18nc("Coptic weekday 6 - KLocale::NarrowName", "P").toString(languages);
335         case 7:
336             return ki18nc("Coptic weekday 7 - KLocale::NarrowName", "T").toString(languages);
337         default:
338             return QString();
339         }
340     }
341 
342     if (format == KLocale::ShortName  || format == KLocale:: ShortNumber) {
343         switch (weekDay) {
344         case 1:
345             return ki18nc("Coptic weekday 1 - KLocale::ShortName", "Pes").toString(languages);
346         case 2:
347             return ki18nc("Coptic weekday 2 - KLocale::ShortName", "Psh").toString(languages);
348         case 3:
349             return ki18nc("Coptic weekday 3 - KLocale::ShortName", "Pef").toString(languages);
350         case 4:
351             return ki18nc("Coptic weekday 4 - KLocale::ShortName", "Pti").toString(languages);
352         case 5:
353             return ki18nc("Coptic weekday 5 - KLocale::ShortName", "Pso").toString(languages);
354         case 6:
355             return ki18nc("Coptic weekday 6 - KLocale::ShortName", "Psa").toString(languages);
356         case 7:
357             return ki18nc("Coptic weekday 7 - KLocale::ShortName", "Tky").toString(languages);
358         default:
359             return QString();
360         }
361     }
362 
363     switch (weekDay) {
364     case 1:
365         return ki18nc("Coptic weekday 1 - KLocale::LongName", "Pesnau").toString(languages);
366     case 2:
367         return ki18nc("Coptic weekday 2 - KLocale::LongName", "Pshoment").toString(languages);
368     case 3:
369         return ki18nc("Coptic weekday 3 - KLocale::LongName", "Peftoou").toString(languages);
370     case 4:
371         return ki18nc("Coptic weekday 4 - KLocale::LongName", "Ptiou").toString(languages);
372     case 5:
373         return ki18nc("Coptic weekday 5 - KLocale::LongName", "Psoou").toString(languages);
374     case 6:
375         return ki18nc("Coptic weekday 6 - KLocale::LongName", "Psabbaton").toString(languages);
376     case 7:
377         return ki18nc("Coptic weekday 7 - KLocale::LongName", "Tkyriakē").toString(languages);
378     default:
379         return QString();
380     }
381 }
382 
KCalendarSystemCoptic(const KSharedConfig::Ptr config,const KLocale * locale)383 KCalendarSystemCoptic::KCalendarSystemCoptic(const KSharedConfig::Ptr config, const KLocale *locale)
384     : KCalendarSystem(*new KCalendarSystemCopticPrivate(this), config, locale)
385 {
386     d_ptr->loadConfig(calendarType());
387 }
388 
KCalendarSystemCoptic(KCalendarSystemCopticPrivate & dd,const KSharedConfig::Ptr config,const KLocale * locale)389 KCalendarSystemCoptic::KCalendarSystemCoptic(KCalendarSystemCopticPrivate &dd,
390         const KSharedConfig::Ptr config, const KLocale *locale)
391     : KCalendarSystem(dd, config, locale)
392 {
393     d_ptr->loadConfig(calendarType());
394 }
395 
~KCalendarSystemCoptic()396 KCalendarSystemCoptic::~KCalendarSystemCoptic()
397 {
398 }
399 
calendarType() const400 QString KCalendarSystemCoptic::calendarType() const
401 {
402     return QLatin1String("coptic");
403 }
404 
calendarSystem() const405 KLocale::CalendarSystem KCalendarSystemCoptic::calendarSystem() const
406 {
407     return KLocale::CopticCalendar;
408 }
409 
epoch() const410 QDate KCalendarSystemCoptic::epoch() const
411 {
412     //0001-01-01, no Year 0.
413     //0284-08-29 AD Julian
414     return QDate::fromJulianDay(1825030);
415 }
416 
earliestValidDate() const417 QDate KCalendarSystemCoptic::earliestValidDate() const
418 {
419     //0001-01-01, no Year 0.
420     //0284-08-29 AD Julian
421     return QDate::fromJulianDay(1825030);
422 }
423 
latestValidDate() const424 QDate KCalendarSystemCoptic::latestValidDate() const
425 {
426     // Set to last day of year 9999 until confirm date formats & widgets support > 9999
427     //9999-12-30
428     //10283-08-29 AD Julian
429     return QDate::fromJulianDay(5477164);
430 }
431 
monthName(int month,int year,MonthNameFormat format) const432 QString KCalendarSystemCoptic::monthName(int month, int year, MonthNameFormat format) const
433 {
434     return KCalendarSystem::monthName(month, year, format);
435 }
436 
monthName(const QDate & date,MonthNameFormat format) const437 QString KCalendarSystemCoptic::monthName(const QDate &date, MonthNameFormat format) const
438 {
439     return KCalendarSystem::monthName(date, format);
440 }
441 
weekDayName(int weekDay,WeekDayNameFormat format) const442 QString KCalendarSystemCoptic::weekDayName(int weekDay, WeekDayNameFormat format) const
443 {
444     return KCalendarSystem::weekDayName(weekDay, format);
445 }
446 
weekDayName(const QDate & date,WeekDayNameFormat format) const447 QString KCalendarSystemCoptic::weekDayName(const QDate &date, WeekDayNameFormat format) const
448 {
449     return KCalendarSystem::weekDayName(date, format);
450 }
451 
isLunar() const452 bool KCalendarSystemCoptic::isLunar() const
453 {
454     return false;
455 }
456 
isLunisolar() const457 bool KCalendarSystemCoptic::isLunisolar() const
458 {
459     return false;
460 }
461 
isSolar() const462 bool KCalendarSystemCoptic::isSolar() const
463 {
464     return true;
465 }
466 
isProleptic() const467 bool KCalendarSystemCoptic::isProleptic() const
468 {
469     return false;
470 }
471 
julianDayToDate(qint64 jd,int & year,int & month,int & day) const472 bool KCalendarSystemCoptic::julianDayToDate(qint64 jd, int &year, int &month, int &day) const
473 {
474     //The Coptic calendar has 12 months of 30 days, a 13th month of 5 or 6 days,
475     //and a leap year every 4th year without fail that falls on the last day of
476     //the year, starting from year 3.
477 
478     //Use a fake year 0 for our epoch instead of the real epoch in year 1. This is because year 3
479     //is the first leap year and a pattern of 365/365/366/365 is hard to calculate, instead a
480     //pattern of 365/365/365/366 with the leap day the very last day makes the maths easier.
481 
482     //Day number in the fake epoch, 0 indexed
483     int dayInEpoch = jd - (epoch().toJulianDay() - 365);
484     //How many full 4 year leap cycles have been completed, 1461 = (365*3)+366
485     int leapCyclesCompleted = dayInEpoch / 1461;
486     //Which year are we in the current 4 year leap cycle, 0 indexed
487     //Need the qMin as day 366 of 4th year of cycle returns following year (max 3 as 0 index)
488     int yearInCurrentLeapCycle = qMin(3, (dayInEpoch % 1461) / 365);
489     //Calculate the year
490     year = (leapCyclesCompleted * 4) + yearInCurrentLeapCycle;
491     //Days since the fake epoch up to 1st day of this year
492     int daysBeforeThisYear = (year * 365) + (year / 4);
493     //Gives the day number in this year, 0 indexed
494     int dayOfThisYear = dayInEpoch -  daysBeforeThisYear;
495     //Then just calculate month and day from that based on regular 30 day months
496     month = ((dayOfThisYear) / 30) + 1;
497     day = dayOfThisYear - ((month - 1) * 30) + 1;
498 
499     // If year is -ve then is BC.  In Coptic there is no year 0, but the maths
500     // is easier if we pretend there is, so internally year of 0 = 1BC = -1 outside
501     if (year < 1) {
502         year = year - 1;
503     }
504 
505     return true;
506 }
507 
dateToJulianDay(int year,int month,int day,qint64 & jd) const508 bool KCalendarSystemCoptic::dateToJulianDay(int year, int month, int day, qint64 &jd) const
509 {
510     //The Coptic calendar has 12 months of 30 days, a 13th month of 5 or 6 days,
511     //and a leap year every 4th year without fail that falls on the last day of
512     //the year, starting from year 3.  This simple repeating pattern makes calculating
513     // a jd the simple process taking the epoch jd and adding on the years months and
514     //days required.
515 
516     // If year is -ve then is 'BC'.  In Coptic there is no year 0, but the maths
517     // is easier if we pretend there is, so internally year of -1 = 1BC = 0 internally
518     int y;
519     if (year < 1) {
520         y = year + 1;
521     } else {
522         y = year;
523     }
524 
525     jd = epoch().toJulianDay() - 1    // jd of day before Epoch
526          + ((y - 1) * 365)              // Add all normal days in years preceding
527          + (y / 4)                      // Add all leap days in years preceding
528          + ((month - 1) * 30)           // Add days this year in months preceding
529          + day;                         // Add days in this month
530 
531     return true;
532 }
533