diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java index dd38b1b5..dece8c48 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java @@ -309,7 +309,7 @@ private static int getLastDayOfGregorianMonth(int year, int month) { * @return the {@code LocalDate}. */ private static LocalDate absDateToDate(int absDate) { - int year = absDate / 366; // Search forward year by year from approximate year + int year = Math.floorDiv(absDate, 366); // Search forward year by year from approximate year while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { year++; } @@ -347,11 +347,12 @@ private static int gregorianDateToAbsDate(int year, int month, int dayOfMonth) { } int y = year - 1; + // Floor division, so that year 0, a leap year, gets its leap day return (absDate // days this year + 365 * y // days in previous years ignoring leap days - + y / 4 // Julian leap days before this year - - y / 100 // minus prior century years - + y / 400); // plus prior years divisible by 400 + + Math.floorDiv(y, 4) // Julian leap days before this year + - Math.floorDiv(y, 100) // minus prior century years + + Math.floorDiv(y, 400)); // plus prior years divisible by 400 } /** @@ -670,13 +671,11 @@ public int getDaysInJewishMonth() { } /** - * Computes and sets the Jewish date fields based on the provided absolute (Gregorian) date. + * Computes and sets the Jewish date fields based on the provided absolute (Gregorian) date. The date may be before + * January 1, 0001, as the molad of Teves 3761 is, on December 12, 0000. * @param gregorianAbsDate the Gregorian absolute date. */ private synchronized void setAbsDate(int gregorianAbsDate) { - if (gregorianAbsDate <= 0) { - throw new IllegalArgumentException("Dates in the BC era are not supported"); - } this.gregorianAbsDate = gregorianAbsDate; // Approximation from below jewishYear = (gregorianAbsDate - JEWISH_EPOCH) / 366; @@ -696,8 +695,8 @@ private synchronized void setAbsDate(int gregorianAbsDate) { // Calculate the day by subtraction jewishDay = gregorianAbsDate - jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1; - // day of week (same calculation as original) - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; + // day of week, with floorMod so that a date before January 1, 0001 gets the right day + dayOfWeek = Math.floorMod(gregorianAbsDate, 7) + 1; } /** @@ -855,6 +854,9 @@ public void setGregorianDate(ZonedDateTime zonedDateTime) { */ public void setGregorianDate(LocalDate localDate) { int absDate = gregorianDateToAbsDate(localDate.getYear(), localDate.getMonth().getValue(), localDate.getDayOfMonth()); + if (absDate <= 0) { + throw new IllegalArgumentException("Dates in the BC era are not supported"); + } setAbsDate(absDate); // convert to Jewish date } @@ -975,7 +977,11 @@ public void minusDays(int days){ if (days < 1) { throw new IllegalArgumentException("The number of days to subtract must be greater than zero."); } - setAbsDate(getAbsDate() - days); + int absDate = getAbsDate() - days; + if (absDate <= 0) { + throw new IllegalArgumentException("Dates in the BC era are not supported"); + } + setAbsDate(absDate); } diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java index f816ef2e..ac3c06a2 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java @@ -149,14 +149,15 @@ private static int getNumOfSpecialDays(LocalDate start, LocalDate end) { /** - * Checks if a date is after start and on or before end. + * Checks if a date is on or after start and on or before end. A cycle can start on, or be extended to start + * on, Yom Kippur or Tisha B'Av, so the start day is counted. * @param start the start ZonedDateTime * @param date the ZonedDateTime to check * @param end the end ZonedDateTime * @return if the date is between the two dates */ private static boolean isBetween(LocalDate start, LocalDate date, LocalDate end) { - return start.isBefore(date) && !end.isBefore(date); + return !date.isBefore(start) && !end.isBefore(date); } /** diff --git a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_JewishDateNavigation.java b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_JewishDateNavigation.java index aad45602..7141ec80 100644 --- a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_JewishDateNavigation.java +++ b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_JewishDateNavigation.java @@ -60,5 +60,18 @@ public void addYearsUsesConfiguredAdarMonthWhenMovingToLeapYear() { Assert.assertEquals(10, adarBeisDate.getJewishDayOfMonth()); } + /** + * The molad of Teves 3761 is Tuesday 12 December of Gregorian year 0, which is a leap year. + */ + @Test + public void moladInGregorianYear0() { + JewishDate molad = new JewishDate(3761, JewishDate.TEVES, 20).getMolad(); + Assert.assertEquals(LocalDate.of(0, 12, 12), molad.getLocalDate()); + Assert.assertEquals(Calendar.TUESDAY, molad.getDayOfWeek()); + Assert.assertEquals(20, molad.getMoladHours()); + Assert.assertEquals(7, molad.getMoladMinutes()); + Assert.assertEquals(2, molad.getMoladChalakim()); + } + } // End of UT_JewishDateNavigation class diff --git a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_YerushalmiTest.java b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_YerushalmiTest.java index 60b9d8b7..d605f7c4 100644 --- a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_YerushalmiTest.java +++ b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_YerushalmiTest.java @@ -1,5 +1,6 @@ package com.kosherjava.zmanim.hebrewcalendar; +import java.time.LocalDate; import java.util.Calendar; import org.junit.*; @@ -50,4 +51,33 @@ public void testCorrectSpecialDate() { System.out.println(hdf.formatDafYomiYerushalmi(jewishCalendar.getDafYomiYerushalmi())); } + /** + * The cycle ending in 2172 is extended for Tisha B'Av 5932. It still has every daf, ending with Niddah 13 on + * 7 August 2172. + */ + @Test + public void testCycleExtendedForTishaBav() { + assertDaf(LocalDate.of(2172, 8, 6), 38, 12); + assertDaf(LocalDate.of(2172, 8, 7), 38, 13); + assertDaf(LocalDate.of(2172, 8, 8), 0, 1); + } + + /** + * Tisha B'Av 6240 falls the day before the last daf of a cycle. The cycle still has every daf, ending with + * Niddah 13 on 16 August 2480, and the next one starts with Berachos 1. + */ + @Test + public void testCycleWithTishaBavBeforeItsLastDaf() { + assertDaf(LocalDate.of(2480, 8, 14), 38, 12); + Assert.assertNull(new JewishCalendar(LocalDate.of(2480, 8, 15)).getDafYomiYerushalmi()); + assertDaf(LocalDate.of(2480, 8, 16), 38, 13); + assertDaf(LocalDate.of(2480, 8, 17), 0, 1); + } + + private static void assertDaf(LocalDate date, int masechtaNumber, int daf) { + Daf dafYomi = new JewishCalendar(date).getDafYomiYerushalmi(); + Assert.assertEquals(date.toString(), masechtaNumber, dafYomi.getMasechtaNumber()); + Assert.assertEquals(date.toString(), daf, dafYomi.getDaf()); + } + }