kcalendarsystemhebrew.cpp
00001 /* 00002 Copyright (c) 2003 Hans Petter Bieker <bieker@kde.org> 00003 Calendar conversion routines based on Hdate v6, by Amos 00004 Shapir 1978 (rev. 1985, 1992) 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 // Derived hebrew kde calendar class 00023 00024 #include <klocale.h> 00025 #include <kdebug.h> 00026 00027 #include "kcalendarsystemhebrew.h" 00028 00029 static int hebrewDaysElapsed(int y); 00030 static TQString num2heb(int num, bool includeMillenium); 00031 00032 class h_date 00033 { 00034 public: 00035 int hd_day; 00036 int hd_mon; 00037 int hd_year; 00038 int hd_dw; 00039 int hd_flg; 00040 }; 00041 00042 /* 00043 * compute general date structure from hebrew date 00044 */ 00045 static class h_date * hebrewToGregorian(int y, int m, int d) 00046 { 00047 static class h_date h; 00048 int s; 00049 00050 y -= 3744; 00051 s = hebrewDaysElapsed(y); 00052 d += s; 00053 s = hebrewDaysElapsed(y + 1) - s; /* length of year */ 00054 00055 if (s > 365 && m > 6 ) 00056 { 00057 --m; 00058 d += 30; 00059 } 00060 d += (59 * (m - 1) + 1) / 2; /* regular months */ 00061 /* special cases */ 00062 if (s % 10 > 4 && m > 2) /* long Heshvan */ 00063 d++; 00064 if (s % 10 < 4 && m > 3) /* short Kislev */ 00065 d--; 00066 // ### HPB: Broken in leap years 00067 //if (s > 365 && m > 6) /* leap year */ 00068 // d += 30; 00069 d -= 6002; 00070 00071 y = (d + 36525) * 4 / 146097 - 1; 00072 d -= y / 4 * 146097 + (y % 4) * 36524; 00073 y *= 100; 00074 00075 /* compute year */ 00076 s = (d + 366)*4/1461-1; 00077 d -= s/4*1461 + (s % 4)*365; 00078 y += s; 00079 /* compute month */ 00080 m = (d + 245)*12/367-7; 00081 d -= m*367/12-30; 00082 if (++m >= 12) { 00083 m -= 12; 00084 y++; 00085 } 00086 h.hd_day = d; 00087 h.hd_mon = m; 00088 h.hd_year = y; 00089 return(&h); 00090 } 00091 00092 /* 00093 * compute date structure from no. of days since 1 Tishrei 3744 00094 */ 00095 static class h_date * gregorianToHebrew(int y, int m, int d) 00096 { 00097 static class h_date h; 00098 int s; 00099 00100 if ((m -= 2) <= 0) { 00101 m += 12; 00102 y--; 00103 } 00104 /* no. of days, Julian calendar */ 00105 d += 365*y + y/4 + 367*m/12 + 5968; 00106 /* Gregorian calendar */ 00107 d -= y/100-y/400-2; 00108 h.hd_dw = (d + 1) % 7; 00109 00110 /* compute the year */ 00111 y += 16; 00112 s = hebrewDaysElapsed(y); 00113 m = hebrewDaysElapsed(y + 1); 00114 while(d >= m) { /* computed year was underestimated */ 00115 s = m; 00116 y++; 00117 m = hebrewDaysElapsed(y + 1); 00118 } 00119 d -= s; 00120 s = m-s; /* size of current year */ 00121 y += 3744; 00122 00123 h.hd_flg = s % 10-4; 00124 00125 /* compute day and month */ 00126 if (d >= s-236) { /* last 8 months are regular */ 00127 d -= s-236; 00128 m = d*2/59; 00129 d -= (m*59 + 1)/2; 00130 m += 4; 00131 if (s > 365 && m <= 5) /* Adar of Meuberet */ 00132 m += 8; 00133 } else { 00134 /* first 4 months have 117-119 days */ 00135 s = 114 + s % 10; 00136 m = d * 4 / s; 00137 d -= (m * s + 3) / 4; 00138 } 00139 00140 h.hd_day = d; 00141 h.hd_mon = m; 00142 h.hd_year = y; 00143 return(&h); 00144 } 00145 00146 static TQString num2heb(int num, bool includeMillenium) 00147 { 00148 const TQChar decade[] = {0x05D8, 0x05D9, 0x05DB, 0x05DC, 0x05DE, 00149 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6}; 00150 TQString result; 00151 00152 if (num < 1 || num > 9999) 00153 return TQString::number(num); 00154 00155 if (num >= 1000) { 00156 if (includeMillenium || num % 1000 == 0) 00157 result += TQChar(0x05D0 - 1 + num / 1000); 00158 num %= 1000; 00159 } 00160 if (num >= 100) { 00161 while (num >= 500) { 00162 result += TQChar(0x05EA); 00163 num -= 400; 00164 } 00165 result += TQChar(0x05E7 - 1 + num / 100); 00166 num %= 100; 00167 } 00168 if (num >= 10) { 00169 if (num == 15 || num == 16) 00170 num -= 9; 00171 result += decade[num / 10]; 00172 num %= 10; 00173 } 00174 if (num > 0) 00175 result += TQChar(0x05D0 - 1 + num); 00176 00177 if (result.length() == 1) 00178 result += "'"; 00179 else 00180 result.insert(result.length() - 1, '\"'); 00181 00182 return result; 00183 } 00184 00185 /* constants, in 1/18th of minute */ 00186 static const int HOUR = 1080; 00187 static const int DAY = 24*HOUR; 00188 static const int WEEK = 7*DAY; 00189 #define M(h,p) ((h)*HOUR+p) 00190 #define MONTH (DAY+M(12,793)) 00191 00196 static int hebrewDaysElapsed(int y) 00197 { 00198 int m, nm, dw, s, l; 00199 00200 l = y * 7 + 1; // no. of leap months 00201 m = y*12+l/19; // total no. of months 00202 l %= 19; 00203 nm = m*MONTH+M(1+6,779); // molad new year 3744 (16BC) + 6 hours 00204 s = m*28+nm/DAY-2; 00205 00206 nm %= WEEK; 00207 dw = nm/DAY; 00208 nm %= DAY; 00209 00210 // special cases of Molad Zaken 00211 if (l < 12 && dw == 3 && nm >= M(9 + 6,204) || 00212 l < 7 && dw == 2 && nm>=M(15+6,589)) 00213 s++,dw++; 00214 /* ADU */ 00215 if (dw == 1 || dw == 4 || dw == 6) 00216 s++; 00217 return s; 00218 } 00219 00224 static int long_cheshvan(int year) 00225 { 00226 TQDate first, last; 00227 class h_date *gd; 00228 00229 gd = hebrewToGregorian(year, 1, 1); 00230 first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1); 00231 00232 gd = hebrewToGregorian(year + 1, 1, 1); 00233 last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1); 00234 00235 return (first.daysTo(last) % 10 == 5); 00236 } 00237 00242 static int short_kislev(int year) 00243 { 00244 TQDate first, last; 00245 class h_date * gd; 00246 00247 gd = hebrewToGregorian(year, 1, 1); 00248 first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1); 00249 00250 gd = hebrewToGregorian(year + 1, 1, 1); 00251 last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1); 00252 00253 return (first.daysTo(last) % 10 == 3); 00254 } 00255 00256 static bool is_leap_year(int year) 00257 { 00258 return ((((7 * year) + 1) % 19) < 7); 00259 } 00260 00261 // Ok 00262 KCalendarSystemHebrew::KCalendarSystemHebrew(const KLocale * locale) 00263 : KCalendarSystem(locale) 00264 { 00265 } 00266 00267 // Ok 00268 KCalendarSystemHebrew::~KCalendarSystemHebrew() 00269 { 00270 } 00271 00272 // Ok 00273 static class h_date * toHebrew(const TQDate & date) 00274 { 00275 class h_date *sd; 00276 sd = gregorianToHebrew(date.year(), date.month(), date.day()); 00277 ++sd->hd_mon; 00278 ++sd->hd_day; 00279 return sd; 00280 } 00281 00282 // Ok 00283 int KCalendarSystemHebrew::year(const TQDate& date) const 00284 { 00285 class h_date *sd = toHebrew(date); 00286 return sd->hd_year; 00287 } 00288 00289 // Ok 00290 int KCalendarSystemHebrew::monthsInYear( const TQDate & date ) const 00291 { 00292 if ( is_leap_year( year(date) ) ) 00293 return 13; 00294 else 00295 return 12; 00296 } 00297 00298 // Ok 00299 int KCalendarSystemHebrew::weeksInYear(int year) const 00300 { 00301 TQDate temp; 00302 setYMD(temp, year, 1, 1); // don't pass an uninitialized TQDate to 00303 // monthsInYear in the next call 00304 setYMD(temp, year, monthsInYear(temp), hndays(monthsInYear(temp), year) ); 00305 00306 int nWeekNumber = weekNumber(temp); 00307 if(nWeekNumber == 1) // last week belongs to next year 00308 { 00309 temp = TQT_TQDATE_OBJECT(temp.addDays(-7)); 00310 nWeekNumber = weekNumber(temp); 00311 } 00312 00313 return nWeekNumber; 00314 } 00315 00316 int KCalendarSystemHebrew::weekNumber(const TQDate& date, int * yearNum) const 00317 { 00318 TQDate firstDayWeek1, lastDayOfYear; 00319 int y = year(date); 00320 int week; 00321 int weekDay1, dayOfWeek1InYear; 00322 00323 // let's guess 1st day of 1st week 00324 setYMD(firstDayWeek1, y, 1, 1); 00325 weekDay1 = dayOfWeek(firstDayWeek1); 00326 00327 // iso 8601: week 1 is the first containing thursday and week starts on 00328 // monday 00329 if (weekDay1 > 4 /*Thursday*/) 00330 firstDayWeek1 = addDays(firstDayWeek1 , 7 - weekDay1 + 1); // next monday 00331 00332 dayOfWeek1InYear = dayOfYear(firstDayWeek1); 00333 00334 if ( dayOfYear(date) < dayOfWeek1InYear ) // our date in prev year's week 00335 { 00336 if ( yearNum ) 00337 *yearNum = y - 1; 00338 return weeksInYear(y - 1); 00339 } 00340 00341 // let's check if its last week belongs to next year 00342 setYMD(lastDayOfYear, y + 1, 1, 1); 00343 lastDayOfYear = addDays(lastDayOfYear, -1); 00344 if ( (dayOfYear(date) >= daysInYear(date) - dayOfWeek(lastDayOfYear) + 1) 00345 // our date is in last week 00346 && dayOfWeek(lastDayOfYear) < 4) // 1st week in next year has thursday 00347 { 00348 if ( yearNum ) 00349 *yearNum = y + 1; 00350 week = 1; 00351 } 00352 else 00353 { 00354 if( weekDay1 < 5 ) // To calculate properly the number of weeks 00355 // from day a to x let's make a day 1 of week 00356 firstDayWeek1 = addDays( firstDayWeek1, -( weekDay1 - 1)); 00357 00358 week = firstDayWeek1.daysTo(date) / 7 + 1; 00359 } 00360 00361 return week; 00362 } 00363 00364 // Ok 00365 TQString KCalendarSystemHebrew::monthName(const TQDate& date, 00366 bool shortName) const 00367 { 00368 return monthName(month(date), year(date), shortName); 00369 } 00370 00371 // Ok 00372 TQString KCalendarSystemHebrew::monthNamePossessive(const TQDate& date, 00373 bool shortName) const 00374 { 00375 return monthNamePossessive(month(date), year(date), shortName); 00376 } 00377 00378 // ### Fixme 00379 TQString KCalendarSystemHebrew::monthName(int month, int year, bool /*shortName*/) const 00380 { 00381 if ( month < 1 ) 00382 return TQString::null; 00383 if ( is_leap_year(year) ) 00384 { 00385 if ( month > 13 ) 00386 return TQString::null; 00387 } 00388 else if ( month > 12 ) 00389 return TQString::null; 00390 00391 // We must map conversion algorithm month index to real index 00392 if( month == 6 && is_leap_year(year) ) 00393 month = 13; /*Adar I*/ 00394 else if ( month == 7 && is_leap_year(year) ) 00395 month = 14; /*Adar II*/ 00396 else if ( month > 7 && is_leap_year(year) ) 00397 month--; //Because of Adar II 00398 00399 switch(month) 00400 { 00401 case 1: 00402 return locale()->translate("Tishrey"); 00403 case 2: 00404 return locale()->translate("Heshvan"); 00405 case 3: 00406 return locale()->translate("Kislev"); 00407 case 4: 00408 return locale()->translate("Tevet"); 00409 case 5: 00410 return locale()->translate("Shvat"); 00411 case 6: 00412 return locale()->translate("Adar"); 00413 case 7: 00414 return locale()->translate("Nisan"); 00415 case 8: 00416 return locale()->translate("Iyar"); 00417 case 9: 00418 return locale()->translate("Sivan"); 00419 case 10: 00420 return locale()->translate("Tamuz"); 00421 case 11: 00422 return locale()->translate("Av"); 00423 case 12: 00424 return locale()->translate("Elul"); 00425 case 13: 00426 return locale()->translate("Adar I"); 00427 case 14: 00428 return locale()->translate("Adar II"); 00429 default: 00430 break; 00431 } 00432 00433 return TQString::null; 00434 } 00435 00436 // ### Fixme 00437 TQString KCalendarSystemHebrew::monthNamePossessive(int month, int year, 00438 bool shortName) const 00439 { 00440 return "of " + monthName(month, year, shortName); 00441 } 00442 00443 bool KCalendarSystemHebrew::setYMD(TQDate & date, int y, int m, int d) const 00444 { 00445 if( y < minValidYear() || y > maxValidYear() ) 00446 return false; 00447 if( m < 1 || m > (is_leap_year(y) ? 13 : 12) ) 00448 return false; 00449 if( d < 1 || d > hndays(m,y) ) 00450 return false; 00451 00452 class h_date * gd = hebrewToGregorian( y, m, d ); 00453 00454 return date.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1); 00455 } 00456 00457 TQString KCalendarSystemHebrew::weekDayName(int day, bool shortName) const 00458 { 00459 return KCalendarSystem::weekDayName(day, shortName); 00460 } 00461 00462 // Ok 00463 TQString KCalendarSystemHebrew::weekDayName(const TQDate& date, 00464 bool shortName) const 00465 { 00466 return weekDayName(dayOfWeek(date), shortName); 00467 } 00468 00469 // Ok 00470 int KCalendarSystemHebrew::dayOfWeek(const TQDate& date) const 00471 { 00472 class h_date *sd = toHebrew(date); 00473 if ( sd->hd_dw == 0 ) 00474 return 7; 00475 else 00476 return (sd->hd_dw); 00477 } 00478 00479 // Ok 00480 int KCalendarSystemHebrew::dayOfYear(const TQDate & date) const 00481 { 00482 TQDate first; 00483 setYMD(first, year(date), 1, 1); 00484 00485 return first.daysTo(date) + 1; 00486 } 00487 00488 int KCalendarSystemHebrew::daysInMonth(const TQDate& date) const 00489 { 00490 return hndays(month(date), year(date)); 00491 } 00492 00493 int KCalendarSystemHebrew::hndays(int mon, int year) const 00494 { 00495 if ( mon == 6 && is_leap_year(year) ) 00496 mon = 13; /*Adar I*/ 00497 else if ( mon == 7 && is_leap_year(year) ) 00498 mon = 14; /*Adar II*/ 00499 else if ( mon > 7 && is_leap_year(year) ) 00500 mon--; //Because of Adar II 00501 00502 if( mon == 8 /*IYYAR*/ || mon == 10 /*TAMUZ*/ || 00503 mon == 12 /*ELUL*/ || mon == 4 /*TEVET*/ || 00504 mon == 14 /*ADAR 2*/|| 00505 ( mon == 6 /*ADAR*/ && !is_leap_year(year)) || 00506 (mon == 2 /*CHESHVAN*/ && !long_cheshvan(year)) || 00507 (mon == 3 /*KISLEV*/ && short_kislev(year))) 00508 return 29; 00509 else 00510 return 30; 00511 } 00512 00513 // Ok 00514 // Min valid year that may be converted to QDate 00515 int KCalendarSystemHebrew::minValidYear() const 00516 { 00517 TQDate date(1753, 1, 1); 00518 00519 return year(date); 00520 } 00521 00522 // Ok 00523 // Max valid year that may be converted to QDate 00524 int KCalendarSystemHebrew::maxValidYear() const 00525 { 00526 TQDate date(8000, 1, 1); 00527 00528 return year(date); 00529 } 00530 00531 // Ok 00532 int KCalendarSystemHebrew::day(const TQDate& date) const 00533 { 00534 class h_date *sd = toHebrew(date); 00535 00536 return sd->hd_day; 00537 } 00538 00539 // Ok 00540 int KCalendarSystemHebrew::month(const TQDate& date) const 00541 { 00542 class h_date *sd = toHebrew(date); 00543 00544 int month = sd->hd_mon; 00545 if ( is_leap_year( sd->hd_year ) ) 00546 { 00547 if( month == 13 /*AdarI*/ ) 00548 month = 6; 00549 else if( month == 14 /*AdarII*/ ) 00550 month = 7; 00551 else if ( month > 6 && month < 13 ) 00552 ++month; 00553 } 00554 00555 return month; 00556 } 00557 00558 // Ok 00559 int KCalendarSystemHebrew::daysInYear(const TQDate & date) const 00560 { 00561 TQDate first, last; 00562 setYMD(first, year(date), 1, 1); // 1 Tishrey 00563 setYMD(last, year(date) + 1, 1, 1); // 1 Tishrey the year later 00564 00565 return first.daysTo(last); 00566 } 00567 00568 // Ok 00569 int KCalendarSystemHebrew::weekDayOfPray() const 00570 { 00571 return 6; // saturday 00572 } 00573 00574 // Ok 00575 TQDate KCalendarSystemHebrew::addDays( const TQDate & date, int ndays ) const 00576 { 00577 return TQT_TQDATE_OBJECT(date.addDays( ndays )); 00578 } 00579 00580 // Ok 00581 TQDate KCalendarSystemHebrew::addMonths( const TQDate & date, int nmonths ) const 00582 { 00583 TQDate result = date; 00584 00585 while ( nmonths > 0 ) 00586 { 00587 result = addDays(result, daysInMonth(result)); 00588 --nmonths; 00589 } 00590 00591 while ( nmonths < 0 ) 00592 { 00593 // get the number of days in the previous month to be consistent with 00594 // addMonths where nmonths > 0 00595 int nDaysInMonth = daysInMonth(addDays(result, -day(result))); 00596 result = addDays(result, -nDaysInMonth); 00597 ++nmonths; 00598 } 00599 00600 return result; 00601 } 00602 00603 // Ok 00604 TQDate KCalendarSystemHebrew::addYears( const TQDate & date, int nyears ) const 00605 { 00606 TQDate result = date; 00607 int y = year(date) + nyears; 00608 00609 setYMD( result, y, month(date), day(date) ); 00610 00611 return result; 00612 } 00613 00614 // Ok 00615 TQString KCalendarSystemHebrew::calendarName() const 00616 { 00617 return TQString::fromLatin1("hebrew"); 00618 } 00619 00620 // Ok 00621 bool KCalendarSystemHebrew::isLunar() const 00622 { 00623 return false; 00624 } 00625 00626 // Ok 00627 bool KCalendarSystemHebrew::isLunisolar() const 00628 { 00629 return true; 00630 } 00631 00632 // Ok 00633 bool KCalendarSystemHebrew::isSolar() const 00634 { 00635 return false; 00636 } 00637 00638 TQString KCalendarSystemHebrew::dayString(const TQDate & pDate, bool bShort) const 00639 { 00640 TQString sResult; 00641 00642 // Only use hebrew numbers if the hebrew setting is selected 00643 if (locale()->language() == TQString::fromLatin1("he")) 00644 sResult = num2heb(day(pDate), false); 00645 else 00646 sResult = KCalendarSystem::dayString(pDate, bShort); 00647 00648 return sResult; 00649 } 00650 00651 TQString KCalendarSystemHebrew::yearString(const TQDate & pDate, bool bShort) const 00652 { 00653 TQString sResult; 00654 00655 // Only use hebrew numbers if the hebrew setting is selected 00656 if (locale()->language() == TQString::fromLatin1("he")) 00657 sResult = num2heb(year(pDate), !bShort); 00658 else 00659 sResult = KCalendarSystem::yearString(pDate, bShort); 00660 00661 return sResult; 00662 } 00663 00664 static int heb2num(const TQString& str, int & iLength) { 00665 TQChar c; 00666 TQString s = str; 00667 int result = 0; 00668 iLength = 0; 00669 int decadeValues[14] = {10, 20, 20, 30, 40, 40, 50, 00670 50, 60, 70, 80, 80, 90, 90}; 00671 00672 uint pos; 00673 for (pos = 0 ; pos < s.length() ; pos++) 00674 { 00675 c = s[pos]; 00676 if (s.length() > pos && (s[pos + 1] == TQChar('\'') || 00677 s[pos + 1] == TQChar('\"'))) 00678 { 00679 iLength++; 00680 s.remove(pos + 1, 1); 00681 } 00682 00683 if (c >= TQChar(0x05D0) && c <= TQChar(0x05D7)) 00684 { 00685 if (s.length() > pos && s[pos + 1] >= TQChar(0x05D0) && 00686 s[pos + 1] <= TQChar(0x05EA)) 00687 result += (c.unicode() - 0x05D0 + 1) * 1000; 00688 else 00689 result += c.unicode() - 0x05D0 + 1; 00690 } 00691 else if (c == TQChar(0x05D8)) 00692 { 00693 if (s.length() > pos && s[pos + 1] >= TQChar(0x05D0) && 00694 s[pos + 1] <= TQChar(0x05EA) && s[pos + 1] != TQChar(0x05D5) && 00695 s[pos + 1] != TQChar(0x05D6)) 00696 result += 9000; 00697 else 00698 result += 9; 00699 } 00700 else if (c >= TQChar(0x05D9) && c <= TQChar(0x05E6)) 00701 { 00702 if (s.length() > pos && s[pos + 1] >= TQChar(0x05D9)) 00703 return -1; 00704 else 00705 result += decadeValues[c.unicode() - 0x05D9]; 00706 } 00707 else if (c >= TQChar(0x05E7) && c <= TQChar(0x05EA)) 00708 { 00709 result += (c.unicode() - 0x05E7 + 1) * 100; 00710 } 00711 else 00712 { 00713 break; 00714 } 00715 } 00716 00717 iLength += pos; 00718 00719 return result; 00720 } 00721 00722 int KCalendarSystemHebrew::dayStringToInteger(const TQString & sNum, int & iLength) const 00723 { 00724 int iResult; 00725 if (locale()->language() == "he") 00726 iResult= heb2num(sNum, iLength); 00727 else 00728 iResult = KCalendarSystem::yearStringToInteger(sNum, iLength); 00729 00730 return iResult; 00731 } 00732 00733 int KCalendarSystemHebrew::yearStringToInteger(const TQString & sNum, int & iLength) const 00734 { 00735 int iResult; 00736 if (locale()->language() == "he") 00737 iResult = heb2num(sNum, iLength); 00738 else 00739 iResult = KCalendarSystem::yearStringToInteger(sNum, iLength); 00740 00741 if (iResult < 1000) 00742 iResult += 5000; // assume we're in the 6th millenium (y6k bug) 00743 00744 return iResult; 00745 } 00746