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 }