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

0001 /*
0002     Copyright (c) 2003 Hans Petter Bieker <bieker@kde.org>
0003     Copyright 2007, 2009, 2010 John Layt <john@layt.net>
0004         Calendar conversion routines based on Hdate v6, by Amos
0005         Shapir 1978 (rev. 1985, 1992)
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Library General Public License for more details.
0016 
0017     You should have received a copy of the GNU Library General Public License
0018     along with this library; see the file COPYING.LIB.  If not, write to
0019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020     Boston, MA 02110-1301, USA.
0021 */
0022 
0023 // Derived hebrew kde calendar class
0024 
0025 #include "kcalendarsystemhebrew_p.h"
0026 #include "kcalendarsystemprivate_p.h"
0027 
0028 #include "klocale.h"
0029 #include "klocalizedstring.h"
0030 
0031 #include <QDate>
0032 #include <QCharRef>
0033 
0034 static int hebrewDaysElapsed(int y);
0035 
0036 class h_date
0037 {
0038 public:
0039     int hd_day;
0040     int hd_mon;
0041     int hd_year;
0042     int hd_dw;
0043     int hd_flg;
0044 };
0045 
0046 /*
0047  * compute general date structure from hebrew date
0048  */
0049 static class h_date *hebrewToGregorian(int y, int m, int d)
0050 {
0051     static class h_date h;
0052     int s;
0053 
0054     y -= 3744;
0055     s = hebrewDaysElapsed(y);
0056     d += s;
0057     s = hebrewDaysElapsed(y + 1) - s;      /* length of year */
0058 
0059     if (s > 365 && m > 6) {
0060         --m;
0061         d += 30;
0062     }
0063     d += (59 * (m - 1) + 1) / 2;      /* regular months */
0064     /* special cases */
0065     if (s % 10 > 4 && m > 2) {    /* long Heshvan */
0066         d++;
0067     }
0068     if (s % 10 < 4 && m > 3) {    /* short Kislev */
0069         d--;
0070     }
0071     // ### HPB: Broken in leap years
0072     //if (s > 365 && m > 6)  /* leap year */
0073     //  d += 30;
0074     d -= 6002;
0075 
0076     y = (d + 36525) * 4 / 146097 - 1;
0077     d -= y / 4 * 146097 + (y % 4) * 36524;
0078     y *= 100;
0079 
0080     /* compute year */
0081     s = (d + 366) * 4 / 1461 - 1;
0082     d -= s / 4 * 1461 + (s % 4) * 365;
0083     y += s;
0084     /* compute month */
0085     m = (d + 245) * 12 / 367 - 7;
0086     d -= m * 367 / 12 - 30;
0087     if (++m >= 12) {
0088         m -= 12;
0089         y++;
0090     }
0091     h.hd_day = d;
0092     h.hd_mon = m;
0093     h.hd_year = y;
0094     return (&h);
0095 }
0096 
0097 /*
0098  * compute date structure from no. of days since 1 Tishrei 3744
0099  */
0100 static class h_date *gregorianToHebrew(int y, int m, int d)
0101 {
0102     static class h_date h;
0103     int s;
0104 
0105     if ((m -= 2) <= 0) {
0106         m += 12;
0107         y--;
0108     }
0109     /* no. of days, Julian calendar */
0110     d += 365 * y + y / 4 + 367 * m / 12 + 5968;
0111     /* Gregorian calendar */
0112     d -= y / 100 - y / 400 - 2;
0113     h.hd_dw = (d + 1) % 7;
0114 
0115     /* compute the year */
0116     y += 16;
0117     s = hebrewDaysElapsed(y);
0118     m = hebrewDaysElapsed(y + 1);
0119     while (d >= m) {   /* computed year was underestimated */
0120         s = m;
0121         y++;
0122         m = hebrewDaysElapsed(y + 1);
0123     }
0124     d -= s;
0125     s = m - s;  /* size of current year */
0126     y += 3744;
0127 
0128     h.hd_flg = s % 10 - 4;
0129 
0130     /* compute day and month */
0131     if (d >= s - 236) {    /* last 8 months are regular */
0132         d -= s - 236;
0133         m = d * 2 / 59;
0134         d -= (m * 59 + 1) / 2;
0135         m += 4;
0136         if (s > 365 && m <= 5) {    /* Adar of Meuberet */
0137             m += 8;
0138         }
0139     } else {
0140         /* first 4 months have 117-119 days */
0141         s = 114 + s % 10;
0142         m = d * 4 / s;
0143         d -= (m * s + 3) / 4;
0144     }
0145 
0146     h.hd_day = d;
0147     h.hd_mon = m;
0148     h.hd_year = y;
0149     return (&h);
0150 }
0151 
0152 /* constants, in 1/18th of minute */
0153 static const int HOUR = 1080;
0154 static const int DAY = 24 * HOUR;
0155 static const int WEEK = 7 * DAY;
0156 #define M(h,p) ((h)*HOUR+p)
0157 #define MONTH (DAY+M(12,793))
0158 
0159 /**
0160  * @internal
0161  * no. of days in y years
0162  */
0163 static int hebrewDaysElapsed(int y)
0164 {
0165     int m, nm, dw, s, l;
0166 
0167     l = y * 7 + 1;  // no. of leap months
0168     m = y * 12 + l / 19;  // total no. of months
0169     l %= 19;
0170     nm = m * MONTH + M(1 + 6, 779);   // molad new year 3744 (16BC) + 6 hours
0171     s = m * 28 + nm / DAY - 2;
0172 
0173     nm %= WEEK;
0174     dw = nm / DAY;
0175     nm %= DAY;
0176 
0177     // special cases of Molad Zaken
0178     if ((l < 12 && dw == 3 && nm >= M(9 + 6, 204)) ||
0179             (l < 7 && dw == 2 && nm >= M(15 + 6, 589))) {
0180         s++, dw++;
0181     }
0182 
0183     /* ADU */
0184     if (dw == 1 || dw == 4 || dw == 6) {
0185         s++;
0186     }
0187     return s;
0188 }
0189 
0190 /**
0191  * @internal
0192  * true if long Cheshvan
0193  */
0194 static int long_cheshvan(int year)
0195 {
0196     QDate first, last;
0197     class h_date *gd;
0198 
0199     gd = hebrewToGregorian(year, 1, 1);
0200     first.setDate(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
0201 
0202     gd = hebrewToGregorian(year + 1, 1, 1);
0203     last.setDate(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
0204 
0205     return (first.daysTo(last) % 10 == 5);
0206 }
0207 
0208 /**
0209  * @internal
0210  * true if short Kislev
0211  */
0212 static int short_kislev(int year)
0213 {
0214     QDate first, last;
0215     class h_date *gd;
0216 
0217     gd = hebrewToGregorian(year, 1, 1);
0218     first.setDate(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
0219 
0220     gd = hebrewToGregorian(year + 1, 1, 1);
0221     last.setDate(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
0222 
0223     return (first.daysTo(last) % 10 == 3);
0224 }
0225 
0226 // Ok
0227 static class h_date *toHebrew(const QDate &date)
0228 {
0229     class h_date *sd;
0230 
0231     sd = gregorianToHebrew(date.year(), date.month(), date.day());
0232     ++sd->hd_mon;
0233     ++sd->hd_day;
0234 
0235     return sd;
0236 }
0237 
0238 class KCalendarSystemHebrewPrivate : public KCalendarSystemPrivate
0239 {
0240 public:
0241     KDELIBS4SUPPORT_DEPRECATED explicit KCalendarSystemHebrewPrivate(KCalendarSystemHebrew *q);
0242 
0243     ~KCalendarSystemHebrewPrivate() override;
0244 
0245     // Virtual methods each calendar system must re-implement
0246     void loadDefaultEraList() override;
0247     int monthsInYear(int year) const override;
0248     int daysInMonth(int year, int month) const override;
0249     int daysInYear(int year) const override;
0250     bool isLeapYear(int year) const override;
0251     bool hasLeapMonths() const override;
0252     bool hasYearZero() const override;
0253     int maxMonthsInYear() const override;
0254     int earliestValidYear() const override;
0255     int latestValidYear() const override;
0256     QString monthName(int month, int year, KLocale::DateTimeComponentFormat format, bool possessive) const override;
0257     QString weekDayName(int weekDay, KLocale::DateTimeComponentFormat format) const override;
0258 
0259     int integerFromString(const QString &string, int maxLength, int &readLength) const override;
0260     QString stringFromInteger(int number, int padWidth = 0, QChar padChar = QLatin1Char('0')) const override;
0261     QString stringFromInteger(int number, int padWidth, QChar padChar, KLocale::DigitSet digitSet) const override;
0262 
0263     virtual int monthNumberToMonthIndex(int year, int month) const;
0264 };
0265 
0266 // Shared d pointer base class definitions
0267 
0268 KCalendarSystemHebrewPrivate::KCalendarSystemHebrewPrivate(KCalendarSystemHebrew *q)
0269     : KCalendarSystemPrivate(q)
0270 {
0271 }
0272 
0273 KCalendarSystemHebrewPrivate::~KCalendarSystemHebrewPrivate()
0274 {
0275 }
0276 
0277 void KCalendarSystemHebrewPrivate::loadDefaultEraList()
0278 {
0279     QString name, shortName, format;
0280     // Jewish Era, Anno Mundi, "Year of the World".
0281     name = i18nc("Calendar Era: Hebrew Era, years > 0, LongFormat", "Anno Mundi");
0282     shortName = i18nc("Calendar Era: Hebrew Era, years > 0, ShortFormat", "AM");
0283     format = i18nc("(kdedt-format) Hebrew, AM, full era year format used for %EY, e.g. 2000 AM", "%Ey %EC");
0284     addEra('+', 1, q->epoch(), 1, q->latestValidDate(), name, shortName, format);
0285 }
0286 
0287 int KCalendarSystemHebrewPrivate::monthsInYear(int year) const
0288 {
0289     if (isLeapYear(year)) {
0290         return 13;
0291     } else {
0292         return 12;
0293     }
0294 }
0295 
0296 int KCalendarSystemHebrewPrivate::daysInMonth(int year, int month) const
0297 {
0298     int mi = monthNumberToMonthIndex(year, month);
0299 
0300     if (mi == 2 && long_cheshvan(year)) {
0301         return 30;
0302     }
0303 
0304     if (mi == 3 && short_kislev(year)) {
0305         return 29;
0306     }
0307 
0308     if (mi % 2 == 0) {    // Even number months have 29 days
0309         return 29;
0310     } else {  // Odd number months have 30 days
0311         return 30;
0312     }
0313 }
0314 
0315 int KCalendarSystemHebrewPrivate::daysInYear(int year) const
0316 {
0317     int days;
0318 
0319     // Get Regular year length
0320     if (isLeapYear(year)) {      // Has 13 months
0321         days = 384;
0322     } else {  // Has 12 months
0323         days = 354;
0324     }
0325 
0326     // Check if is Deficient or Abundant year
0327     if (short_kislev(year)) {     // Deficient
0328         days = days - 1;
0329     } else if (long_cheshvan(year)) {     // Abundant
0330         days = days + 1;
0331     }
0332 
0333     return days;
0334 }
0335 
0336 bool KCalendarSystemHebrewPrivate::isLeapYear(int year) const
0337 {
0338     return ((((7 * year) + 1) % 19) < 7);
0339 }
0340 
0341 bool KCalendarSystemHebrewPrivate::hasLeapMonths() const
0342 {
0343     return true;
0344 }
0345 
0346 bool KCalendarSystemHebrewPrivate::hasYearZero() const
0347 {
0348     return false;
0349 }
0350 
0351 int KCalendarSystemHebrewPrivate::maxMonthsInYear() const
0352 {
0353     return 13;
0354 }
0355 
0356 int KCalendarSystemHebrewPrivate::earliestValidYear() const
0357 {
0358     return 5344;
0359 }
0360 
0361 int KCalendarSystemHebrewPrivate::latestValidYear() const
0362 {
0363     return 8119;
0364 }
0365 
0366 int KCalendarSystemHebrewPrivate::integerFromString(const QString &inputString, int maxLength, int &readLength) const
0367 {
0368     if (locale()->language() == QLatin1String("he")) {
0369 
0370         // Hebrew numbers are composed of combinations of normal letters which have a numeric value.
0371         // This is a non-positional system, the numeric values are simply added together, however
0372         // convention is for a RTL highest to lowest value ordering. There is also a degree of
0373         // ambiguity due to the lack of a letter for 0, hence 5 and 5000 are written the same.
0374         // Hebrew numbers are only used in dates.
0375         // See http://www.i18nguy.com/unicode/hebrew-numbers.html for more explaination
0376 
0377         /*
0378         Ref table for numbers to Hebrew chars
0379 
0380         Value     1       2       3        4        5       6         7        8      9
0381 
0382         x 1    Alef א  Bet  ב  Gimel ג  Dalet ד  He   ה  Vav  ו    Zayen ז  Het  ח  Tet  ט
0383                0x05D0  0x05D1  0x05D2   0x05D3   0x05D4  0x05D5    0x05D6   0x05D7  0x05D8
0384 
0385         x 10   Yod  י  Kaf  כ  Lamed ל  Mem  מ   Nun  נ  Samekh ס  Ayin ע   Pe   פ  Tzadi צ
0386                0x05D9  0x05DB  0x05DC   0x05DE   0x05E0  0x05E1    0x05E2   0x05E4  0x05E6
0387 
0388         x 100  Qof  ק  Resh ר  Shin ש   Tav  ת
0389                0x05E7  0x05E8  0x05E9   0x05EA
0390 
0391         Note special cases 15 = 9 + 6 = 96 טו and 16 = 9 + 7 = 97 טז
0392         */
0393 
0394         int decadeValues[14] = {10, 20, 20, 30, 40, 40, 50, 50, 60, 70, 80, 80, 90, 90};
0395 
0396         QChar thisChar, nextChar;
0397         QString string = inputString;
0398 
0399         int stringLength = string.length();
0400         readLength = 0;
0401         int position = 0;
0402         int result = 0;
0403         int value = 0;
0404 
0405         for (; position < stringLength; ++position) {
0406 
0407             thisChar = string[position];
0408 
0409             if (position + 1 < stringLength) {
0410                 nextChar = string[position + 1];
0411                 // Ignore any geresh or gershayim chars, we don't bother checking they are in the right place
0412                 if (nextChar == QLatin1Char('\'') ||  nextChar == QChar(0x05F3) ||      // geresh
0413                         nextChar == QLatin1Char('\"') ||  nextChar == QChar(0x05F4)) {     // gershayim
0414                     string.remove(position + 1, 1);
0415                     stringLength = string.length();
0416                     if (position + 1 < stringLength) {
0417                         nextChar = string[position + 1];
0418                     } else {
0419                         nextChar = QChar();
0420                     }
0421                     readLength = readLength + 1;
0422                 }
0423             } else {
0424                 nextChar = QChar();
0425             }
0426 
0427             if (thisChar >= QChar(0x05D0) && thisChar <= QChar(0x05D7)) {
0428 
0429                 // If this char Alef to Het, 1 to 8, א to ח
0430 
0431                 // If next char is any valid digit char (Alef to Tav, 1 to 400, א to ת)
0432                 // then this char is a thousands digit
0433                 // else this char is a ones digit
0434 
0435                 if (nextChar >= QChar(0x05D0) && nextChar <= QChar(0x05EA)) {
0436                     value = (thisChar.unicode() - 0x05D0 + 1) * 1000;
0437                 } else {
0438                     value = thisChar.unicode() - 0x05D0 + 1;
0439                 }
0440 
0441             } else if (thisChar == QChar(0x05D8)) {
0442 
0443                 // If this char is Tet, 9, ט
0444 
0445                 // If next char is any valid digit char (Alef to Tav, 1 to 400, א to ת)
0446                 // and next char not 6 (Special case for 96 = 15)
0447                 // and next char not 7 (Special case for 97 = 16)
0448                 // then is a thousands digit else is 9
0449 
0450                 if (nextChar >= QChar(0x05D0) && nextChar <= QChar(0x05EA) &&
0451                         nextChar != QChar(0x05D5) && nextChar != QChar(0x05D6)) {
0452                     value = 9000;
0453                 } else {
0454                     value = 9;
0455                 }
0456 
0457             } else if (thisChar >= QChar(0x05D9) && thisChar <= QChar(0x05E6)) {
0458 
0459                 // If this char Yod to Tsadi, 10 to 90, י to צ
0460 
0461                 // If next char is a tens or hundreds char then is an error
0462                 // Else is a tens digit
0463 
0464                 if (nextChar >= QChar(0x05D9)) {
0465                     return -1;
0466                 } else {
0467                     value = decadeValues[thisChar.unicode() - 0x05D9];
0468                 }
0469 
0470             } else if (thisChar >= QChar(0x05E7) && thisChar <= QChar(0x05EA)) {
0471 
0472                 // If this char Qof to Tav, 100 to 400, ק to ת, then is hundreds digit
0473 
0474                 value = (thisChar.unicode() - 0x05E7 + 1) * 100;
0475 
0476             } else {
0477 
0478                 // If this char any non-digit char including whitespace or punctuation, we're done
0479                 break;
0480 
0481             }
0482 
0483             result = result + value;
0484 
0485             value = 0;
0486         }
0487 
0488         readLength += position;
0489 
0490         return result;
0491 
0492     } else {
0493         return KCalendarSystemPrivate::integerFromString(inputString, maxLength, readLength);
0494     }
0495 }
0496 
0497 QString KCalendarSystemHebrewPrivate::stringFromInteger(int number, int padWidth, QChar padChar) const
0498 {
0499     return KCalendarSystemPrivate::stringFromInteger(number, padWidth, padChar);
0500 }
0501 
0502 QString KCalendarSystemHebrewPrivate::stringFromInteger(int number, int padWidth, QChar padChar, KLocale::DigitSet digitSet) const
0503 {
0504     if (locale()->language() == QLatin1String("he")) {
0505 
0506         // Hebrew numbers are composed of combinations of normal letters which have a numeric value.
0507         // This is a non-positional system, the numeric values are simply added together, however
0508         // convention is for a RTL highest to lowest value ordering. There is also a degree of
0509         // ambiguity due to the lack of a letter for 0, hence 5 and 5000 are written the same.
0510         // Hebrew numbers are only used in dates.
0511         // See http://www.i18nguy.com/unicode/hebrew-numbers.html for more explaination
0512 
0513         /*
0514         Ref table for numbers to Hebrew chars
0515 
0516         Value     1       2       3        4        5       6         7        8      9
0517 
0518         x 1    Alef א  Bet  ב  Gimel ג  Dalet ד  He   ה  Vav  ו    Zayen ז  Het  ח  Tet  ט
0519                0x05D0  0x05D1  0x05D2   0x05D3   0x05D4  0x05D5    0x05D6   0x05D7  0x05D8
0520 
0521         x 10   Yod  י  Kaf  כ  Lamed ל  Mem  מ   Nun  נ  Samekh ס  Ayin ע   Pe   פ  Tzadi צ
0522                0x05D9  0x05DB  0x05DC   0x05DE   0x05E0  0x05E1    0x05E2   0x05E4  0x05E6
0523 
0524         x 100  Qof  ק  Resh ר  Shin ש   Tav  ת
0525                0x05E7  0x05E8  0x05E9   0x05EA
0526 
0527         Note special cases 15 = 9 + 6 = 96 טו and 16 = 9 + 7 = 97 טז
0528         */
0529 
0530         const QChar decade[] = {
0531             //  Tet = ט,    Yod = י,    Kaf = כ,    Lamed = ל,  Mem = מ
0532             //  Nun = נ,    Samekh = ס, Ayin = ע,   Pe = פ,     Tsadi = צ
0533             0x05D8,     0x05D9,     0x05DB,     0x05DC,     0x05DE,
0534             0x05E0,     0x05E1,     0x05E2,     0x05E4,     0x05E6
0535         };
0536 
0537         QString result;
0538 
0539         // We have no rules for coping with numbers outside this range
0540         if (number < 1 || number > 9999) {
0541             return KCalendarSystemPrivate::stringFromInteger(number, padWidth, padChar, digitSet);
0542         }
0543 
0544         // Translate the thousands digit, just uses letter for number 1..9 ( א to ט, Alef to Tet )
0545         // Years 5001-5999 do not have the thousands by convention
0546         if (number >= 1000) {
0547             if (number <= 5000 || number >= 6000) {
0548                 result += QChar(0x05D0 - 1 + number / 1000);    // Alef א to Tet ט
0549             }
0550             number %= 1000;
0551         }
0552 
0553         // Translate the hundreds digit
0554         // Use traditional method where we only have letters assigned values for 100, 200, 300 and 400
0555         // so may need to repeat 400 twice to make up the required number
0556         if (number >= 100) {
0557             while (number >= 500) {
0558                 result += QChar(0x05EA);    // Tav = ת
0559                 number -= 400;
0560             }
0561             result += QChar(0x05E7 - 1 + number / 100);   // Qof = ק to xxx
0562             number %= 100;
0563         }
0564 
0565         // Translate the tens digit
0566         // The numbers 15 and 16 translate to letters that spell out the name of God which is
0567         // forbidden, so require special treatment where 15 = 9 + 6 and 1 = 9 + 7.
0568         if (number >= 10) {
0569             if (number == 15 || number == 16) {
0570                 number -= 9;
0571             }
0572             result += decade[number / 10];
0573             number %= 10;
0574         }
0575 
0576         // Translate the ones digit, uses letter for number 1..9 ( א to ט, Alef to Tet )
0577         if (number > 0) {
0578             result += QChar(0x05D0 - 1 + number);    // Alef = א to xxx
0579         }
0580 
0581         // When used in a string with mixed names and numbers the numbers need special chars to
0582         // distinguish them from words composed of the same letters.
0583         // Single digit numbers are followed by a geresh symbol ? (Unicode = 0x05F3), but we use
0584         // single quote for convenience.
0585         // Multiple digit numbers have a gershayim symbol ? (Unicode = 0x05F4) as second-to-last
0586         // char, but we use double quote for convenience.
0587         if (result.length() == 1) {
0588             result += QLatin1Char('\'');
0589         } else {
0590             result.insert(result.length() - 1, QLatin1Char('\"'));
0591         }
0592 
0593         return result;
0594 
0595     } else {
0596         return KCalendarSystemPrivate::stringFromInteger(number, padWidth, padChar, digitSet);
0597     }
0598 }
0599 
0600 int KCalendarSystemHebrewPrivate::monthNumberToMonthIndex(int year, int month) const
0601 {
0602     if (isLeapYear(year)) {
0603         if (month == 6) {
0604             return 13;        // Adar I
0605         } else if (month == 7) {
0606             return 14;        // Adar II
0607         } else if (month > 7) {
0608             return month - 1; // Because of Adar II
0609         }
0610     }
0611 
0612     return month;
0613 }
0614 
0615 QString KCalendarSystemHebrewPrivate::monthName(int month, int year, KLocale::DateTimeComponentFormat format, bool possessive) const
0616 {
0617     // We must map month number to month index
0618     int monthIndex = monthNumberToMonthIndex(year, month);
0619 
0620     QStringList languages = locale()->languageList();
0621 
0622     if (format == KLocale::NarrowName) {
0623         switch (monthIndex) {
0624         case 1:
0625             return ki18nc("Hebrew month 1 - KLocale::NarrowName",  "T").toString(languages);
0626         case 2:
0627             return ki18nc("Hebrew month 2 - KLocale::NarrowName",  "H").toString(languages);
0628         case 3:
0629             return ki18nc("Hebrew month 3 - KLocale::NarrowName",  "K").toString(languages);
0630         case 4:
0631             return ki18nc("Hebrew month 4 - KLocale::NarrowName",  "T").toString(languages);
0632         case 5:
0633             return ki18nc("Hebrew month 5 - KLocale::NarrowName",  "S").toString(languages);
0634         case 6:
0635             return ki18nc("Hebrew month 6 - KLocale::NarrowName",  "A").toString(languages);
0636         case 7:
0637             return ki18nc("Hebrew month 7 - KLocale::NarrowName",  "N").toString(languages);
0638         case 8:
0639             return ki18nc("Hebrew month 8 - KLocale::NarrowName",  "I").toString(languages);
0640         case 9:
0641             return ki18nc("Hebrew month 9 - KLocale::NarrowName",  "S").toString(languages);
0642         case 10:
0643             return ki18nc("Hebrew month 10 - KLocale::NarrowName", "T").toString(languages);
0644         case 11:
0645             return ki18nc("Hebrew month 11 - KLocale::NarrowName", "A").toString(languages);
0646         case 12:
0647             return ki18nc("Hebrew month 12 - KLocale::NarrowName", "E").toString(languages);
0648         case 13:
0649             return ki18nc("Hebrew month 13 - KLocale::NarrowName", "A").toString(languages);
0650         case 14:
0651             return ki18nc("Hebrew month 14 - KLocale::NarrowName", "A").toString(languages);
0652         default:
0653             return QString();
0654         }
0655     }
0656 
0657     if (format == KLocale::ShortName && possessive) {
0658         switch (monthIndex) {
0659         case 1:
0660             return ki18nc("Hebrew month 1 - KLocale::ShortName Possessive",  "of Tis").toString(languages);
0661         case 2:
0662             return ki18nc("Hebrew month 2 - KLocale::ShortName Possessive",  "of Hes").toString(languages);
0663         case 3:
0664             return ki18nc("Hebrew month 3 - KLocale::ShortName Possessive",  "of Kis").toString(languages);
0665         case 4:
0666             return ki18nc("Hebrew month 4 - KLocale::ShortName Possessive",  "of Tev").toString(languages);
0667         case 5:
0668             return ki18nc("Hebrew month 5 - KLocale::ShortName Possessive",  "of Shv").toString(languages);
0669         case 6:
0670             return ki18nc("Hebrew month 6 - KLocale::ShortName Possessive",  "of Ada").toString(languages);
0671         case 7:
0672             return ki18nc("Hebrew month 7 - KLocale::ShortName Possessive",  "of Nis").toString(languages);
0673         case 8:
0674             return ki18nc("Hebrew month 8 - KLocale::ShortName Possessive",  "of Iya").toString(languages);
0675         case 9:
0676             return ki18nc("Hebrew month 9 - KLocale::ShortName Possessive",  "of Siv").toString(languages);
0677         case 10:
0678             return ki18nc("Hebrew month 10 - KLocale::ShortName Possessive", "of Tam").toString(languages);
0679         case 11:
0680             return ki18nc("Hebrew month 11 - KLocale::ShortName Possessive", "of Av").toString(languages);
0681         case 12:
0682             return ki18nc("Hebrew month 12 - KLocale::ShortName Possessive", "of Elu").toString(languages);
0683         case 13:
0684             return ki18nc("Hebrew month 13 - KLocale::ShortName Possessive", "of Ad1").toString(languages);
0685         case 14:
0686             return ki18nc("Hebrew month 14 - KLocale::ShortName Possessive", "of Ad2").toString(languages);
0687         default:
0688             return QString();
0689         }
0690     }
0691 
0692     if (format == KLocale::ShortName && !possessive) {
0693         switch (monthIndex) {
0694         case 1:
0695             return ki18nc("Hebrew month 1 - KLocale::ShortName",  "Tis").toString(languages);
0696         case 2:
0697             return ki18nc("Hebrew month 2 - KLocale::ShortName",  "Hes").toString(languages);
0698         case 3:
0699             return ki18nc("Hebrew month 3 - KLocale::ShortName",  "Kis").toString(languages);
0700         case 4:
0701             return ki18nc("Hebrew month 4 - KLocale::ShortName",  "Tev").toString(languages);
0702         case 5:
0703             return ki18nc("Hebrew month 5 - KLocale::ShortName",  "Shv").toString(languages);
0704         case 6:
0705             return ki18nc("Hebrew month 6 - KLocale::ShortName",  "Ada").toString(languages);
0706         case 7:
0707             return ki18nc("Hebrew month 7 - KLocale::ShortName",  "Nis").toString(languages);
0708         case 8:
0709             return ki18nc("Hebrew month 8 - KLocale::ShortName",  "Iya").toString(languages);
0710         case 9:
0711             return ki18nc("Hebrew month 9 - KLocale::ShortName",  "Siv").toString(languages);
0712         case 10:
0713             return ki18nc("Hebrew month 10 - KLocale::ShortName", "Tam").toString(languages);
0714         case 11:
0715             return ki18nc("Hebrew month 11 - KLocale::ShortName", "Av").toString(languages);
0716         case 12:
0717             return ki18nc("Hebrew month 12 - KLocale::ShortName", "Elu").toString(languages);
0718         case 13:
0719             return ki18nc("Hebrew month 13 - KLocale::ShortName", "Ad1").toString(languages);
0720         case 14:
0721             return ki18nc("Hebrew month 14 - KLocale::ShortName", "Ad2").toString(languages);
0722         default:
0723             return QString();
0724         }
0725     }
0726 
0727     if (format == KLocale::LongName && possessive) {
0728         switch (monthIndex) {
0729         case 1:
0730             return ki18nc("Hebrew month 1 - KLocale::LongName Possessive",  "of Tishrey").toString(languages);
0731         case 2:
0732             return ki18nc("Hebrew month 2 - KLocale::LongName Possessive",  "of Heshvan").toString(languages);
0733         case 3:
0734             return ki18nc("Hebrew month 3 - KLocale::LongName Possessive",  "of Kislev").toString(languages);
0735         case 4:
0736             return ki18nc("Hebrew month 4 - KLocale::LongName Possessive",  "of Tevet").toString(languages);
0737         case 5:
0738             return ki18nc("Hebrew month 5 - KLocale::LongName Possessive",  "of Shvat").toString(languages);
0739         case 6:
0740             return ki18nc("Hebrew month 6 - KLocale::LongName Possessive",  "of Adar").toString(languages);
0741         case 7:
0742             return ki18nc("Hebrew month 7 - KLocale::LongName Possessive",  "of Nisan").toString(languages);
0743         case 8:
0744             return ki18nc("Hebrew month 8 - KLocale::LongName Possessive",  "of Iyar").toString(languages);
0745         case 9:
0746             return ki18nc("Hebrew month 9 - KLocale::LongName Possessive",  "of Sivan").toString(languages);
0747         case 10:
0748             return ki18nc("Hebrew month 10 - KLocale::LongName Possessive", "of Tamuz").toString(languages);
0749         case 11:
0750             return ki18nc("Hebrew month 11 - KLocale::LongName Possessive", "of Av").toString(languages);
0751         case 12:
0752             return ki18nc("Hebrew month 12 - KLocale::LongName Possessive", "of Elul").toString(languages);
0753         case 13:
0754             return ki18nc("Hebrew month 13 - KLocale::LongName Possessive", "of Adar I").toString(languages);
0755         case 14:
0756             return ki18nc("Hebrew month 14 - KLocale::LongName Possessive", "of Adar II").toString(languages);
0757         default:
0758             return QString();
0759         }
0760     }
0761 
0762     // Default to LongName
0763     switch (monthIndex) {
0764     case 1:
0765         return ki18nc("Hebrew month 1 - KLocale::LongName",  "Tishrey").toString(languages);
0766     case 2:
0767         return ki18nc("Hebrew month 2 - KLocale::LongName",  "Heshvan").toString(languages);
0768     case 3:
0769         return ki18nc("Hebrew month 3 - KLocale::LongName",  "Kislev").toString(languages);
0770     case 4:
0771         return ki18nc("Hebrew month 4 - KLocale::LongName",  "Tevet").toString(languages);
0772     case 5:
0773         return ki18nc("Hebrew month 5 - KLocale::LongName",  "Shvat").toString(languages);
0774     case 6:
0775         return ki18nc("Hebrew month 6 - KLocale::LongName",  "Adar").toString(languages);
0776     case 7:
0777         return ki18nc("Hebrew month 7 - KLocale::LongName",  "Nisan").toString(languages);
0778     case 8:
0779         return ki18nc("Hebrew month 8 - KLocale::LongName",  "Iyar").toString(languages);
0780     case 9:
0781         return ki18nc("Hebrew month 9 - KLocale::LongName",  "Sivan").toString(languages);
0782     case 10:
0783         return ki18nc("Hebrew month 10 - KLocale::LongName", "Tamuz").toString(languages);
0784     case 11:
0785         return ki18nc("Hebrew month 11 - KLocale::LongName", "Av").toString(languages);
0786     case 12:
0787         return ki18nc("Hebrew month 12 - KLocale::LongName", "Elul").toString(languages);
0788     case 13:
0789         return ki18nc("Hebrew month 13 - KLocale::LongName", "Adar I").toString(languages);
0790     case 14:
0791         return ki18nc("Hebrew month 14 - KLocale::LongName", "Adar II").toString(languages);
0792     default:
0793         return QString();
0794     }
0795 }
0796 
0797 // Use Western day names for now as that's what the old version did,
0798 // but wouldn't it be better to use the right Hebrew names like Shabbat?
0799 // Could make it switchable by adding new enums to WeekDayFormat, e.g. ShortNameWestern?
0800 QString KCalendarSystemHebrewPrivate::weekDayName(int weekDay, KLocale::DateTimeComponentFormat format) const
0801 {
0802     QStringList languages = locale()->languageList();
0803 
0804     if (format == KLocale::NarrowName) {
0805         switch (weekDay) {
0806         case 1:
0807             return ki18nc("Gregorian weekday 1 - KLocale::NarrowName ", "M").toString(languages);
0808         case 2:
0809             return ki18nc("Gregorian weekday 2 - KLocale::NarrowName ", "T").toString(languages);
0810         case 3:
0811             return ki18nc("Gregorian weekday 3 - KLocale::NarrowName ", "W").toString(languages);
0812         case 4:
0813             return ki18nc("Gregorian weekday 4 - KLocale::NarrowName ", "T").toString(languages);
0814         case 5:
0815             return ki18nc("Gregorian weekday 5 - KLocale::NarrowName ", "F").toString(languages);
0816         case 6:
0817             return ki18nc("Gregorian weekday 6 - KLocale::NarrowName ", "S").toString(languages);
0818         case 7:
0819             return ki18nc("Gregorian weekday 7 - KLocale::NarrowName ", "S").toString(languages);
0820         default:
0821             return QString();
0822         }
0823     }
0824 
0825     if (format == KLocale::ShortName  || format == KLocale:: ShortNumber) {
0826         switch (weekDay) {
0827         case 1:
0828             return ki18nc("Gregorian weekday 1 - KLocale::ShortName", "Mon").toString(languages);
0829         case 2:
0830             return ki18nc("Gregorian weekday 2 - KLocale::ShortName", "Tue").toString(languages);
0831         case 3:
0832             return ki18nc("Gregorian weekday 3 - KLocale::ShortName", "Wed").toString(languages);
0833         case 4:
0834             return ki18nc("Gregorian weekday 4 - KLocale::ShortName", "Thu").toString(languages);
0835         case 5:
0836             return ki18nc("Gregorian weekday 5 - KLocale::ShortName", "Fri").toString(languages);
0837         case 6:
0838             return ki18nc("Gregorian weekday 6 - KLocale::ShortName", "Sat").toString(languages);
0839         case 7:
0840             return ki18nc("Gregorian weekday 7 - KLocale::ShortName", "Sun").toString(languages);
0841         default: return QString();
0842         }
0843     }
0844 
0845     switch (weekDay) {
0846     case 1:
0847         return ki18nc("Gregorian weekday 1 - KLocale::LongName", "Monday").toString(languages);
0848     case 2:
0849         return ki18nc("Gregorian weekday 2 - KLocale::LongName", "Tuesday").toString(languages);
0850     case 3:
0851         return ki18nc("Gregorian weekday 3 - KLocale::LongName", "Wednesday").toString(languages);
0852     case 4:
0853         return ki18nc("Gregorian weekday 4 - KLocale::LongName", "Thursday").toString(languages);
0854     case 5:
0855         return ki18nc("Gregorian weekday 5 - KLocale::LongName", "Friday").toString(languages);
0856     case 6:
0857         return ki18nc("Gregorian weekday 6 - KLocale::LongName", "Saturday").toString(languages);
0858     case 7:
0859         return ki18nc("Gregorian weekday 7 - KLocale::LongName", "Sunday").toString(languages);
0860     default:
0861         return QString();
0862     }
0863 }
0864 
0865 KCalendarSystemHebrew::KCalendarSystemHebrew(const KSharedConfig::Ptr config, const KLocale *locale)
0866     : KCalendarSystem(*new KCalendarSystemHebrewPrivate(this), config, locale)
0867 {
0868     d_ptr->loadConfig(calendarType());
0869 }
0870 
0871 KCalendarSystemHebrew::KCalendarSystemHebrew(KCalendarSystemHebrewPrivate &dd,
0872         const KSharedConfig::Ptr config, const KLocale *locale)
0873     : KCalendarSystem(dd, config, locale)
0874 {
0875     d_ptr->loadConfig(calendarType());
0876 }
0877 
0878 KCalendarSystemHebrew::~KCalendarSystemHebrew()
0879 {
0880 }
0881 
0882 QString KCalendarSystemHebrew::calendarType() const
0883 {
0884     return QLatin1String("hebrew");
0885 }
0886 
0887 KLocale::CalendarSystem KCalendarSystemHebrew::calendarSystem() const
0888 {
0889     return KLocale::HebrewCalendar;
0890 }
0891 
0892 QDate KCalendarSystemHebrew::epoch() const
0893 {
0894     // Hebrew 0001-01-01 (Gregorian -3760-09-07, Julian -3761-10-07)
0895     return QDate::fromJulianDay(347998);
0896 }
0897 
0898 QDate KCalendarSystemHebrew::earliestValidDate() const
0899 {
0900     // Current formulas using direct Gregorian <-> Hebrew conversion using Qt
0901     // will return invalid results prior to the Gregorian switchover in 1582
0902     // Next valid Hebrew year starts 5344-01-01 (Gregorian 1583-09-17)
0903     return QDate::fromJulianDay(2299498);
0904 }
0905 
0906 QDate KCalendarSystemHebrew::latestValidDate() const
0907 {
0908     // Testing shows current formulas only work up to 8119-13-29 (Gregorian 4359-10-07)
0909     return QDate::fromJulianDay(3313431);
0910 }
0911 
0912 QString KCalendarSystemHebrew::monthName(int month, int year, MonthNameFormat format) const
0913 {
0914     return KCalendarSystem::monthName(month, year, format);
0915 }
0916 
0917 QString KCalendarSystemHebrew::monthName(const QDate &date, MonthNameFormat format) const
0918 {
0919     return KCalendarSystem::monthName(date, format);
0920 }
0921 
0922 QString KCalendarSystemHebrew::weekDayName(int weekDay, WeekDayNameFormat format) const
0923 {
0924     return KCalendarSystem::weekDayName(weekDay, format);
0925 }
0926 
0927 QString KCalendarSystemHebrew::weekDayName(const QDate &date, WeekDayNameFormat format) const
0928 {
0929     return KCalendarSystem::weekDayName(date, format);
0930 }
0931 
0932 int KCalendarSystemHebrew::yearStringToInteger(const QString &string, int &readLength) const
0933 {
0934     int result = KCalendarSystem::yearStringToInteger(string, readLength);
0935 
0936     // Hebrew has no letter for 0, so 5 and 5000 are written the same
0937     // Assume if less than 10 then we are in an exact multiple of 1000
0938     if (result < 10) {
0939         result = result * 1000;
0940     }
0941 
0942     // Not good just assuming, make configurable
0943     if (result < 1000) {
0944         result += 5000; // assume we're in the 6th millenium (y6k bug)
0945     }
0946 
0947     return result;
0948 }
0949 
0950 bool KCalendarSystemHebrew::isLunar() const
0951 {
0952     return false;
0953 }
0954 
0955 bool KCalendarSystemHebrew::isLunisolar() const
0956 {
0957     return true;
0958 }
0959 
0960 bool KCalendarSystemHebrew::isSolar() const
0961 {
0962     return false;
0963 }
0964 
0965 bool KCalendarSystemHebrew::isProleptic() const
0966 {
0967     return false;
0968 }
0969 
0970 bool KCalendarSystemHebrew::julianDayToDate(qint64 jd, int &year, int &month, int &day) const
0971 {
0972     class h_date *sd = toHebrew(QDate::fromJulianDay(jd));
0973 
0974     year = sd->hd_year;
0975 
0976     month = sd->hd_mon;
0977     if (isLeapYear(sd->hd_year)) {
0978         if (month == 13 /*AdarI*/) {
0979             month = 6;
0980         } else if (month == 14 /*AdarII*/) {
0981             month = 7;
0982         } else if (month > 6 && month < 13) {
0983             ++month;
0984         }
0985     }
0986 
0987     day = sd->hd_day;
0988 
0989     return true;
0990 }
0991 
0992 bool KCalendarSystemHebrew::dateToJulianDay(int year, int month, int day, qint64 &jd) const
0993 {
0994     class h_date *gd = hebrewToGregorian(year, month, day);
0995 
0996     QDate tempDate(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
0997 
0998     jd = tempDate.toJulianDay();
0999 
1000     return true;
1001 }