internal override LocalInstant AddMonths(LocalInstant localInstant, int months) { // Note: this method gives the same result regardless of the month numbering used // by the instance. The method works in terms of civil month numbers for most of // the time in order to simplify the logic. if (months == 0) { return(localInstant); } long tickOfDay = TimeOfDayCalculator.GetTickOfDay(localInstant); var startDate = HebrewScripturalCalculator.HebrewFromAbsolute(AbsoluteDayFromLocalInstant(localInstant)); int year = startDate.Year; int month = HebrewMonthConverter.ScripturalToCivil(year, startDate.Month); // This arithmetic works the same both backwards and forwards. year += (months / MonthsPerLeapCycle) * YearsPerLeapCycle; months = months % MonthsPerLeapCycle; if (months > 0) { // Add as many months as we need to in order to act as if we'd begun at the start // of the year, for simplicity. months += month - 1; // Add a year at a time while (months >= GetMaxMonth(year)) { months -= GetMaxMonth(year); year++; } // However many months we've got left to add tells us the final month. month = months + 1; } else { // Pretend we were given the month at the end of the years. months -= GetMaxMonth(year) - month; // Subtract a year at a time while (months + GetMaxMonth(year) <= 0) { months += GetMaxMonth(year); year--; } // However many months we've got left to add (which will still be negative...) // tells us the final month. month = GetMaxMonth(year) + months; } // Convert back to scriptural for the last bit month = HebrewMonthConverter.CivilToScriptural(year, month); int day = Math.Min(HebrewScripturalCalculator.DaysInMonth(year, month), startDate.Day); int absoluteDay = HebrewScripturalCalculator.AbsoluteFromHebrew(year, month, day); return(LocalInstantFromAbsoluteDay(absoluteDay, tickOfDay)); }
/// <summary> /// Change the year, maintaining month and day as well as possible. This doesn't /// work in the same way as other calendars; see http://judaism.stackexchange.com/questions/39053 /// for the reasoning behind the rules. /// </summary> internal override LocalInstant SetYear(LocalInstant localInstant, int year) { long tickOfDay = TimeOfDayCalculator.GetTickOfDay(localInstant); int absoluteSourceDay = AbsoluteDayFromLocalInstant(localInstant); YearMonthDay ymd = HebrewScripturalCalculator.HebrewFromAbsolute(absoluteSourceDay); int targetDay = ymd.Day; int targetScripturalMonth = ymd.Month; if (targetScripturalMonth == 13 && !IsLeapYear(year)) { // If we were in Adar II and the target year is not a leap year, map to Adar. targetScripturalMonth = 12; } else if (targetScripturalMonth == 12 && IsLeapYear(year) && !IsLeapYear(ymd.Year)) { // If we were in Adar (non-leap year), go to Adar II rather than Adar I in a leap year. targetScripturalMonth = 13; } // If we're aiming for the 30th day of Heshvan, Kislev or an Adar, it's possible that the change in year // has meant the day becomes invalid. In that case, roll over to the 1st of the subsequent month. if (targetDay == 30 && (targetScripturalMonth == 8 || targetScripturalMonth == 9 || targetScripturalMonth == 12)) { if (HebrewScripturalCalculator.DaysInMonth(year, targetScripturalMonth) != 30) { targetDay = 1; targetScripturalMonth++; // From Adar, roll to Nisan. if (targetScripturalMonth == 13) { targetScripturalMonth = 1; } } } int absoluteTargetDay = HebrewScripturalCalculator.AbsoluteFromHebrew(year, targetScripturalMonth, targetDay); return(LocalInstantFromAbsoluteDay(absoluteTargetDay, tickOfDay)); }
/// <summary> /// Change the year, maintaining month and day as well as possible. This doesn't /// work in the same way as other calendars; see https://judaism.stackexchange.com/questions/39053 /// for the reasoning behind the rules. /// </summary> internal override YearMonthDay SetYear(YearMonthDay yearMonthDay, int year) { int currentYear = yearMonthDay.Year; int currentMonth = yearMonthDay.Month; int targetDay = yearMonthDay.Day; int targetScripturalMonth = CalendarToScripturalMonth(currentYear, currentMonth); if (targetScripturalMonth == 13 && !IsLeapYear(year)) { // If we were in Adar II and the target year is not a leap year, map to Adar. targetScripturalMonth = 12; } else if (targetScripturalMonth == 12 && IsLeapYear(year) && !IsLeapYear(currentYear)) { // If we were in Adar (non-leap year), go to Adar II rather than Adar I in a leap year. targetScripturalMonth = 13; } // If we're aiming for the 30th day of Heshvan, Kislev or an Adar, it's possible that the change in year // has meant the day becomes invalid. In that case, roll over to the 1st of the subsequent month. if (targetDay == 30 && (targetScripturalMonth == 8 || targetScripturalMonth == 9 || targetScripturalMonth == 12)) { if (HebrewScripturalCalculator.DaysInMonth(year, targetScripturalMonth) != 30) { targetDay = 1; targetScripturalMonth++; // From Adar, roll to Nisan. if (targetScripturalMonth == 13) { targetScripturalMonth = 1; } } } int targetCalendarMonth = ScripturalToCalendarMonth(year, targetScripturalMonth); return(new YearMonthDay(year, targetCalendarMonth, targetDay)); }
internal override int GetDaysInMonth(int year, int month) => HebrewScripturalCalculator.DaysInMonth(year, CalendarToScripturalMonth(year, month));
internal override int GetDaysInMonth(int year, int month) { return(HebrewScripturalCalculator.DaysInMonth(year, calendarToScriptural(year, month))); }