summaryrefslogtreecommitdiffstats
path: root/kdecore/kcalendarsystemhebrew.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/kcalendarsystemhebrew.cpp')
-rw-r--r--kdecore/kcalendarsystemhebrew.cpp746
1 files changed, 746 insertions, 0 deletions
diff --git a/kdecore/kcalendarsystemhebrew.cpp b/kdecore/kcalendarsystemhebrew.cpp
new file mode 100644
index 000000000..694261eda
--- /dev/null
+++ b/kdecore/kcalendarsystemhebrew.cpp
@@ -0,0 +1,746 @@
+/*
+ Copyright (c) 2003 Hans Petter Bieker <bieker@kde.org>
+ Calendar conversion routines based on Hdate v6, by Amos
+ Shapir 1978 (rev. 1985, 1992)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// Derived hebrew kde calendar class
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kcalendarsystemhebrew.h"
+
+static int hebrewDaysElapsed(int y);
+static QString num2heb(int num, bool includeMillenium);
+
+class h_date
+{
+public:
+ int hd_day;
+ int hd_mon;
+ int hd_year;
+ int hd_dw;
+ int hd_flg;
+};
+
+/*
+ * compute general date structure from hebrew date
+ */
+static class h_date * hebrewToGregorian(int y, int m, int d)
+{
+ static class h_date h;
+ int s;
+
+ y -= 3744;
+ s = hebrewDaysElapsed(y);
+ d += s;
+ s = hebrewDaysElapsed(y + 1) - s; /* length of year */
+
+ if (s > 365 && m > 6 )
+ {
+ --m;
+ d += 30;
+ }
+ d += (59 * (m - 1) + 1) / 2; /* regular months */
+ /* special cases */
+ if (s % 10 > 4 && m > 2) /* long Heshvan */
+ d++;
+ if (s % 10 < 4 && m > 3) /* short Kislev */
+ d--;
+ // ### HPB: Broken in leap years
+ //if (s > 365 && m > 6) /* leap year */
+ // d += 30;
+ d -= 6002;
+
+ y = (d + 36525) * 4 / 146097 - 1;
+ d -= y / 4 * 146097 + (y % 4) * 36524;
+ y *= 100;
+
+ /* compute year */
+ s = (d + 366)*4/1461-1;
+ d -= s/4*1461 + (s % 4)*365;
+ y += s;
+ /* compute month */
+ m = (d + 245)*12/367-7;
+ d -= m*367/12-30;
+ if (++m >= 12) {
+ m -= 12;
+ y++;
+ }
+ h.hd_day = d;
+ h.hd_mon = m;
+ h.hd_year = y;
+ return(&h);
+}
+
+/*
+ * compute date structure from no. of days since 1 Tishrei 3744
+ */
+static class h_date * gregorianToHebrew(int y, int m, int d)
+{
+ static class h_date h;
+ int s;
+
+ if ((m -= 2) <= 0) {
+ m += 12;
+ y--;
+ }
+ /* no. of days, Julian calendar */
+ d += 365*y + y/4 + 367*m/12 + 5968;
+ /* Gregorian calendar */
+ d -= y/100-y/400-2;
+ h.hd_dw = (d + 1) % 7;
+
+ /* compute the year */
+ y += 16;
+ s = hebrewDaysElapsed(y);
+ m = hebrewDaysElapsed(y + 1);
+ while(d >= m) { /* computed year was underestimated */
+ s = m;
+ y++;
+ m = hebrewDaysElapsed(y + 1);
+ }
+ d -= s;
+ s = m-s; /* size of current year */
+ y += 3744;
+
+ h.hd_flg = s % 10-4;
+
+ /* compute day and month */
+ if (d >= s-236) { /* last 8 months are regular */
+ d -= s-236;
+ m = d*2/59;
+ d -= (m*59 + 1)/2;
+ m += 4;
+ if (s > 365 && m <= 5) /* Adar of Meuberet */
+ m += 8;
+ } else {
+ /* first 4 months have 117-119 days */
+ s = 114 + s % 10;
+ m = d * 4 / s;
+ d -= (m * s + 3) / 4;
+ }
+
+ h.hd_day = d;
+ h.hd_mon = m;
+ h.hd_year = y;
+ return(&h);
+}
+
+static QString num2heb(int num, bool includeMillenium)
+{
+ const QChar decade[] = {0x05D8, 0x05D9, 0x05DB, 0x05DC, 0x05DE,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6};
+ QString result;
+
+ if (num < 1 || num > 9999)
+ return QString::number(num);
+
+ if (num >= 1000) {
+ if (includeMillenium || num % 1000 == 0)
+ result += QChar(0x05D0 - 1 + num / 1000);
+ num %= 1000;
+ }
+ if (num >= 100) {
+ while (num >= 500) {
+ result += QChar(0x05EA);
+ num -= 400;
+ }
+ result += QChar(0x05E7 - 1 + num / 100);
+ num %= 100;
+ }
+ if (num >= 10) {
+ if (num == 15 || num == 16)
+ num -= 9;
+ result += decade[num / 10];
+ num %= 10;
+ }
+ if (num > 0)
+ result += QChar(0x05D0 - 1 + num);
+
+ if (result.length() == 1)
+ result += "'";
+ else
+ result.insert(result.length() - 1, '\"');
+
+ return result;
+}
+
+/* constants, in 1/18th of minute */
+static const int HOUR = 1080;
+static const int DAY = 24*HOUR;
+static const int WEEK = 7*DAY;
+#define M(h,p) ((h)*HOUR+p)
+#define MONTH (DAY+M(12,793))
+
+/**
+ * @internal
+ * no. of days in y years
+ */
+static int hebrewDaysElapsed(int y)
+{
+ int m, nm, dw, s, l;
+
+ l = y * 7 + 1; // no. of leap months
+ m = y*12+l/19; // total no. of months
+ l %= 19;
+ nm = m*MONTH+M(1+6,779); // molad new year 3744 (16BC) + 6 hours
+ s = m*28+nm/DAY-2;
+
+ nm %= WEEK;
+ dw = nm/DAY;
+ nm %= DAY;
+
+ // special cases of Molad Zaken
+ if (l < 12 && dw == 3 && nm >= M(9 + 6,204) ||
+ l < 7 && dw == 2 && nm>=M(15+6,589))
+ s++,dw++;
+ /* ADU */
+ if (dw == 1 || dw == 4 || dw == 6)
+ s++;
+ return s;
+}
+
+/**
+ * @internal
+ * true if long Cheshvan
+ */
+static int long_cheshvan(int year)
+{
+ QDate first, last;
+ class h_date *gd;
+
+ gd = hebrewToGregorian(year, 1, 1);
+ first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
+
+ gd = hebrewToGregorian(year + 1, 1, 1);
+ last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
+
+ return (first.daysTo(last) % 10 == 5);
+}
+
+/**
+ * @internal
+ * true if short Kislev
+ */
+static int short_kislev(int year)
+{
+ QDate first, last;
+ class h_date * gd;
+
+ gd = hebrewToGregorian(year, 1, 1);
+ first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
+
+ gd = hebrewToGregorian(year + 1, 1, 1);
+ last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
+
+ return (first.daysTo(last) % 10 == 3);
+}
+
+static bool is_leap_year(int year)
+{
+ return ((((7 * year) + 1) % 19) < 7);
+}
+
+// Ok
+KCalendarSystemHebrew::KCalendarSystemHebrew(const KLocale * locale)
+ : KCalendarSystem(locale)
+{
+}
+
+// Ok
+KCalendarSystemHebrew::~KCalendarSystemHebrew()
+{
+}
+
+// Ok
+static class h_date * toHebrew(const QDate & date)
+{
+ class h_date *sd;
+ sd = gregorianToHebrew(date.year(), date.month(), date.day());
+ ++sd->hd_mon;
+ ++sd->hd_day;
+ return sd;
+}
+
+// Ok
+int KCalendarSystemHebrew::year(const QDate& date) const
+{
+ class h_date *sd = toHebrew(date);
+ return sd->hd_year;
+}
+
+// Ok
+int KCalendarSystemHebrew::monthsInYear( const QDate & date ) const
+{
+ if ( is_leap_year( year(date) ) )
+ return 13;
+ else
+ return 12;
+}
+
+// Ok
+int KCalendarSystemHebrew::weeksInYear(int year) const
+{
+ QDate temp;
+ setYMD(temp, year, 1, 1); // don't pass an uninitialized QDate to
+ // monthsInYear in the next call
+ setYMD(temp, year, monthsInYear(temp), hndays(monthsInYear(temp), year) );
+
+ int nWeekNumber = weekNumber(temp);
+ if(nWeekNumber == 1) // last week belongs to next year
+ {
+ temp = temp.addDays(-7);
+ nWeekNumber = weekNumber(temp);
+ }
+
+ return nWeekNumber;
+}
+
+int KCalendarSystemHebrew::weekNumber(const QDate& date, int * yearNum) const
+{
+ QDate firstDayWeek1, lastDayOfYear;
+ int y = year(date);
+ int week;
+ int weekDay1, dayOfWeek1InYear;
+
+ // let's guess 1st day of 1st week
+ setYMD(firstDayWeek1, y, 1, 1);
+ weekDay1 = dayOfWeek(firstDayWeek1);
+
+ // iso 8601: week 1 is the first containing thursday and week starts on
+ // monday
+ if (weekDay1 > 4 /*Thursday*/)
+ firstDayWeek1 = addDays(firstDayWeek1 , 7 - weekDay1 + 1); // next monday
+
+ dayOfWeek1InYear = dayOfYear(firstDayWeek1);
+
+ if ( dayOfYear(date) < dayOfWeek1InYear ) // our date in prev year's week
+ {
+ if ( yearNum )
+ *yearNum = y - 1;
+ return weeksInYear(y - 1);
+ }
+
+ // let's check if its last week belongs to next year
+ setYMD(lastDayOfYear, y + 1, 1, 1);
+ lastDayOfYear = addDays(lastDayOfYear, -1);
+ if ( (dayOfYear(date) >= daysInYear(date) - dayOfWeek(lastDayOfYear) + 1)
+ // our date is in last week
+ && dayOfWeek(lastDayOfYear) < 4) // 1st week in next year has thursday
+ {
+ if ( yearNum )
+ *yearNum = y + 1;
+ week = 1;
+ }
+ else
+ {
+ if( weekDay1 < 5 ) // To calculate properly the number of weeks
+ // from day a to x let's make a day 1 of week
+ firstDayWeek1 = addDays( firstDayWeek1, -( weekDay1 - 1));
+
+ week = firstDayWeek1.daysTo(date) / 7 + 1;
+ }
+
+ return week;
+}
+
+// Ok
+QString KCalendarSystemHebrew::monthName(const QDate& date,
+ bool shortName) const
+{
+ return monthName(month(date), year(date), shortName);
+}
+
+// Ok
+QString KCalendarSystemHebrew::monthNamePossessive(const QDate& date,
+ bool shortName) const
+{
+ return monthNamePossessive(month(date), year(date), shortName);
+}
+
+// ### Fixme
+QString KCalendarSystemHebrew::monthName(int month, int year, bool /*shortName*/) const
+{
+ if ( month < 1 )
+ return QString::null;
+ if ( is_leap_year(year) )
+ {
+ if ( month > 13 )
+ return QString::null;
+ }
+ else if ( month > 12 )
+ return QString::null;
+
+ // We must map conversion algorithm month index to real index
+ if( month == 6 && is_leap_year(year) )
+ month = 13; /*Adar I*/
+ else if ( month == 7 && is_leap_year(year) )
+ month = 14; /*Adar II*/
+ else if ( month > 7 && is_leap_year(year) )
+ month--; //Because of Adar II
+
+ switch(month)
+ {
+ case 1:
+ return locale()->translate("Tishrey");
+ case 2:
+ return locale()->translate("Heshvan");
+ case 3:
+ return locale()->translate("Kislev");
+ case 4:
+ return locale()->translate("Tevet");
+ case 5:
+ return locale()->translate("Shvat");
+ case 6:
+ return locale()->translate("Adar");
+ case 7:
+ return locale()->translate("Nisan");
+ case 8:
+ return locale()->translate("Iyar");
+ case 9:
+ return locale()->translate("Sivan");
+ case 10:
+ return locale()->translate("Tamuz");
+ case 11:
+ return locale()->translate("Av");
+ case 12:
+ return locale()->translate("Elul");
+ case 13:
+ return locale()->translate("Adar I");
+ case 14:
+ return locale()->translate("Adar II");
+ default:
+ break;
+ }
+
+ return QString::null;
+}
+
+// ### Fixme
+QString KCalendarSystemHebrew::monthNamePossessive(int month, int year,
+ bool shortName) const
+{
+ return "of " + monthName(month, year, shortName);
+}
+
+bool KCalendarSystemHebrew::setYMD(QDate & date, int y, int m, int d) const
+{
+ if( y < minValidYear() || y > maxValidYear() )
+ return false;
+ if( m < 1 || m > (is_leap_year(y) ? 13 : 12) )
+ return false;
+ if( d < 1 || d > hndays(m,y) )
+ return false;
+
+ class h_date * gd = hebrewToGregorian( y, m, d );
+
+ return date.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
+}
+
+QString KCalendarSystemHebrew::weekDayName(int day, bool shortName) const
+{
+ return KCalendarSystem::weekDayName(day, shortName);
+}
+
+// Ok
+QString KCalendarSystemHebrew::weekDayName(const QDate& date,
+ bool shortName) const
+{
+ return weekDayName(dayOfWeek(date), shortName);
+}
+
+// Ok
+int KCalendarSystemHebrew::dayOfWeek(const QDate& date) const
+{
+ class h_date *sd = toHebrew(date);
+ if ( sd->hd_dw == 0 )
+ return 7;
+ else
+ return (sd->hd_dw);
+}
+
+// Ok
+int KCalendarSystemHebrew::dayOfYear(const QDate & date) const
+{
+ QDate first;
+ setYMD(first, year(date), 1, 1);
+
+ return first.daysTo(date) + 1;
+}
+
+int KCalendarSystemHebrew::daysInMonth(const QDate& date) const
+{
+ return hndays(month(date), year(date));
+}
+
+int KCalendarSystemHebrew::hndays(int mon, int year) const
+{
+ if ( mon == 6 && is_leap_year(year) )
+ mon = 13; /*Adar I*/
+ else if ( mon == 7 && is_leap_year(year) )
+ mon = 14; /*Adar II*/
+ else if ( mon > 7 && is_leap_year(year) )
+ mon--; //Because of Adar II
+
+ if( mon == 8 /*IYYAR*/ || mon == 10 /*TAMUZ*/ ||
+ mon == 12 /*ELUL*/ || mon == 4 /*TEVET*/ ||
+ mon == 14 /*ADAR 2*/||
+ ( mon == 6 /*ADAR*/ && !is_leap_year(year)) ||
+ (mon == 2 /*CHESHVAN*/ && !long_cheshvan(year)) ||
+ (mon == 3 /*KISLEV*/ && short_kislev(year)))
+ return 29;
+ else
+ return 30;
+}
+
+// Ok
+// Min valid year that may be converted to QDate
+int KCalendarSystemHebrew::minValidYear() const
+{
+ QDate date(1753, 1, 1);
+
+ return year(date);
+}
+
+// Ok
+// Max valid year that may be converted to QDate
+int KCalendarSystemHebrew::maxValidYear() const
+{
+ QDate date(8000, 1, 1);
+
+ return year(date);
+}
+
+// Ok
+int KCalendarSystemHebrew::day(const QDate& date) const
+{
+ class h_date *sd = toHebrew(date);
+
+ return sd->hd_day;
+}
+
+// Ok
+int KCalendarSystemHebrew::month(const QDate& date) const
+{
+ class h_date *sd = toHebrew(date);
+
+ int month = sd->hd_mon;
+ if ( is_leap_year( sd->hd_year ) )
+ {
+ if( month == 13 /*AdarI*/ )
+ month = 6;
+ else if( month == 14 /*AdarII*/ )
+ month = 7;
+ else if ( month > 6 && month < 13 )
+ ++month;
+ }
+
+ return month;
+}
+
+// Ok
+int KCalendarSystemHebrew::daysInYear(const QDate & date) const
+{
+ QDate first, last;
+ setYMD(first, year(date), 1, 1); // 1 Tishrey
+ setYMD(last, year(date) + 1, 1, 1); // 1 Tishrey the year later
+
+ return first.daysTo(last);
+}
+
+// Ok
+int KCalendarSystemHebrew::weekDayOfPray() const
+{
+ return 6; // saturday
+}
+
+// Ok
+QDate KCalendarSystemHebrew::addDays( const QDate & date, int ndays ) const
+{
+ return date.addDays( ndays );
+}
+
+// Ok
+QDate KCalendarSystemHebrew::addMonths( const QDate & date, int nmonths ) const
+{
+ QDate result = date;
+
+ while ( nmonths > 0 )
+ {
+ result = addDays(result, daysInMonth(result));
+ --nmonths;
+ }
+
+ while ( nmonths < 0 )
+ {
+ // get the number of days in the previous month to be consistent with
+ // addMonths where nmonths > 0
+ int nDaysInMonth = daysInMonth(addDays(result, -day(result)));
+ result = addDays(result, -nDaysInMonth);
+ ++nmonths;
+ }
+
+ return result;
+}
+
+// Ok
+QDate KCalendarSystemHebrew::addYears( const QDate & date, int nyears ) const
+{
+ QDate result = date;
+ int y = year(date) + nyears;
+
+ setYMD( result, y, month(date), day(date) );
+
+ return result;
+}
+
+// Ok
+QString KCalendarSystemHebrew::calendarName() const
+{
+ return QString::fromLatin1("hebrew");
+}
+
+// Ok
+bool KCalendarSystemHebrew::isLunar() const
+{
+ return false;
+}
+
+// Ok
+bool KCalendarSystemHebrew::isLunisolar() const
+{
+ return true;
+}
+
+// Ok
+bool KCalendarSystemHebrew::isSolar() const
+{
+ return false;
+}
+
+QString KCalendarSystemHebrew::dayString(const QDate & pDate, bool bShort) const
+{
+ QString sResult;
+
+ // Only use hebrew numbers if the hebrew setting is selected
+ if (locale()->language() == QString::fromLatin1("he"))
+ sResult = num2heb(day(pDate), false);
+ else
+ sResult = KCalendarSystem::dayString(pDate, bShort);
+
+ return sResult;
+}
+
+QString KCalendarSystemHebrew::yearString(const QDate & pDate, bool bShort) const
+{
+ QString sResult;
+
+ // Only use hebrew numbers if the hebrew setting is selected
+ if (locale()->language() == QString::fromLatin1("he"))
+ sResult = num2heb(year(pDate), !bShort);
+ else
+ sResult = KCalendarSystem::yearString(pDate, bShort);
+
+ return sResult;
+}
+
+static int heb2num(const QString& str, int & iLength) {
+ QChar c;
+ QString s = str;
+ int result = 0;
+ iLength = 0;
+ int decadeValues[14] = {10, 20, 20, 30, 40, 40, 50,
+ 50, 60, 70, 80, 80, 90, 90};
+
+ uint pos;
+ for (pos = 0 ; pos < s.length() ; pos++)
+ {
+ c = s[pos];
+ if (s.length() > pos && (s[pos + 1] == QChar('\'') ||
+ s[pos + 1] == QChar('\"')))
+ {
+ iLength++;
+ s.remove(pos + 1, 1);
+ }
+
+ if (c >= QChar(0x05D0) && c <= QChar(0x05D7))
+ {
+ if (s.length() > pos && s[pos + 1] >= QChar(0x05D0) &&
+ s[pos + 1] <= QChar(0x05EA))
+ result += (c.unicode() - 0x05D0 + 1) * 1000;
+ else
+ result += c.unicode() - 0x05D0 + 1;
+ }
+ else if (c == QChar(0x05D8))
+ {
+ if (s.length() > pos && s[pos + 1] >= QChar(0x05D0) &&
+ s[pos + 1] <= QChar(0x05EA) && s[pos + 1] != QChar(0x05D5) &&
+ s[pos + 1] != QChar(0x05D6))
+ result += 9000;
+ else
+ result += 9;
+ }
+ else if (c >= QChar(0x05D9) && c <= QChar(0x05E6))
+ {
+ if (s.length() > pos && s[pos + 1] >= QChar(0x05D9))
+ return -1;
+ else
+ result += decadeValues[c.unicode() - 0x05D9];
+ }
+ else if (c >= QChar(0x05E7) && c <= QChar(0x05EA))
+ {
+ result += (c.unicode() - 0x05E7 + 1) * 100;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ iLength += pos;
+
+ return result;
+}
+
+int KCalendarSystemHebrew::dayStringToInteger(const QString & sNum, int & iLength) const
+{
+ int iResult;
+ if (locale()->language() == "he")
+ iResult= heb2num(sNum, iLength);
+ else
+ iResult = KCalendarSystem::yearStringToInteger(sNum, iLength);
+
+ return iResult;
+}
+
+int KCalendarSystemHebrew::yearStringToInteger(const QString & sNum, int & iLength) const
+{
+ int iResult;
+ if (locale()->language() == "he")
+ iResult = heb2num(sNum, iLength);
+ else
+ iResult = KCalendarSystem::yearStringToInteger(sNum, iLength);
+
+ if (iResult < 1000)
+ iResult += 5000; // assume we're in the 6th millenium (y6k bug)
+
+ return iResult;
+}
+