2
Copyright (c) 2003 Hans Petter Bieker <bieker@kde.org>
3
Calendar conversion routines based on Hdate v6, by Amos
4
Shapir 1978 (rev. 1985, 1992)
6
This library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public
8
License as published by the Free Software Foundation; either
9
version 2 of the License, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public License
17
along with this library; see the file COPYING.LIB. If not, write to
18
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
Boston, MA 02110-1301, USA.
22
// Derived hebrew kde calendar class
24
#include "kcalendarsystemhebrew.h"
29
#include <qdatetime.h>
32
static int hebrewDaysElapsed(int y);
33
static QString num2heb(int num, bool includeMillenium);
46
* compute general date structure from hebrew date
48
static class h_date * hebrewToGregorian(int y, int m, int d)
50
static class h_date h;
54
s = hebrewDaysElapsed(y);
56
s = hebrewDaysElapsed(y + 1) - s; /* length of year */
58
if (s > 365 && m > 6 )
63
d += (59 * (m - 1) + 1) / 2; /* regular months */
65
if (s % 10 > 4 && m > 2) /* long Heshvan */
67
if (s % 10 < 4 && m > 3) /* short Kislev */
69
// ### HPB: Broken in leap years
70
//if (s > 365 && m > 6) /* leap year */
74
y = (d + 36525) * 4 / 146097 - 1;
75
d -= y / 4 * 146097 + (y % 4) * 36524;
79
s = (d + 366)*4/1461-1;
80
d -= s/4*1461 + (s % 4)*365;
83
m = (d + 245)*12/367-7;
96
* compute date structure from no. of days since 1 Tishrei 3744
98
static class h_date * gregorianToHebrew(int y, int m, int d)
100
static class h_date h;
107
/* no. of days, Julian calendar */
108
d += 365*y + y/4 + 367*m/12 + 5968;
109
/* Gregorian calendar */
111
h.hd_dw = (d + 1) % 7;
113
/* compute the year */
115
s = hebrewDaysElapsed(y);
116
m = hebrewDaysElapsed(y + 1);
117
while(d >= m) { /* computed year was underestimated */
120
m = hebrewDaysElapsed(y + 1);
123
s = m-s; /* size of current year */
128
/* compute day and month */
129
if (d >= s-236) { /* last 8 months are regular */
134
if (s > 365 && m <= 5) /* Adar of Meuberet */
137
/* first 4 months have 117-119 days */
140
d -= (m * s + 3) / 4;
149
static QString num2heb(int num, bool includeMillenium)
151
const QChar decade[] = {0x05D8, 0x05D9, 0x05DB, 0x05DC, 0x05DE,
152
0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6};
155
if (num < 1 || num > 9999)
156
return QString::number(num);
159
if (includeMillenium || num % 1000 == 0)
160
result += QChar(0x05D0 - 1 + num / 1000);
165
result += QChar(0x05EA);
168
result += QChar(0x05E7 - 1 + num / 100);
172
if (num == 15 || num == 16)
174
result += decade[num / 10];
178
result += QChar(0x05D0 - 1 + num);
180
if (result.length() == 1)
183
result.insert(result.length() - 1, '\"');
188
/* constants, in 1/18th of minute */
189
static const int HOUR = 1080;
190
static const int DAY = 24*HOUR;
191
static const int WEEK = 7*DAY;
192
#define M(h,p) ((h)*HOUR+p)
193
#define MONTH (DAY+M(12,793))
197
* no. of days in y years
199
static int hebrewDaysElapsed(int y)
203
l = y * 7 + 1; // no. of leap months
204
m = y*12+l/19; // total no. of months
206
nm = m*MONTH+M(1+6,779); // molad new year 3744 (16BC) + 6 hours
213
// special cases of Molad Zaken
214
if (l < 12 && dw == 3 && nm >= M(9 + 6,204) ||
215
l < 7 && dw == 2 && nm>=M(15+6,589))
218
if (dw == 1 || dw == 4 || dw == 6)
225
* true if long Cheshvan
227
static int long_cheshvan(int year)
232
gd = hebrewToGregorian(year, 1, 1);
233
first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
235
gd = hebrewToGregorian(year + 1, 1, 1);
236
last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
238
return (first.daysTo(last) % 10 == 5);
243
* true if short Kislev
245
static int short_kislev(int year)
250
gd = hebrewToGregorian(year, 1, 1);
251
first.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
253
gd = hebrewToGregorian(year + 1, 1, 1);
254
last.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
256
return (first.daysTo(last) % 10 == 3);
259
static bool is_leap_year(int year)
261
return ((((7 * year) + 1) % 19) < 7);
265
KCalendarSystemHebrew::KCalendarSystemHebrew(const KLocale * locale)
266
: KCalendarSystem(locale)
271
KCalendarSystemHebrew::~KCalendarSystemHebrew()
276
static class h_date * toHebrew(const QDate & date)
279
sd = gregorianToHebrew(date.year(), date.month(), date.day());
286
int KCalendarSystemHebrew::year(const QDate& date) const
288
class h_date *sd = toHebrew(date);
293
int KCalendarSystemHebrew::monthsInYear( const QDate & date ) const
295
if ( is_leap_year( year(date) ) )
302
int KCalendarSystemHebrew::weeksInYear(int year) const
305
setYMD(temp, year, 1, 1); // don't pass an uninitialized QDate to
306
// monthsInYear in the next call
307
setYMD(temp, year, monthsInYear(temp), hndays(monthsInYear(temp), year) );
309
int nWeekNumber = weekNumber(temp);
310
if(nWeekNumber == 1) // last week belongs to next year
312
temp = temp.addDays(-7);
313
nWeekNumber = weekNumber(temp);
319
int KCalendarSystemHebrew::weekNumber(const QDate& date, int * yearNum) const
321
QDate firstDayWeek1, lastDayOfYear;
324
int weekDay1, dayOfWeek1InYear;
326
// let's guess 1st day of 1st week
327
setYMD(firstDayWeek1, y, 1, 1);
328
weekDay1 = dayOfWeek(firstDayWeek1);
330
// iso 8601: week 1 is the first containing thursday and week starts on
332
if (weekDay1 > 4 /*Thursday*/)
333
firstDayWeek1 = addDays(firstDayWeek1 , 7 - weekDay1 + 1); // next monday
335
dayOfWeek1InYear = dayOfYear(firstDayWeek1);
337
if ( dayOfYear(date) < dayOfWeek1InYear ) // our date in prev year's week
341
return weeksInYear(y - 1);
344
// let's check if its last week belongs to next year
345
setYMD(lastDayOfYear, y + 1, 1, 1);
346
lastDayOfYear = addDays(lastDayOfYear, -1);
347
if ( (dayOfYear(date) >= daysInYear(date) - dayOfWeek(lastDayOfYear) + 1)
348
// our date is in last week
349
&& dayOfWeek(lastDayOfYear) < 4) // 1st week in next year has thursday
357
if( weekDay1 < 5 ) // To calculate properly the number of weeks
358
// from day a to x let's make a day 1 of week
359
firstDayWeek1 = addDays( firstDayWeek1, -( weekDay1 - 1));
361
week = firstDayWeek1.daysTo(date) / 7 + 1;
368
QString KCalendarSystemHebrew::monthName(const QDate& date,
369
bool shortName) const
371
return monthName(month(date), year(date), shortName);
375
QString KCalendarSystemHebrew::monthNamePossessive(const QDate& date,
376
bool shortName) const
378
return monthNamePossessive(month(date), year(date), shortName);
382
QString KCalendarSystemHebrew::monthName(int month, int year, bool /*shortName*/) const
386
if ( is_leap_year(year) )
391
else if ( month > 12 )
394
// We must map conversion algorithm month index to real index
395
if( month == 6 && is_leap_year(year) )
396
month = 13; /*Adar I*/
397
else if ( month == 7 && is_leap_year(year) )
398
month = 14; /*Adar II*/
399
else if ( month > 7 && is_leap_year(year) )
400
month--; //Because of Adar II
405
return ki18n("Tishrey").toString(locale());
407
return ki18n("Heshvan").toString(locale());
409
return ki18n("Kislev").toString(locale());
411
return ki18n("Tevet").toString(locale());
413
return ki18n("Shvat").toString(locale());
415
return ki18n("Adar").toString(locale());
417
return ki18n("Nisan").toString(locale());
419
return ki18n("Iyar").toString(locale());
421
return ki18n("Sivan").toString(locale());
423
return ki18n("Tamuz").toString(locale());
425
return ki18n("Av").toString(locale());
427
return ki18n("Elul").toString(locale());
429
return ki18n("Adar I").toString(locale());
431
return ki18n("Adar II").toString(locale());
440
QString KCalendarSystemHebrew::monthNamePossessive(int month, int year,
441
bool shortName) const
443
return "of " + monthName(month, year, shortName);
446
bool KCalendarSystemHebrew::setYMD(QDate & date, int y, int m, int d) const
448
if( y < minValidYear() || y > maxValidYear() )
450
if( m < 1 || m > (is_leap_year(y) ? 13 : 12) )
452
if( d < 1 || d > hndays(m,y) )
455
class h_date * gd = hebrewToGregorian( y, m, d );
457
return date.setYMD(gd->hd_year, gd->hd_mon + 1, gd->hd_day + 1);
460
QString KCalendarSystemHebrew::weekDayName(int day, bool shortName) const
462
return KCalendarSystem::weekDayName(day, shortName);
466
QString KCalendarSystemHebrew::weekDayName(const QDate& date,
467
bool shortName) const
469
return weekDayName(dayOfWeek(date), shortName);
473
int KCalendarSystemHebrew::dayOfWeek(const QDate& date) const
475
class h_date *sd = toHebrew(date);
476
if ( sd->hd_dw == 0 )
483
int KCalendarSystemHebrew::dayOfYear(const QDate & date) const
486
setYMD(first, year(date), 1, 1);
488
return first.daysTo(date) + 1;
491
int KCalendarSystemHebrew::daysInMonth(const QDate& date) const
493
return hndays(month(date), year(date));
496
int KCalendarSystemHebrew::hndays(int mon, int year) const
498
if ( mon == 6 && is_leap_year(year) )
500
else if ( mon == 7 && is_leap_year(year) )
501
mon = 14; /*Adar II*/
502
else if ( mon > 7 && is_leap_year(year) )
503
mon--; //Because of Adar II
505
if( mon == 8 /*IYYAR*/ || mon == 10 /*TAMUZ*/ ||
506
mon == 12 /*ELUL*/ || mon == 4 /*TEVET*/ ||
507
mon == 14 /*ADAR 2*/||
508
( mon == 6 /*ADAR*/ && !is_leap_year(year)) ||
509
(mon == 2 /*CHESHVAN*/ && !long_cheshvan(year)) ||
510
(mon == 3 /*KISLEV*/ && short_kislev(year)))
517
// Min valid year that may be converted to QDate
518
int KCalendarSystemHebrew::minValidYear() const
520
QDate date(1753, 1, 1);
526
// Max valid year that may be converted to QDate
527
int KCalendarSystemHebrew::maxValidYear() const
529
QDate date(8000, 1, 1);
535
int KCalendarSystemHebrew::day(const QDate& date) const
537
class h_date *sd = toHebrew(date);
543
int KCalendarSystemHebrew::month(const QDate& date) const
545
class h_date *sd = toHebrew(date);
547
int month = sd->hd_mon;
548
if ( is_leap_year( sd->hd_year ) )
550
if( month == 13 /*AdarI*/ )
552
else if( month == 14 /*AdarII*/ )
554
else if ( month > 6 && month < 13 )
562
int KCalendarSystemHebrew::daysInYear(const QDate & date) const
565
setYMD(first, year(date), 1, 1); // 1 Tishrey
566
setYMD(last, year(date) + 1, 1, 1); // 1 Tishrey the year later
568
return first.daysTo(last);
572
int KCalendarSystemHebrew::weekDayOfPray() const
574
return 6; // saturday
578
QDate KCalendarSystemHebrew::addDays( const QDate & date, int ndays ) const
580
return date.addDays( ndays );
584
QDate KCalendarSystemHebrew::addMonths( const QDate & date, int nmonths ) const
588
while ( nmonths > 0 )
590
result = addDays(result, daysInMonth(result));
594
while ( nmonths < 0 )
596
// get the number of days in the previous month to be consistent with
597
// addMonths where nmonths > 0
598
int nDaysInMonth = daysInMonth(addDays(result, -day(result)));
599
result = addDays(result, -nDaysInMonth);
607
QDate KCalendarSystemHebrew::addYears( const QDate & date, int nyears ) const
610
int y = year(date) + nyears;
612
setYMD( result, y, month(date), day(date) );
618
QString KCalendarSystemHebrew::calendarName() const
620
return QLatin1String("hebrew");
624
bool KCalendarSystemHebrew::isLunar() const
630
bool KCalendarSystemHebrew::isLunisolar() const
636
bool KCalendarSystemHebrew::isSolar() const
641
QString KCalendarSystemHebrew::dayString(const QDate & pDate, bool bShort) const
645
// Only use hebrew numbers if the hebrew setting is selected
646
if (locale()->language() == QLatin1String("he"))
647
sResult = num2heb(day(pDate), false);
649
sResult = KCalendarSystem::dayString(pDate, bShort);
654
QString KCalendarSystemHebrew::yearString(const QDate & pDate, bool bShort) const
658
// Only use hebrew numbers if the hebrew setting is selected
659
if (locale()->language() == QLatin1String("he"))
660
sResult = num2heb(year(pDate), !bShort);
662
sResult = KCalendarSystem::yearString(pDate, bShort);
667
static int heb2num(const QString& str, int & iLength) {
672
int decadeValues[14] = {10, 20, 20, 30, 40, 40, 50,
673
50, 60, 70, 80, 80, 90, 90};
676
for (pos = 0 ; pos < s.length() ; pos++)
679
if (s.length() > pos && (s[pos + 1] == QChar('\'') ||
680
s[pos + 1] == QChar('\"')))
683
s.remove(pos + 1, 1);
686
if (c >= QChar(0x05D0) && c <= QChar(0x05D7))
688
if (s.length() > pos && s[pos + 1] >= QChar(0x05D0) &&
689
s[pos + 1] <= QChar(0x05EA))
690
result += (c.unicode() - 0x05D0 + 1) * 1000;
692
result += c.unicode() - 0x05D0 + 1;
694
else if (c == QChar(0x05D8))
696
if (s.length() > pos && s[pos + 1] >= QChar(0x05D0) &&
697
s[pos + 1] <= QChar(0x05EA) && s[pos + 1] != QChar(0x05D5) &&
698
s[pos + 1] != QChar(0x05D6))
703
else if (c >= QChar(0x05D9) && c <= QChar(0x05E6))
705
if (s.length() > pos && s[pos + 1] >= QChar(0x05D9))
708
result += decadeValues[c.unicode() - 0x05D9];
710
else if (c >= QChar(0x05E7) && c <= QChar(0x05EA))
712
result += (c.unicode() - 0x05E7 + 1) * 100;
725
int KCalendarSystemHebrew::dayStringToInteger(const QString & sNum, int & iLength) const
728
if (locale()->language() == "he")
729
iResult= heb2num(sNum, iLength);
731
iResult = KCalendarSystem::yearStringToInteger(sNum, iLength);
736
int KCalendarSystemHebrew::yearStringToInteger(const QString & sNum, int & iLength) const
739
if (locale()->language() == "he")
740
iResult = heb2num(sNum, iLength);
742
iResult = KCalendarSystem::yearStringToInteger(sNum, iLength);
745
iResult += 5000; // assume we're in the 6th millenium (y6k bug)