File indexing completed on 2025-01-05 04:49:35
0001 /* 0002 This file is part of KOrganizer. 0003 0004 SPDX-FileCopyrightText: 2003 Jonathan Singer <jsinger@leeta.net> 0005 SPDX-FileCopyrightText: 2007 Loïc Corbasson <loic.corbasson@gmail.com> 0006 Calendar routines from Hebrew Calendar by Frank Yellin. 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "converter.h" 0012 0013 HebrewDate::HebrewDate(const DateResult &d) 0014 : mYear(d.year) 0015 , mMonth(d.month) 0016 , mDay(d.day) 0017 , mDayOfWeek(d.day_of_week) 0018 , mHebrewMonthLength(d.hebrew_month_length) 0019 , mSecularMonthLength(d.secular_month_length) 0020 , mOnHebrewLeapYear(d.hebrew_leap_year_p) 0021 , mOnSecularLeapYear(d.secular_leap_year_p) 0022 , mKvia(d.kvia) 0023 , mHebrewDayNumber(d.hebrew_day_number) 0024 { 0025 } 0026 0027 HebrewDate::~HebrewDate() 0028 { 0029 } 0030 0031 HebrewDate HebrewDate::fromSecular(int year, int month, int day) 0032 { 0033 DateResult result; 0034 Converter::secularToHebrewConversion(year, month, day, &result); 0035 return HebrewDate(result); 0036 } 0037 0038 HebrewDate HebrewDate::fromHebrew(int year, int month, int day) 0039 { 0040 DateResult result; 0041 Converter::hebrewToSecularConversion(year, month, day, &result); 0042 return HebrewDate(result); 0043 } 0044 0045 int HebrewDate::year() const 0046 { 0047 return mYear; 0048 } 0049 0050 int HebrewDate::month() const 0051 { 0052 return mMonth; 0053 } 0054 0055 int HebrewDate::day() const 0056 { 0057 return mDay; 0058 } 0059 0060 int HebrewDate::dayOfWeek() const 0061 { 0062 return mDayOfWeek; 0063 } 0064 0065 int HebrewDate::hebrewMonthLength() const 0066 { 0067 return mHebrewMonthLength; 0068 } 0069 0070 int HebrewDate::secularMonthLength() const 0071 { 0072 return mSecularMonthLength; 0073 } 0074 0075 bool HebrewDate::isOnHebrewLeapYear() const 0076 { 0077 return mOnHebrewLeapYear; 0078 } 0079 0080 bool HebrewDate::isOnSecularLeapYear() const 0081 { 0082 return mOnSecularLeapYear; 0083 } 0084 0085 int HebrewDate::kvia() const 0086 { 0087 return mKvia; 0088 } 0089 0090 int HebrewDate::hebrewDayNumber() const 0091 { 0092 return mHebrewDayNumber; 0093 } 0094 0095 //////////////////////////////////////////////////////////////////////////////// 0096 0097 long Converter::absolute_from_gregorian(int year, int month, int day) 0098 { 0099 int xyear, day_number; 0100 0101 xyear = year - 1; 0102 day_number = day + 31 * (month - 1); 0103 if (month > 2) { 0104 day_number -= (23 + (4 * month)) / 10; 0105 if (gregorian_leap_year_p(year)) { 0106 day_number++; 0107 } 0108 } 0109 return day_number /* the day number within the current year */ 0110 + 365L * xyear /* days in prior years */ 0111 + (xyear / 4) /* Julian leap years */ 0112 + (-(xyear / 100)) /* deduct century years */ 0113 + (xyear / 400); /* add Gregorian leap years */ 0114 } 0115 0116 /* Given a Hebrew date, calculate the number of days since January 0, 0001, 0117 Gregorian */ 0118 long Converter::absolute_from_hebrew(int year, int month, int day) 0119 { 0120 long sum = day + hebrew_elapsed_days(year) - 1373429L; 0121 int i; 0122 0123 if (month < 7) { 0124 int months = hebrew_months_in_year(year); 0125 for (i = 7; i <= months; ++i) { 0126 sum += hebrew_month_length(year, i); 0127 } 0128 for (i = 1; i < month; ++i) { 0129 sum += hebrew_month_length(year, i); 0130 } 0131 } else { 0132 for (i = 7; i < month; ++i) { 0133 sum += hebrew_month_length(year, i); 0134 } 0135 } 0136 return sum; 0137 } 0138 0139 /* Given an absolute date, calculate the gregorian date */ 0140 void Converter::gregorian_from_absolute(long date, int *yearp, int *monthp, int *dayp) 0141 { 0142 int year, month, day; 0143 0144 for (year = date / 366; date >= absolute_from_gregorian(year + 1, 1, 1); ++year) { } 0145 0146 for (month = 1; (month <= 11) && (date >= absolute_from_gregorian(year, 1 + month, 1)); ++month) { } 0147 0148 day = 1 + date - absolute_from_gregorian(year, month, 1); 0149 *yearp = year; 0150 *monthp = month; 0151 *dayp = day; 0152 } 0153 0154 /* Given an absolute date, calculate the Hebrew date */ 0155 void Converter::hebrew_from_absolute(long date, int *yearp, int *monthp, int *dayp) 0156 { 0157 int year, month, day, gyear, gmonth, gday, months; 0158 0159 gregorian_from_absolute(date, &gyear, &gmonth, &gday); 0160 year = gyear + 3760; 0161 while (date >= absolute_from_hebrew(1 + year, 7, 1)) { 0162 year++; 0163 } 0164 months = hebrew_months_in_year(year); 0165 for (month = 7; date > absolute_from_hebrew(year, month, hebrew_month_length(year, month)); month = 1 + (month % months)) { } 0166 day = 1 + date - absolute_from_hebrew(year, month, 1); 0167 *yearp = year; 0168 *monthp = month; 0169 *dayp = day; 0170 } 0171 0172 /* Number of months in a Hebrew year */ 0173 int Converter::hebrew_months_in_year(int year) 0174 { 0175 if (hebrew_leap_year_p(year)) { 0176 return 13; 0177 } else { 0178 return 12; 0179 } 0180 } 0181 0182 /* Number of days in a Hebrew month */ 0183 int Converter::hebrew_month_length(int year, int month) 0184 { 0185 switch (month) { 0186 case Tishrei: 0187 case Shvat: 0188 case Nissan: 0189 case Sivan: 0190 case Ab: 0191 return 30; 0192 0193 case Tevet: 0194 case Iyar: 0195 case Tamuz: 0196 case Elul: 0197 case AdarII: 0198 return 29; 0199 0200 case Cheshvan: 0201 // 29 days, unless it's a long year. 0202 if ((hebrew_year_length(year) % 10) == 5) { 0203 return 30; 0204 } else { 0205 return 29; 0206 } 0207 0208 case Kislev: 0209 // 30 days, unless it's a short year. 0210 if ((hebrew_year_length(year) % 10) == 3) { 0211 return 29; 0212 } else { 0213 return 30; 0214 } 0215 0216 case Adar: 0217 // Adar (non-leap year) has 29 days. Adar I has 30 days. 0218 if (hebrew_leap_year_p(year)) { 0219 return 30; 0220 } else { 0221 return 29; 0222 } 0223 0224 default: 0225 return 0; 0226 } 0227 } 0228 0229 /* Number of days in a Julian or gregorian month */ 0230 int Converter::secular_month_length(int year, int month /*, bool julianp */) 0231 { 0232 switch (month) { 0233 case January: 0234 case March: 0235 case May: 0236 case July: 0237 case August: 0238 case October: 0239 case December: 0240 return 31; 0241 0242 case April: 0243 case June: 0244 case September: 0245 case November: 0246 return 30; 0247 0248 case February: 0249 if (gregorian_leap_year_p(year)) { 0250 return 29; 0251 } else { 0252 return 28; 0253 } 0254 0255 default: 0256 return 0; 0257 } 0258 } 0259 0260 /* Is it a leap year in the gregorian calendar */ 0261 bool Converter::gregorian_leap_year_p(int year) 0262 { 0263 if ((year % 4) != 0) { 0264 return 0; 0265 } 0266 if ((year % 400) == 0) { 0267 return 1; 0268 } 0269 if ((year % 100) == 0) { 0270 return 0; 0271 } 0272 return 1; 0273 } 0274 0275 /* Is it a leap year in the Jewish Calendar */ 0276 bool Converter::hebrew_leap_year_p(int year) 0277 { 0278 switch (year % 19) { 0279 case 0: 0280 case 3: 0281 case 6: 0282 case 8: 0283 case 11: 0284 case 14: 0285 case 17: 0286 return 1; 0287 default: 0288 return 0; 0289 } 0290 } 0291 0292 /* Return the number of days from 1 Tishrei 0001 to the beginning of the given 0293 year. Since this routine gets called frequently with the same year arguments, 0294 we cache the most recent values. */ 0295 #define MEMORY 5 0296 long Converter::hebrew_elapsed_days(int year) 0297 { 0298 static int saved_year[MEMORY] = {-1, -1, -1, -1, -1}; 0299 static long saved_value[MEMORY]; 0300 int i; 0301 0302 for (i = 0; i < MEMORY; ++i) { 0303 if (year == saved_year[i]) { 0304 return saved_value[i]; 0305 } 0306 } 0307 for (i = 0; i < MEMORY - 1; ++i) { 0308 saved_year[i] = saved_year[1 + i]; 0309 saved_value[i] = saved_value[1 + i]; 0310 } 0311 saved_year[MEMORY - 1] = year; 0312 saved_value[MEMORY - 1] = hebrew_elapsed_days2(year); 0313 return saved_value[MEMORY - 1]; 0314 } 0315 0316 /* Called by hebrew_elapsed_days to make the calculations if the result is not 0317 in the cache */ 0318 long Converter::hebrew_elapsed_days2(int year) 0319 { 0320 long prev_year = year - 1; 0321 long months_elapsed = 235L * (prev_year / 19) + /* months in complete cycles so far */ 0322 12L * (prev_year % 19) + /* regular months in this cycle */ 0323 (((prev_year % 19) * 7 + 1) / 19); 0324 /* leap months in this cycle */ 0325 long parts_elapsed = 5604 + 13753 * months_elapsed; 0326 long day = 1 + 29 * months_elapsed + parts_elapsed / 25920; 0327 long parts = parts_elapsed % 25920; 0328 int weekday = day % 7; 0329 long alt_day = ((parts >= 19440) || (weekday == 2 && (parts >= 9924) && !hebrew_leap_year_p(year)) 0330 || (weekday == 1 && (parts >= 16789) && hebrew_leap_year_p(prev_year))) 0331 ? day + 1 0332 : day; 0333 0334 switch (alt_day % 7) { 0335 case 0: 0336 case 3: 0337 case 5: 0338 return 1 + alt_day; 0339 default: 0340 return alt_day; 0341 } 0342 } 0343 0344 /* Number of days in the given Hebrew year */ 0345 int Converter::hebrew_year_length(int year) 0346 { 0347 return hebrew_elapsed_days(1 + year) - hebrew_elapsed_days(year); 0348 } 0349 0350 /* Fill in the DateResult structure based on the given secular date */ 0351 void Converter::secularToHebrewConversion(int syear, int smonth, int sday, struct DateResult *result) 0352 { 0353 int hyear, hmonth, hday; 0354 long absolute; 0355 0356 absolute = absolute_from_gregorian(syear, smonth, sday); 0357 0358 hebrew_from_absolute(absolute, &hyear, &hmonth, &hday); 0359 0360 result->year = hyear; 0361 result->month = hmonth; 0362 result->day = hday; 0363 finish_up(absolute, hyear, hmonth, syear, smonth, result); 0364 } 0365 0366 /* Fill in the DateResult structure based on the given Hebrew date */ 0367 void Converter::hebrewToSecularConversion(int hyear, int hmonth, int hday, struct DateResult *result) 0368 { 0369 int syear, smonth, sday; 0370 long absolute; 0371 0372 absolute = absolute_from_hebrew(hyear, hmonth, hday); 0373 gregorian_from_absolute(absolute, &syear, &smonth, &sday); 0374 result->year = hyear; 0375 result->month = hmonth; 0376 result->day = hday; 0377 finish_up(absolute, hyear, hmonth, syear, smonth, result); 0378 } 0379 0380 /* This is common code for filling up the DateResult structure */ 0381 void Converter::finish_up(long absolute, int hyear, int hmonth, int syear, int smonth, struct DateResult *result) 0382 { 0383 result->hebrew_month_length = hebrew_month_length(hyear, hmonth); 0384 result->secular_month_length = secular_month_length(syear, smonth); 0385 result->hebrew_leap_year_p = hebrew_leap_year_p(hyear); 0386 result->secular_leap_year_p = gregorian_leap_year_p(syear); 0387 result->kvia = (hebrew_year_length(hyear) % 10) - 3; 0388 // absolute is -1 on 1/1/0001 Julian 0389 result->day_of_week = (7 + absolute) % 7; 0390 result->hebrew_day_number = absolute - absolute_from_hebrew(hyear, 7, 1) + 1; 0391 }