From dba09236088f5a6297d012ada8ee4de1e22d2039 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 16 Mar 2026 08:42:46 -0400 Subject: [PATCH 1/9] Migrate AstronomicalCalendar to use LocalDate - Fix Timezone Bug - Use correct SUNRISE enum in getInstantFromTime --- .gitignore | 3 +- .../zmanim/AstronomicalCalendar.java | 119 +++++++++--------- .../zmanim/ComprehensiveZmanimCalendar.java | 57 +++------ .../com/kosherjava/zmanim/ZmanimCalendar.java | 4 +- .../zmanim/util/AstronomicalCalculator.java | 21 ++-- .../kosherjava/zmanim/util/GeoLocation.java | 4 +- .../zmanim/util/NOAACalculator.java | 59 ++++----- .../zmanim/util/SunTimesCalculator.java | 35 +++--- .../zmanim/util/ZmanimFormatter.java | 28 +++-- 9 files changed, 155 insertions(+), 175 deletions(-) diff --git a/.gitignore b/.gitignore index e82f6727..fdadea96 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target/ .idea/ zmanim.iml .gradle -build \ No newline at end of file +build +local.properties \ No newline at end of file diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index dd6606c4..5f33571b 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -19,16 +19,18 @@ import java.time.Duration; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; import com.kosherjava.zmanim.util.ZmanimFormatter; /** * A Java calendar that calculates astronomical times such as {@link #getSunrise() sunrise}, {@link #getSunset() - * sunset} and twilight times. This class contains a {@link #getZonedDateTime() zonedDateTime} and can therefore use the standard + * sunset} and twilight times. This class contains a {@link #getLocalDate() zonedDateTime} and can therefore use the standard * Calendar functionality to change dates etc. The calculation engine used to calculate the astronomical times can be * changed to a different implementation by implementing the abstract {@link AstronomicalCalculator} and setting it with * the {@link #setAstronomicalCalculator(AstronomicalCalculator)}. A number of different calculation engine @@ -95,7 +97,7 @@ public class AstronomicalCalendar implements Cloneable { /** * The ZonedDateTime encapsulated by this class to track the current date used by the class */ - private ZonedDateTime zonedDateTime; + private LocalDate localDate; /** * the {@link GeoLocation} used for calculations. @@ -329,7 +331,7 @@ public static Instant getTimeOffset(Instant time, long offsetMillis) { public Instant getSunriseOffsetByDegrees(double offsetZenith) { double dawn = getUTCSunrise(offsetZenith); return Double.isNaN(dawn) ? null - : getInstantFromTime(dawn, SolarEvent.SUNSET); + : getInstantFromTime(dawn, SolarEvent.SUNRISE); } /** @@ -371,7 +373,7 @@ public AstronomicalCalendar() { * @see #setAstronomicalCalculator(AstronomicalCalculator) for changing the calculator class. */ public AstronomicalCalendar(GeoLocation geoLocation) { - setZonedDateTime(ZonedDateTime.now(geoLocation.getZoneId())); + setLocalDate(LocalDate.now(geoLocation.getZoneId())); setGeoLocation(geoLocation);// duplicate call setAstronomicalCalculator(AstronomicalCalculator.getDefault()); } @@ -387,7 +389,7 @@ public AstronomicalCalendar(GeoLocation geoLocation) { * not set, {@link Double#NaN} will be returned. See detailed explanation on top of the page. */ public double getUTCSunrise(double zenith) { - return getAstronomicalCalculator().getUTCSunrise(getAdjustedCalendar(), getGeoLocation(), zenith, true); + return getAstronomicalCalculator().getUTCSunrise(getAdjustedLocalDate(), getGeoLocation(), zenith, true); } /** @@ -405,7 +407,7 @@ public double getUTCSunrise(double zenith) { * @see AstronomicalCalendar#getUTCSeaLevelSunset */ public double getUTCSeaLevelSunrise(double zenith) { - return getAstronomicalCalculator().getUTCSunrise(getAdjustedCalendar(), getGeoLocation(), zenith, false); + return getAstronomicalCalculator().getUTCSunrise(getAdjustedLocalDate(), getGeoLocation(), zenith, false); } /** @@ -420,7 +422,7 @@ public double getUTCSeaLevelSunrise(double zenith) { * @see AstronomicalCalendar#getUTCSeaLevelSunset */ public double getUTCSunset(double zenith) { - return getAstronomicalCalculator().getUTCSunset(getAdjustedCalendar(), getGeoLocation(), zenith, true); + return getAstronomicalCalculator().getUTCSunset(getAdjustedLocalDate(), getGeoLocation(), zenith, true); } /** @@ -439,7 +441,7 @@ public double getUTCSunset(double zenith) { * @see AstronomicalCalendar#getUTCSeaLevelSunrise */ public double getUTCSeaLevelSunset(double zenith) { - return getAstronomicalCalculator().getUTCSunset(getAdjustedCalendar(), getGeoLocation(), zenith, false); + return getAstronomicalCalculator().getUTCSunset(getAdjustedLocalDate(), getGeoLocation(), zenith, false); } /** @@ -506,7 +508,7 @@ public long getTemporalHour(Instant startOfDay, Instant endOfDay) { //FIXME new * @see com.kosherjava.zmanim.util.SunTimesCalculator#getUTCNoon(Calendar, GeoLocation) */ public Instant getSunTransit() { - double noon = getAstronomicalCalculator().getUTCNoon(getAdjustedCalendar(), getGeoLocation()); + double noon = getAstronomicalCalculator().getUTCNoon(getAdjustedLocalDate(), getGeoLocation()); return getInstantFromTime(noon, SolarEvent.NOON); //FIXME NEW CODE } @@ -536,7 +538,7 @@ public Instant getSunTransit() { * @see com.kosherjava.zmanim.util.SunTimesCalculator#getUTCNoon(Calendar, GeoLocation) */ public Instant getSolarMidnight() { - double noon = getAstronomicalCalculator().getUTCMidnight(getAdjustedCalendar(), getGeoLocation()); + double noon = getAstronomicalCalculator().getUTCMidnight(getAdjustedLocalDate(), getGeoLocation()); return getInstantFromTime(noon, SolarEvent.MIDNIGHT); } @@ -589,35 +591,27 @@ protected Instant getInstantFromTime(double time, SolarEvent solarEvent) { return null; } - ZonedDateTime adjustedZonedDateTime = getAdjustedZonedDateTime(); + LocalDate date = getAdjustedLocalDate(); - LocalDate date = adjustedZonedDateTime - .withZoneSameInstant(ZoneOffset.UTC) - .toLocalDate(); + double localTimeHours = (getGeoLocation().getLongitude() / 15) + time; - // Convert fractional hour to total seconds - int totalSeconds = (int) Math.floor(time * 3600); - int hours = totalSeconds / 3600; - int minutes = (totalSeconds % 3600) / 60; - int seconds = totalSeconds % 60; - int localTimeHours = (int) getGeoLocation().getLongitude() / 15; - - if (solarEvent == SolarEvent.SUNRISE && localTimeHours + hours > 18) { + if (solarEvent == SolarEvent.SUNRISE && localTimeHours > 18) { date = date.minusDays(1); - } else if (solarEvent == SolarEvent.SUNSET && localTimeHours + hours < 6) { + } else if (solarEvent == SolarEvent.SUNSET && localTimeHours < 6) { date = date.plusDays(1); - } else if (solarEvent == SolarEvent.MIDNIGHT && localTimeHours + hours < 12) { + } else if (solarEvent == SolarEvent.MIDNIGHT && localTimeHours < 12) { date = date.plusDays(1); } else if (solarEvent == SolarEvent.NOON) { - if (localTimeHours + hours < 0) { + if (localTimeHours < 0) { date = date.plusDays(1); - } else if (localTimeHours + hours > 24) { + } else if (localTimeHours > 24) { date = date.minusDays(1); } } + LocalDateTime dateTime = date.atStartOfDay().plusSeconds((long) (time*3600)); - LocalTime localTime = LocalTime.of(hours, minutes, seconds); - return ZonedDateTime.of(date, localTime, ZoneOffset.UTC).toInstant(); + // The computed time is in UTC fractional hours; anchor in UTC before converting. + return ZonedDateTime.of(dateTime, ZoneOffset.UTC).toInstant(); } /** @@ -718,39 +712,42 @@ public Instant getLocalMeanTime(double hours) { throw new IllegalArgumentException("Hours must be between 0 and 23.9999..."); } - double rawOffset = getGeoLocation().getZoneId().getRules().getStandardOffset(getZonedDateTime().toInstant()).getTotalSeconds() * 1000; + double rawOffset = getGeoLocation().getZoneId().getRules().getOffset(getMidnightLastNight().toInstant()).getTotalSeconds() * 1000; double utcTime = hours - rawOffset / (double) HOUR_MILLIS; Instant instant = getInstantFromTime(utcTime, SolarEvent.SUNRISE); - return getTimeOffset(instant, -getGeoLocation().getLocalMeanTimeOffset(getZonedDateTime().toInstant())); - } - - /** - * Adjusts the ZonedDateTime to deal with edge cases where the location crosses the antimeridian. - * - * @see GeoLocation#getAntimeridianAdjustment(Instant) - * @return the adjusted Calendar - */ - private ZonedDateTime getAdjustedCalendar(){ - int offset = getGeoLocation().getAntimeridianAdjustment(getZonedDateTime().toInstant()); - if (offset == 0) { - return getZonedDateTime(); - } - ZonedDateTime adjustedZonedDateTime = getZonedDateTime(); - return adjustedZonedDateTime.plusDays(1); + return getTimeOffset(instant, -getGeoLocation().getLocalMeanTimeOffset(getMidnightLastNight().toInstant())); } - + /** * Adjusts the ZonedDateTime to deal with edge cases where the location crosses the antimeridian. * * @see GeoLocation#getAntimeridianAdjustment(Instant) * @return the adjusted Calendar */ - private ZonedDateTime getAdjustedZonedDateTime(){ - ZonedDateTime adjustedZonedDateTime = getZonedDateTime(); - int offset = getGeoLocation().getAntimeridianAdjustment(getZonedDateTime().toInstant()); - return offset == 0 ? adjustedZonedDateTime : adjustedZonedDateTime.plusDays(offset); - } + private LocalDate getAdjustedLocalDate(){ + int offset = getGeoLocation().getAntimeridianAdjustment(getMidnightLastNight().toInstant()); + return offset == 0 ? getLocalDate() : getLocalDate().plusDays(offset); + } + + /** + * Used by Molad based zmanim to determine if zmanim occur during the current day. + * This is also used as the anchor for current timezone-offset calculations. + * @see #getMoladBasedTime(Instant, Instant, Instant, boolean) + * @return midnight at the start of the current local date in the configured timezone + */ + protected ZonedDateTime getMidnightLastNight() { + return ZonedDateTime.of(getLocalDate(),LocalTime.MIDNIGHT,getGeoLocation().getZoneId()); + } + + /** + * Used by Molad based zmanim to determine if zmanim occur during the current day. + * @see #getMoladBasedTime(Instant, Instant, Instant, boolean) + * @return following midnight + */ + protected ZonedDateTime getMidnightTonight() { + return ZonedDateTime.of(getLocalDate().plusDays(1),LocalTime.MIDNIGHT,getGeoLocation().getZoneId()); + } /** * Returns an XML formatted representation of the class using the default output of the @@ -787,7 +784,7 @@ public boolean equals(Object object) { return false; } AstronomicalCalendar aCal = (AstronomicalCalendar) object; - return getZonedDateTime().equals(aCal.getZonedDateTime()) && getGeoLocation().equals(aCal.getGeoLocation()) + return getLocalDate().equals(aCal.getLocalDate()) && getGeoLocation().equals(aCal.getGeoLocation()) && getAstronomicalCalculator().equals(aCal.getAstronomicalCalculator()); } @@ -797,7 +794,7 @@ public boolean equals(Object object) { public int hashCode() { int result = 17; result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash - result += 37 * result + getZonedDateTime().hashCode(); + result += 37 * result + getLocalDate().hashCode(); result += 37 * result + getGeoLocation().hashCode(); result += 37 * result + getAstronomicalCalculator().hashCode(); return result; @@ -823,7 +820,6 @@ public GeoLocation getGeoLocation() { */ public void setGeoLocation(GeoLocation geoLocation) { this.geoLocation = geoLocation; - getZonedDateTime().withZoneSameInstant(getGeoLocation().getZoneId()); } /** @@ -856,27 +852,24 @@ public void setAstronomicalCalculator(AstronomicalCalculator astronomicalCalcula * * @return Returns the ZonedDateTime. */ - public ZonedDateTime getZonedDateTime() { - return this.zonedDateTime; + public LocalDate getLocalDate() { + return this.localDate; } /** * Sets the ZonedDateTime object for us in this class. - * @param zonedDateTime + * @param localDate * The ZonedDateTime to set. */ - public void setZonedDateTime(ZonedDateTime zonedDateTime) { - this.zonedDateTime = zonedDateTime; - if (getGeoLocation() != null) {// if available set the Calendar's timezone to the GeoLocation TimeZone - getZonedDateTime().withZoneSameInstant(getGeoLocation().getZoneId()); - } + public void setLocalDate(LocalDate localDate) { + this.localDate = localDate; } /** * A method that creates a deep copy of the object. * Note: If the {@link java.util.TimeZone} in the cloned {@link com.kosherjava.zmanim.util.GeoLocation} will * be changed from the original, it is critical that - * {@link com.kosherjava.zmanim.AstronomicalCalendar#getZonedDateTime()}. + * {@link com.kosherjava.zmanim.AstronomicalCalendar#getLocalDate()}. * {@link java.util.Calendar#setTimeZone(TimeZone) setTimeZone(TimeZone)} be called in order for the * AstronomicalCalendar to output times in the expected offset after being cloned. * diff --git a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java index f10f4aec..e16acf23 100644 --- a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java @@ -15,6 +15,7 @@ */ package com.kosherjava.zmanim; +import java.time.LocalDate; import java.util.Calendar; // FIXME remove once FORWARD can be refactored. import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; @@ -3254,7 +3255,7 @@ public Instant getFixedLocalChatzos() { */ public Instant getSofZmanKidushLevanaBetweenMoldos(Instant alos, Instant tzais) { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime @@ -3289,8 +3290,8 @@ public Instant getSofZmanKidushLevanaBetweenMoldos(Instant alos, Instant tzais) * returned. */ private Instant getMoladBasedTime(Instant moladBasedTime, Instant alos, Instant tzais, boolean techila) { - Instant lastMidnight = getMidnightLastNight(); - Instant midnightTonight = getMidnightTonight(); + Instant lastMidnight = getMidnightLastNight().toInstant(); + Instant midnightTonight = getMidnightTonight().toInstant(); if(moladBasedTime.isBefore(lastMidnight) || moladBasedTime.isAfter(midnightTonight)){ // Invalid time, bailout return null; } else if (alos == null || tzais == null){ // Not enough info to adjust @@ -3358,7 +3359,7 @@ public Instant getSofZmanKidushLevanaBetweenMoldos() { public Instant getSofZmanKidushLevana15Days(Instant alos, Instant tzais) { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime // Do not calculate for impossible dates, but account for extreme cases. In the extreme case of Rapa Iti in @@ -3437,7 +3438,7 @@ public Instant getTchilasZmanKidushLevana3Days() { public Instant getTchilasZmanKidushLevana3Days(Instant alos, Instant tzais) { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime @@ -3477,7 +3478,7 @@ public Instant getTchilasZmanKidushLevana3Days(Instant alos, Instant tzais) { public Instant getZmanMolad() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime @@ -3496,28 +3497,6 @@ public Instant getZmanMolad() { } return molad; } - - /** - * Used by Molad based zmanim to determine if zmanim occur during the current day. - * @see #getMoladBasedTime(Instant, Instant, Instant, boolean) - * @return previous midnight - */ - private Instant getMidnightLastNight() { - ZonedDateTime midnight = getZonedDateTime().truncatedTo(ChronoUnit.DAYS); - return midnight.toInstant(); - } - - /** - * Used by Molad based zmanim to determine if zmanim occur during the current day. - * @see #getMoladBasedTime(Instant, Instant, Instant, boolean) - * @return following midnight - */ - private Instant getMidnightTonight() { - return getZonedDateTime() - .plusDays(1) - .truncatedTo(ChronoUnit.DAYS) - .toInstant(); - } /** * Returns the earliest time of Kiddush Levana according to the opinions that it should not be said until 7 @@ -3543,7 +3522,7 @@ private Instant getMidnightTonight() { */ public Instant getTchilasZmanKidushLevana7Days(Instant alos, Instant tzais) { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime // Optimize to not calculate for impossible dates, but account for extreme cases. Tchilas zman kiddush Levana 7 days for @@ -3591,7 +3570,7 @@ public Instant getTchilasZmanKidushLevana7Days() { */ public Instant getSofZmanAchilasChametzGRA() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3621,7 +3600,7 @@ public Instant getSofZmanAchilasChametzGRA() { */ public Instant getSofZmanAchilasChametzMGA72Minutes() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3652,7 +3631,7 @@ public Instant getSofZmanAchilasChametzMGA72Minutes() { public Instant getSofZmanAchilasChametzMGA72MinutesZmanis() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3682,7 +3661,7 @@ public Instant getSofZmanAchilasChametzMGA72MinutesZmanis() { public Instant getSofZmanAchilasChametzMGA16Point1Degrees() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3707,7 +3686,7 @@ public Instant getSofZmanAchilasChametzMGA16Point1Degrees() { */ public Instant getSofZmanBiurChametzGRA() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3735,7 +3714,7 @@ public Instant getSofZmanBiurChametzGRA() { */ public Instant getSofZmanBiurChametzMGA72Minutes() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3763,7 +3742,7 @@ public Instant getSofZmanBiurChametzMGA72Minutes() { */ public Instant getSofZmanBiurChametzMGA72MinutesZmanis() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3793,7 +3772,7 @@ public Instant getSofZmanBiurChametzMGA72MinutesZmanis() { */ public Instant getSofZmanBiurChametzMGA16Point1Degrees() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime @@ -3969,7 +3948,7 @@ public Instant getSofZmanTfilaBaalHatanya() { */ public Instant getSofZmanAchilasChametzBaalHatanya() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { @@ -3992,7 +3971,7 @@ public Instant getSofZmanAchilasChametzBaalHatanya() { */ public Instant getSofZmanBiurChametzBaalHatanya() { JewishCalendar jewishCalendar = new JewishCalendar(); - ZonedDateTime zdt = getZonedDateTime(); + LocalDate zdt = getLocalDate(); jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { return getTimeOffset(getSunriseBaalHatanya(), getShaahZmanisBaalHatanya() * 5); diff --git a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java index 4edff603..966137c7 100644 --- a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java @@ -1084,8 +1084,8 @@ public void setCandleLightingOffset(double candleLightingOffset) { */ public boolean isAssurBemlacha(Instant currentTime, Instant tzais, boolean inIsrael) { JewishCalendar jewishCalendar = new JewishCalendar(); - jewishCalendar.setGregorianDate(getZonedDateTime().getYear(),getZonedDateTime().getMonthValue(), - getZonedDateTime().getDayOfMonth()); + jewishCalendar.setGregorianDate(getLocalDate().getYear(), getLocalDate().getMonthValue(), + getLocalDate().getDayOfMonth()); jewishCalendar.setInIsrael(inIsrael); diff --git a/src/main/java/com/kosherjava/zmanim/util/AstronomicalCalculator.java b/src/main/java/com/kosherjava/zmanim/util/AstronomicalCalculator.java index 9bcda8d8..68645b9e 100644 --- a/src/main/java/com/kosherjava/zmanim/util/AstronomicalCalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/AstronomicalCalculator.java @@ -15,6 +15,7 @@ */ package com.kosherjava.zmanim.util; +import java.time.LocalDate; import java.time.ZonedDateTime; /** @@ -104,7 +105,7 @@ public static AstronomicalCalculator getDefault() { * A method that calculates UTC sunrise as well as any time based on an angle above or below sunrise. This abstract * method is implemented by the classes that extend this class. * - * @param zonedDateTime + * @param localDate * Used to calculate day of year. * @param geoLocation * The location information used for astronomical calculating sun times. @@ -121,14 +122,14 @@ public static AstronomicalCalculator getDefault() { * {@link java.lang.Double#NaN} will be returned. * @see #getElevationAdjustment(double) */ - public abstract double getUTCSunrise(ZonedDateTime zonedDateTime, GeoLocation geoLocation, double zenith, + public abstract double getUTCSunrise(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation); /** * A method that calculates UTC sunset as well as any time based on an angle above or below sunset. This abstract * method is implemented by the classes that extend this class. * - * @param zonedDateTime + * @param localDate * Used to calculate day of year. * @param geoLocation * The location information used for astronomical calculating sun times. @@ -145,7 +146,7 @@ public abstract double getUTCSunrise(ZonedDateTime zonedDateTime, GeoLocation ge * {@link java.lang.Double#NaN} will be returned. * @see #getElevationAdjustment(double) */ - public abstract double getUTCSunset(ZonedDateTime zonedDateTime, GeoLocation geoLocation, double zenith, + public abstract double getUTCSunset(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation); @@ -155,14 +156,14 @@ public abstract double getUTCSunset(ZonedDateTime zonedDateTime, GeoLocation geo * true solar noon, while the {@link com.kosherjava.zmanim.util.SunTimesCalculator} approximates it, calculating * the time as halfway between sunrise and sunset. * - * @param zonedDateTime + * @param localDate * Used to calculate day of year. * @param geoLocation * The location information used for astronomical calculating sun times. * * @return the time in minutes from zero UTC */ - public abstract double getUTCNoon(ZonedDateTime zonedDateTime, GeoLocation geoLocation); + public abstract double getUTCNoon(LocalDate localDate, GeoLocation geoLocation); /** @@ -171,21 +172,21 @@ public abstract double getUTCSunset(ZonedDateTime zonedDateTime, GeoLocation geo * true solar midnight, while the {@link com.kosherjava.zmanim.util.SunTimesCalculator} approximates it, calculating * the time as 12 hours after halfway between sunrise and sunset. * - * @param zonedDateTime + * @param localDate * Used to calculate day of year. * @param geoLocation * The location information used for astronomical calculating sun times. * * @return the time in minutes from zero UTC */ - public abstract double getUTCMidnight(ZonedDateTime zonedDateTime, GeoLocation geoLocation); + public abstract double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation); /** * Return the Solar Elevation for the * horizontal coordinate system at the given location at the given time. Can be negative if the sun is below the * horizon. Not corrected for altitude. * - * @param zonedDateTime + * @param localDate * time of calculation * @param geoLocation * The location information @@ -200,7 +201,7 @@ public abstract double getUTCSunset(ZonedDateTime zonedDateTime, GeoLocation geo * horizontal coordinate system at the given location at the given time. Not corrected for altitude. True south is 180 * degrees. * - * @param zonedDateTime + * @param localDate * time of calculation * @param geoLocation * The location information diff --git a/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java b/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java index 976dac76..3b43c3b2 100644 --- a/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java +++ b/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java @@ -342,7 +342,7 @@ public void setZoneId(ZoneId zoneId) { */ public long getLocalMeanTimeOffset(Instant instant) { ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId); - long timezoneOffsetMillis = zonedDateTime.getOffset().getTotalSeconds() * 1000; + long timezoneOffsetMillis = zonedDateTime.getOffset().getTotalSeconds() * 1000L; return (long) (getLongitude() * 4 * MINUTE_MILLIS - timezoneOffsetMillis); } @@ -695,7 +695,7 @@ public String toString() { * An implementation of the {@link java.lang.Object#clone()} method that creates a deep copy of the object. * Note: If the {@link java.time.ZoneId} in the clone will be changed from the original, it is critical - * that {@link com.kosherjava.zmanim.AstronomicalCalendar#getZonedDateTime()}. + * that {@link com.kosherjava.zmanim.AstronomicalCalendar#getLocalDate()}. * * @see java.lang.Object#clone() */ diff --git a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java index ebb3be97..094674de 100644 --- a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java @@ -16,6 +16,7 @@ package com.kosherjava.zmanim.util; import java.time.ZoneOffset; +import java.time.LocalDate; import java.time.ZonedDateTime; /** @@ -71,10 +72,10 @@ public String getCalculatorName() { /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean) */ - public double getUTCSunrise(ZonedDateTime zdt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { + public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; double adjustedZenith = adjustZenith(zenith, elevation); - double sunrise = getSunRiseSetUTC(zdt, geoLocation.getLatitude(), -geoLocation.getLongitude(), + double sunrise = getSunRiseSetUTC(dt, geoLocation.getLatitude(), -geoLocation.getLongitude(), adjustedZenith, SolarEvent.SUNRISE); sunrise = sunrise / 60; return sunrise > 0 ? sunrise % 24 : sunrise % 24 + 24; // ensure that the time is >= 0 and < 24 @@ -83,10 +84,10 @@ public double getUTCSunrise(ZonedDateTime zdt, GeoLocation geoLocation, double z /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean) */ - public double getUTCSunset(ZonedDateTime zdt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { + public double getUTCSunset(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; double adjustedZenith = adjustZenith(zenith, elevation); - double sunset = getSunRiseSetUTC(zdt, geoLocation.getLatitude(), -geoLocation.getLongitude(), + double sunset = getSunRiseSetUTC(dt, geoLocation.getLatitude(), -geoLocation.getLongitude(), adjustedZenith, SolarEvent.SUNSET); sunset = sunset / 60; return sunset > 0 ? sunset % 24 : sunset % 24 + 24; // ensure that the time is >= 0 and < 24 @@ -95,15 +96,15 @@ public double getUTCSunset(ZonedDateTime zdt, GeoLocation geoLocation, double ze /** * Return the Julian day from a Java Calendar. * - * @param zonedDateTime - * The ZonedDateTime + * @param localDate + * The LocalDate * @return the Julian day corresponding to the date Note: Number is returned for the start of the Julian * day. Fractional days / time should be added later. */ - private static double getJulianDay(ZonedDateTime zonedDateTime) { - int year = zonedDateTime.getYear(); - int month = zonedDateTime.getMonthValue(); - int day = zonedDateTime.getDayOfMonth(); + private static double getJulianDay(LocalDate localDate) { + int year = localDate.getYear(); + int month = localDate.getMonthValue(); + int day = localDate.getDayOfMonth(); if (month <= 2) { year -= 1; @@ -313,16 +314,16 @@ private static double getSunHourAngle(double latitude, double solarDeclination, /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation) */ - public double getSolarElevation(ZonedDateTime zdt, GeoLocation geoLocation) { - return getSolarElevationAzimuth(zdt, geoLocation, false); + public double getSolarElevation(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { + return getSolarElevationAzimuth(zonedDateTime, geoLocation, false); } /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(Calendar, GeoLocation) */ - public double getSolarAzimuth(ZonedDateTime zdt, GeoLocation geoLocation) { - return getSolarElevationAzimuth(zdt, geoLocation, true); + public double getSolarAzimuth(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { + return getSolarElevationAzimuth(zonedDateTime, geoLocation, true); } /** @@ -331,7 +332,7 @@ public double getSolarAzimuth(ZonedDateTime zdt, GeoLocation geoLocation) { * and time. Can be negative if the sun is below the horizon. Elevation is based on sea-level and is not * adjusted for altitude. * - * @param zonedDateTime + * @param localDate * time of calculation * @param geoLocation * The location for calculating the elevation or azimuth. @@ -347,14 +348,14 @@ private double getSolarElevationAzimuth(ZonedDateTime zonedDateTime, GeoLocation double lat = Math.toRadians(geoLocation.getLatitude()); double lon = geoLocation.getLongitude(); - ZonedDateTime utc = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC); + ZonedDateTime utc = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC); double fractionalDay = (utc.getHour() + (utc.getMinute() + (utc.getSecond() + utc.getNano() / 1_000_000_000.0) / 60.0) / 60.0) / 24.0; - double jd = getJulianDay(utc) + fractionalDay; + double jd = getJulianDay(utc.toLocalDate()) + fractionalDay; double jc = getJulianCenturiesFromJulianDay(jd); double decl = Math.toRadians(getSunDeclination(jc)); @@ -396,15 +397,15 @@ private double getSolarElevationAzimuth(ZonedDateTime zonedDateTime, GeoLocation * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) * @see #getSolarNoonMidnightUTC(double, double, SolarEvent) * - * @param zonedDateTime - * The zonedDateTime representing the date to calculate solar noon for + * @param localDate + * The localDate representing the date to calculate solar noon for * @param geoLocation * The location information used for astronomical calculating sun times. This class uses only requires * the longitude for calculating noon since it is the same time anywhere along the longitude line. * @return the time in minutes from zero UTC */ - public double getUTCNoon(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { - double noon = getSolarNoonMidnightUTC(getJulianDay(zonedDateTime), -geoLocation.getLongitude(), SolarEvent.NOON); + public double getUTCNoon(LocalDate localDate, GeoLocation geoLocation) { + double noon = getSolarNoonMidnightUTC(getJulianDay(localDate), -geoLocation.getLongitude(), SolarEvent.NOON); noon = noon / 60; return noon > 0 ? noon % 24 : noon % 24 + 24; // ensure that the time is >= 0 and < 24 } @@ -420,15 +421,15 @@ public double getUTCNoon(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) * @see #getSolarNoonMidnightUTC(double, double, SolarEvent) * - * @param zonedDateTime - * The ZonedDateTime representing the date to calculate solar noon for + * @param localDate + * The LocalDate representing the date to calculate solar noon for * @param geoLocation * The location information used for astronomical calculating sun times. This class uses only requires * the longitude for calculating noon since it is the same time anywhere along the longitude line. * @return the time in minutes from zero UTC */ - public double getUTCMidnight(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { - double midnight = getSolarNoonMidnightUTC(getJulianDay(zonedDateTime), -geoLocation.getLongitude(), SolarEvent.MIDNIGHT); + public double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation) { + double midnight = getSolarNoonMidnightUTC(getJulianDay(localDate), -geoLocation.getLongitude(), SolarEvent.MIDNIGHT); midnight = midnight / 60; return midnight > 0 ? midnight % 24 : midnight % 24 + 24; // ensure that the time is >= 0 and < 24 } @@ -469,8 +470,8 @@ private static double getSolarNoonMidnightUTC(double julianDay, double longitude * of sunrise or sunset in minutes for the given day at the given location on earth. * @todo Possibly increase the number of passes for improved accuracy, especially in the Arctic areas. * - * @param zonedDateTime - * The ZonedDateTime. + * @param localDate + * The LocalDate. * @param latitude * The latitude of observer in degrees * @param longitude @@ -481,9 +482,9 @@ private static double getSolarNoonMidnightUTC(double julianDay, double longitude * If the calculation is for {@link SolarEvent#SUNRISE SUNRISE} or {@link SolarEvent#SUNSET SUNSET} * @return the time in minutes from zero Universal Coordinated Time (UTC) */ - private static double getSunRiseSetUTC(ZonedDateTime zonedDateTime, double latitude, double longitude, double zenith, + private static double getSunRiseSetUTC(LocalDate localDate, double latitude, double longitude, double zenith, SolarEvent solarEvent) { - double julianDay = getJulianDay(zonedDateTime); + double julianDay = getJulianDay(localDate); // Find the time of solar noon at the location, and use that declination. // This is better than start of the Julian day diff --git a/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java b/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java index 904d67f2..0512d6a0 100644 --- a/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java @@ -15,6 +15,7 @@ */ package com.kosherjava.zmanim.util; +import java.time.LocalDate; import java.time.ZonedDateTime; /** @@ -48,19 +49,19 @@ public String getCalculatorName() { /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean) */ - public double getUTCSunrise(ZonedDateTime zdt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { + public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; double adjustedZenith = adjustZenith(zenith, elevation); - return getTimeUTC(zdt, geoLocation, adjustedZenith, true); + return getTimeUTC(dt, geoLocation, adjustedZenith, true); } /** * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean) */ - public double getUTCSunset(ZonedDateTime zdt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { + public double getUTCSunset(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; double adjustedZenith = adjustZenith(zenith, elevation); - return getTimeUTC(zdt, geoLocation, adjustedZenith, false); + return getTimeUTC(dt, geoLocation, adjustedZenith, false); } /** @@ -222,8 +223,8 @@ private static double getLocalMeanTime(double localHour, double sunRightAscensio * Get sunrise or sunset time in UTC, according to flag. This time is returned as * a double and is not adjusted for time-zone. * - * @param zonedDateTime - * the ZonedDateTime object to extract the day of year for calculation + * @param localDate + * the LocalDate object to extract the day of year for calculation * @param geoLocation * the GeoLocation object that contains the latitude and longitude * @param zenith @@ -234,8 +235,8 @@ private static double getLocalMeanTime(double localHour, double sunRightAscensio * (expected behavior for some locations such as near the poles, * {@link Double#NaN} will be returned. */ - private static double getTimeUTC(ZonedDateTime zonedDateTime, GeoLocation geoLocation, double zenith, boolean isSunrise) { - int dayOfYear = zonedDateTime.getDayOfYear(); + private static double getTimeUTC(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean isSunrise) { + int dayOfYear = localDate.getDayOfYear(); double sunMeanAnomaly = getMeanAnomaly(dayOfYear, geoLocation.getLongitude(), isSunrise); double sunTrueLong = getSunTrueLongitude(sunMeanAnomaly); double sunRightAscensionHours = getSunRightAscensionHours(sunTrueLong); @@ -265,16 +266,16 @@ private static double getTimeUTC(ZonedDateTime zonedDateTime, GeoLocation geoLoc * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) * @see NOAACalculator * - * @param zonedDateTime - * The ZonedDateTime representing the date to calculate solar noon for + * @param localDate + * The LocalDate representing the date to calculate solar noon for * @param geoLocation * The location information used for astronomical calculating sun times. * @return the time in minutes from zero UTC. If an error was encountered in the calculation (expected behavior for * some locations such as near the poles, {@link Double#NaN} will be returned. */ - public double getUTCNoon(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { - double sunrise = getUTCSunrise(zonedDateTime, geoLocation, 90, false); - double sunset = getUTCSunset(zonedDateTime, geoLocation, 90, false); + public double getUTCNoon(LocalDate localDate, GeoLocation geoLocation) { + double sunrise = getUTCSunrise(localDate, geoLocation, 90, false); + double sunset = getUTCSunset(localDate, geoLocation, 90, false); double noon = sunrise + ((sunset - sunrise) / 2); if (noon < 0) { noon += 12; @@ -295,15 +296,15 @@ public double getUTCNoon(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) * @see NOAACalculator * - * @param zonedDateTime - * The ZonedDateTime representing the date to calculate solar noon for + * @param localDate + * The LocalDate representing the date to calculate solar noon for * @param geoLocation * The location information used for astronomical calculating sun times. * @return the time in minutes from zero UTC. If an error was encountered in the calculation (expected behavior for * some locations such as near the poles, {@link Double#NaN} will be returned. */ - public double getUTCMidnight(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { - return (getUTCNoon(zonedDateTime, geoLocation) + 12); + public double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation) { + return (getUTCNoon(localDate, geoLocation) + 12); } /** diff --git a/src/main/java/com/kosherjava/zmanim/util/ZmanimFormatter.java b/src/main/java/com/kosherjava/zmanim/util/ZmanimFormatter.java index e93e7a45..342886fc 100644 --- a/src/main/java/com/kosherjava/zmanim/util/ZmanimFormatter.java +++ b/src/main/java/com/kosherjava/zmanim/util/ZmanimFormatter.java @@ -17,6 +17,8 @@ import java.lang.reflect.Method; import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -277,8 +279,8 @@ public String format(Time time) { * settings. * @return the formatted String */ - public String formatDateTime(Instant instant, ZonedDateTime zonedDateTime) { - ZonedDateTime dateTime = instant.atZone(zonedDateTime.getZone()); + public String formatDateTime(Instant instant, ZoneId zoneId) { + ZonedDateTime dateTime = instant.atZone(zoneId); if (this.dateTimeFormatter.toString().equals("yyyy-MM-dd'T'HH:mm:ss")) { return getXSDateTime(instant); @@ -380,7 +382,7 @@ public static String toXML(AstronomicalCalendar astronomicalCalendar) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd"); df = df.withZone(astronomicalCalendar.getGeoLocation().getZoneId()); - Instant instant = astronomicalCalendar.getZonedDateTime().toInstant(); + LocalDate localDate = astronomicalCalendar.getLocalDate(); ZoneId zi = astronomicalCalendar.getGeoLocation().getZoneId(); StringBuilder sb = new StringBuilder("<"); @@ -400,7 +402,7 @@ public static String toXML(AstronomicalCalendar astronomicalCalendar) { // output += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "; // output += xsi:schemaLocation="http://www.kosherjava.com/zmanim basicZmanim.xsd" } - sb.append(" date=\"").append(df.format(instant)).append("\""); + sb.append(" date=\"").append(df.format(localDate)).append("\""); sb.append(" type=\"").append(astronomicalCalendar.getClass().getName()).append("\""); sb.append(" algorithm=\"").append(astronomicalCalendar.getAstronomicalCalculator().getCalculatorName()).append("\""); sb.append(" location=\"").append(astronomicalCalendar.getGeoLocation().getLocationName()).append("\""); @@ -409,7 +411,9 @@ public static String toXML(AstronomicalCalendar astronomicalCalendar) { sb.append(" elevation=\"").append(astronomicalCalendar.getGeoLocation().getElevation()).append("\""); sb.append(" timeZoneName=\"").append(zi.getDisplayName(TextStyle.FULL, Locale.getDefault())).append("\""); sb.append(" timeZoneID=\"").append(zi.getId()).append("\""); - double offsetHours = astronomicalCalendar.getZonedDateTime().getOffset().getTotalSeconds() / 3600.0; + + ZonedDateTime lastMidnight = ZonedDateTime.of(astronomicalCalendar.getLocalDate(), LocalTime.MIDNIGHT, astronomicalCalendar.getGeoLocation().getZoneId()); + double offsetHours = lastMidnight.getOffset().getTotalSeconds() / 3600.0; sb.append(" timeZoneOffset=\"").append(offsetHours).append("\""); //sb.append(" useElevationAllZmanim=\"").append(astronomicalCalendar.useElevationAllZmanim()).append("\""); //TODO likely using reflection @@ -455,7 +459,7 @@ public static String toXML(AstronomicalCalendar astronomicalCalendar) { for (int i = 0; i < dateList.size(); i++) { zman = (Zman) dateList.get(i); sb.append("\t<").append(zman.getLabel()).append(">"); - sb.append(formatter.formatDateTime(zman.getZman(), astronomicalCalendar.getZonedDateTime())); + sb.append(formatter.formatDateTime(zman.getZman(), astronomicalCalendar.getGeoLocation().getZoneId())); sb.append("\n"); } Collections.sort(durationList, Zman.DURATION_ORDER); @@ -539,11 +543,11 @@ public static String toJSON(AstronomicalCalendar astronomicalCalendar) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd") .withZone(astronomicalCalendar.getGeoLocation().getZoneId()); - Instant instant = astronomicalCalendar.getZonedDateTime().toInstant(); + LocalDate localDate = astronomicalCalendar.getLocalDate(); ZoneId zi = astronomicalCalendar.getGeoLocation().getZoneId(); StringBuilder sb = new StringBuilder("{\n\"metadata\":{\n"); - sb.append("\t\"date\":\"").append(df.format(instant)).append("\",\n"); + sb.append("\t\"date\":\"").append(df.format(localDate)).append("\",\n"); sb.append("\t\"type\":\"").append(astronomicalCalendar.getClass().getName()).append("\",\n"); sb.append("\t\"algorithm\":\"").append(astronomicalCalendar.getAstronomicalCalculator().getCalculatorName()).append("\",\n"); sb.append("\t\"location\":\"").append(astronomicalCalendar.getGeoLocation().getLocationName()).append("\",\n"); @@ -554,9 +558,9 @@ public static String toJSON(AstronomicalCalendar astronomicalCalendar) { sb.append("\t\"timeZoneName\":\"").append(zi.getDisplayName(TextStyle.FULL, Locale.getDefault())).append("\",\n"); sb.append("\t\"timeZoneID\":\"").append(zi.getId()).append("\",\n"); //FIXME - - - double offsetHours = astronomicalCalendar.getZonedDateTime().getOffset().getTotalSeconds() / 3600.0; + + ZonedDateTime lastMidnight = ZonedDateTime.of(astronomicalCalendar.getLocalDate(), LocalTime.MIDNIGHT, astronomicalCalendar.getGeoLocation().getZoneId()); + double offsetHours = lastMidnight.getOffset().getTotalSeconds() / 3600.0; sb.append(" timeZoneOffset=\"").append(offsetHours).append("\""); sb.append("},\n\""); @@ -603,7 +607,7 @@ public static String toJSON(AstronomicalCalendar astronomicalCalendar) { for (int i = 0; i < dateList.size(); i++) { zman = (Zman) dateList.get(i); sb.append("\t\"").append(zman.getLabel()).append("\":\""); - sb.append(formatter.formatDateTime(zman.getZman(), astronomicalCalendar.getZonedDateTime())); + sb.append(formatter.formatDateTime(zman.getZman(), astronomicalCalendar.getGeoLocation().getZoneId())); sb.append("\",\n"); } Collections.sort(durationList, Zman.DURATION_ORDER); From 0375dfe76f40ac3475a79785fb23f82faa3ff764 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 16 Mar 2026 08:49:24 -0400 Subject: [PATCH 2/9] Fix Javadocs --- .../zmanim/AstronomicalCalendar.java | 8 +++---- .../com/kosherjava/zmanim/ZmanimCalendar.java | 8 ++++--- .../zmanim/hebrewcalendar/JewishCalendar.java | 8 +++---- .../zmanim/util/NOAACalculator.java | 24 +++++++++---------- .../zmanim/util/SunTimesCalculator.java | 12 +++++----- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index 5f33571b..270f79c0 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -24,6 +24,8 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; +import java.util.TimeZone; + import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; import com.kosherjava.zmanim.util.ZmanimFormatter; @@ -663,10 +665,10 @@ public double getSunriseSolarDipFromOffset(double minutes) { * @return the degrees below the horizon after sunset that match the offset in minutes passed it as a parameter. If * the calculation can't be computed (no sunset occurs on this day) a {@link Double#NaN} will be returned. * @deprecated This method is slow and inefficient and should NEVER be used in a loop. This method should be replaced - * by calls to {@link AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation)}. That method will + * by calls to {@link AstronomicalCalculator#getSolarElevation(ZonedDateTime, GeoLocation)}. That method will * efficiently return the the solar elevation (the sun's position in degrees below (or above) the horizon) * at the given time even in the arctic when there is no sunrise. - * @see AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation) + * @see AstronomicalCalculator#getSolarElevation(ZonedDateTime, GeoLocation) * @see #getSunriseSolarDipFromOffset(double) */ @Deprecated(forRemoval=false) @@ -870,8 +872,6 @@ public void setLocalDate(LocalDate localDate) { * Note: If the {@link java.util.TimeZone} in the cloned {@link com.kosherjava.zmanim.util.GeoLocation} will * be changed from the original, it is critical that * {@link com.kosherjava.zmanim.AstronomicalCalendar#getLocalDate()}. - * {@link java.util.Calendar#setTimeZone(TimeZone) setTimeZone(TimeZone)} be called in order for the - * AstronomicalCalendar to output times in the expected offset after being cloned. * * @see java.lang.Object#clone() */ diff --git a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java index 966137c7..19f06be3 100644 --- a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java @@ -16,6 +16,8 @@ package com.kosherjava.zmanim; import java.time.Instant; +import java.time.LocalDate; + import com.kosherjava.zmanim.hebrewcalendar.JewishCalendar; import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; @@ -399,9 +401,9 @@ public Instant getChatzos() { * zmaniyos after sunrise. See The Definition * of Chatzos for a detailed explanation of the ways to calculate Chatzos. * - * @see com.kosherjava.zmanim.util.NOAACalculator#getUTCNoon(ZonedDateTime, GeoLocation) - * @see com.kosherjava.zmanim.util.SunTimesCalculator#getUTCNoon(ZonedDateTime, GeoLocation) - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(ZonedDateTime, GeoLocation) + * @see com.kosherjava.zmanim.util.NOAACalculator#getUTCNoon(LocalDate, GeoLocation) + * @see com.kosherjava.zmanim.util.SunTimesCalculator#getUTCNoon(LocalDate, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) * @see AstronomicalCalendar#getSunTransit(Instant, Instant) * @see #getChatzos() * @see #getSunTransit() diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java index d1dae9fd..ddf05f3d 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java @@ -1276,7 +1276,7 @@ public Instant getMoladAsInstant() { * @return the Date representing the moment 3 days after the molad. * * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana3Days() - * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana3Days(Date, Date) + * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana3Days(Instant, Instant) */ public Instant getTchilasZmanKidushLevana3Days() { return getMoladAsInstant().plus(Duration.ofHours(72)); // 3 days after the molad @@ -1292,7 +1292,7 @@ public Instant getTchilasZmanKidushLevana3Days() { * @return the Date representing the moment 7 days after the molad. * * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana7Days() - * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana7Days(Date, Date) + * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana7Days(Instant, Instant) */ public Instant getTchilasZmanKidushLevana7Days() { return getMoladAsInstant().plus(Duration.ofHours(168)); // 7 days after the molad @@ -1311,7 +1311,7 @@ public Instant getTchilasZmanKidushLevana7Days() { * * @see #getSofZmanKidushLevana15Days() * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevanaBetweenMoldos() - * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevanaBetweenMoldos(Date, Date) + * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevanaBetweenMoldos(Instant, Instant) */ public Instant getSofZmanKidushLevanaBetweenMoldos() { Instant molad = getMoladAsInstant(); @@ -1342,7 +1342,7 @@ public Instant getSofZmanKidushLevanaBetweenMoldos() { * @return the Date representing the moment 15 days after the molad. * @see #getSofZmanKidushLevanaBetweenMoldos() * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevana15Days() - * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevana15Days(Date, Date) + * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevana15Days(Instant, Instant) */ public Instant getSofZmanKidushLevana15Days() { return getMoladAsInstant().plus(Duration.ofHours(24 * 15)); diff --git a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java index 094674de..fe445bf7 100644 --- a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java @@ -70,7 +70,7 @@ public String getCalculatorName() { } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(LocalDate, GeoLocation, double, boolean) */ public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; @@ -82,7 +82,7 @@ public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(LocalDate, GeoLocation, double, boolean) */ public double getUTCSunset(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; @@ -312,15 +312,15 @@ private static double getSunHourAngle(double latitude, double solarDeclination, } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(ZonedDateTime, GeoLocation) */ public double getSolarElevation(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { return getSolarElevationAzimuth(zonedDateTime, geoLocation, false); } - + /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(ZonedDateTime, GeoLocation) */ public double getSolarAzimuth(ZonedDateTime zonedDateTime, GeoLocation geoLocation) { return getSolarElevationAzimuth(zonedDateTime, geoLocation, true); @@ -332,7 +332,7 @@ public double getSolarAzimuth(ZonedDateTime zonedDateTime, GeoLocation geoLocati * and time. Can be negative if the sun is below the horizon. Elevation is based on sea-level and is not * adjusted for altitude. * - * @param localDate + * @param zonedDateTime * time of calculation * @param geoLocation * The location for calculating the elevation or azimuth. @@ -340,8 +340,8 @@ public double getSolarAzimuth(ZonedDateTime zonedDateTime, GeoLocation geoLocati * true for azimuth, false for elevation * @return solar elevation or azimuth in degrees. * - * @see #getSolarElevation(Calendar, GeoLocation) - * @see #getSolarAzimuth(Calendar, GeoLocation) + * @see #getSolarElevation(ZonedDateTime, GeoLocation) + * @see #getSolarAzimuth(ZonedDateTime, GeoLocation) */ private double getSolarElevationAzimuth(ZonedDateTime zonedDateTime, GeoLocation geoLocation, boolean isAzimuth) { @@ -394,7 +394,7 @@ private double getSolarElevationAzimuth(ZonedDateTime zonedDateTime, GeoLocation * Other calculators may return a more simplified calculation of halfway between sunrise and sunset. See The Definition of Chatzos for details on * solar noon calculations. - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) * @see #getSolarNoonMidnightUTC(double, double, SolarEvent) * * @param localDate @@ -418,7 +418,7 @@ public double getUTCNoon(LocalDate localDate, GeoLocation geoLocation) { * simplified calculation of halfway between sunrise and sunset. See The Definition of Chatzos for details on * solar noon / midnight calculations. - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) * @see #getSolarNoonMidnightUTC(double, double, SolarEvent) * * @param localDate @@ -449,8 +449,8 @@ public double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation) { * * @return the time in minutes from zero UTC * - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) - * @see #getUTCNoon(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) + * @see #getUTCNoon(LocalDate, GeoLocation) */ private static double getSolarNoonMidnightUTC(double julianDay, double longitude, SolarEvent solarEvent) { julianDay = (solarEvent == SolarEvent.NOON) ? julianDay : julianDay + 0.5; diff --git a/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java b/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java index 0512d6a0..19b75418 100644 --- a/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java @@ -47,7 +47,7 @@ public String getCalculatorName() { } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(LocalDate, GeoLocation, double, boolean) */ public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; @@ -56,7 +56,7 @@ public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(LocalDate, GeoLocation, double, boolean */ public double getUTCSunset(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation) { double elevation = adjustForElevation ? geoLocation.getElevation() : 0; @@ -263,7 +263,7 @@ private static double getTimeUTC(LocalDate localDate, GeoLocation geoLocation, d * {@link NOAACalculator}, the default calculator, returns true solar noon. See The Definition of Chatzos for details on solar * noon calculations. - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) * @see NOAACalculator * * @param localDate @@ -293,7 +293,7 @@ public double getUTCNoon(LocalDate localDate, GeoLocation geoLocation) { * {@link NOAACalculator}, the default calculator, returns true solar noon. See The Definition of Chatzos for details on solar * noon calculations. - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation) * @see NOAACalculator * * @param localDate @@ -308,14 +308,14 @@ public double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation) { } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(ZonedDateTime, GeoLocation) */ public double getSolarAzimuth(ZonedDateTime zdt, GeoLocation geoLocation) { throw new UnsupportedOperationException("The SunTimesCalculator class does not implement the getSolarAzimuth method. Use the NOAACalculator instead."); } /** - * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation) + * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(ZonedDateTime, GeoLocation) */ public double getSolarElevation(ZonedDateTime zdt, GeoLocation geoLocation) { throw new UnsupportedOperationException("The SunTimesCalculator class does not implement the getSolarElevation method. Use the NOAACalculator instead."); From 0f99d98c2de325c6cd8940c85b61b165b0787b11 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 16 Mar 2026 15:50:20 -0400 Subject: [PATCH 3/9] Rename getSunrise to getSunriseWithElevation. Fix javadoc on `isUseElevation` --- .../zmanim/AstronomicalCalendar.java | 97 +++++++---- .../zmanim/ComprehensiveZmanimCalendar.java | 152 +++++++++--------- .../com/kosherjava/zmanim/ZmanimCalendar.java | 82 +++++----- .../RegressionTestFileWriter.java | 3 +- 4 files changed, 187 insertions(+), 147 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index 270f79c0..618f7336 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -23,15 +23,13 @@ import java.time.LocalTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.TimeZone; import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; import com.kosherjava.zmanim.util.ZmanimFormatter; /** - * A Java calendar that calculates astronomical times such as {@link #getSunrise() sunrise}, {@link #getSunset() + * A Java calendar that calculates astronomical times such as {@link #getSunriseWithElevation() sunrise}, {@link #getSunsetWithElevation() * sunset} and twilight times. This class contains a {@link #getLocalDate() zonedDateTime} and can therefore use the standard * Calendar functionality to change dates etc. The calculation engine used to calculate the astronomical times can be * changed to a different implementation by implementing the abstract {@link AstronomicalCalculator} and setting it with @@ -112,7 +110,7 @@ public class AstronomicalCalendar implements Cloneable { private AstronomicalCalculator astronomicalCalculator; /** - * The getSunrise method returns a Instant representing the + * The getSunriseWithElevation method returns a Instant representing the * {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunrise time. The zenith used * for the calculation uses {@link #GEOMETRIC_ZENITH geometric zenith} of 90° plus * {@link AstronomicalCalculator#getElevationAdjustment(double)}. This is adjusted by the @@ -127,7 +125,7 @@ public class AstronomicalCalendar implements Cloneable { * @see #getSeaLevelSunrise() * @see AstronomicalCalendar#getUTCSunrise */ - public Instant getSunrise() { + public Instant getSunriseWithElevation() { double sunrise = getUTCSunrise(GEOMETRIC_ZENITH); if (Double.isNaN(sunrise)) { return null; @@ -135,6 +133,28 @@ public Instant getSunrise() { return getInstantFromTime(sunrise, SolarEvent.SUNRISE); } } + /** + * @deprecated Use {@link #getSunriseWithElevation()} instead. + * This method already accounts for the observer's elevation, but the name + * does not clearly indicate this behavior. The replacement method has a + * clearer and more descriptive name. + * + * @return the Instant representing the exact sunrise time. If the calculation can't be computed such as + * in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it + * does not set, a null will be returned. See detailed explanation on top of the page. + * @see AstronomicalCalculator#adjustZenith + * @see #getSeaLevelSunrise() + * @see AstronomicalCalendar#getUTCSunrise + */ + @Deprecated(forRemoval = false) + public Instant getSunrise() { + double sunrise = getUTCSunrise(GEOMETRIC_ZENITH); + if (Double.isNaN(sunrise)) { + return null; + } else { + return getInstantFromTime(sunrise, SolarEvent.SUNRISE); + } + } /** * A method that returns the sunrise without {@link AstronomicalCalculator#getElevationAdjustment(double) elevation @@ -145,7 +165,7 @@ public Instant getSunrise() { * @return the Instant representing the exact sea-level sunrise time. If the calculation can't be computed * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one * where it does not set, a null will be returned. See detailed explanation on top of the page. - * @see AstronomicalCalendar#getSunrise + * @see AstronomicalCalendar#getSunriseWithElevation * @see AstronomicalCalendar#getUTCSeaLevelSunrise * @see #getSeaLevelSunset() */ @@ -196,18 +216,40 @@ public Instant getBeginAstronomicalTwilight() { return getSunriseOffsetByDegrees(ASTRONOMICAL_ZENITH); } + /** + * The getSunsetWithElevation method returns a Instant representing the + * {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunset time. The zenith used for + * the calculation uses {@link #GEOMETRIC_ZENITH geometric zenith} of 90° plus + * {@link AstronomicalCalculator#getElevationAdjustment(double)}. This is adjusted by the + * {@link AstronomicalCalculator} to add approximately 50/60 of a degree to account for 34 archminutes of refraction + * and 16 archminutes for the sun's radius for a total of {@link AstronomicalCalculator#adjustZenith 90.83333°}. + * See documentation for the specific implementation of the {@link AstronomicalCalculator} that you are using. Note: + * In certain cases the calculates sunset will occur before sunrise. This will typically happen when a timezone + * other than the local timezone is used (calculating Los Angeles sunset using a GMT timezone for example). In this + * case the sunset date will be incremented to the following date. + * + * @return the Instant representing the exact sunset time. If the calculation can't be computed such as in + * the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it + * does not set, a null will be returned. See detailed explanation on top of the page. + * @see AstronomicalCalculator#adjustZenith + * @see #getSeaLevelSunset() + * @see AstronomicalCalendar#getUTCSunset + */ + public Instant getSunsetWithElevation() { + double sunset = getUTCSunset(GEOMETRIC_ZENITH); + if (Double.isNaN(sunset)) { + return null; + } else { + return getInstantFromTime(sunset, SolarEvent.SUNSET); + } + } + /** - * The getSunset method returns a Instant representing the - * {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunset time. The zenith used for - * the calculation uses {@link #GEOMETRIC_ZENITH geometric zenith} of 90° plus - * {@link AstronomicalCalculator#getElevationAdjustment(double)}. This is adjusted by the - * {@link AstronomicalCalculator} to add approximately 50/60 of a degree to account for 34 archminutes of refraction - * and 16 archminutes for the sun's radius for a total of {@link AstronomicalCalculator#adjustZenith 90.83333°}. - * See documentation for the specific implementation of the {@link AstronomicalCalculator} that you are using. Note: - * In certain cases the calculates sunset will occur before sunrise. This will typically happen when a timezone - * other than the local timezone is used (calculating Los Angeles sunset using a GMT timezone for example). In this - * case the sunset date will be incremented to the following date. - * + * @deprecated Use {@link #getSunsetWithElevation()} instead. + * This method already accounts for the observer's elevation, but its name + * does not clearly reflect that behavior. The replacement method provides + * a more accurate and descriptive name. + * * @return the Instant representing the exact sunset time. If the calculation can't be computed such as in * the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it * does not set, a null will be returned. See detailed explanation on top of the page. @@ -215,6 +257,7 @@ public Instant getBeginAstronomicalTwilight() { * @see #getSeaLevelSunset() * @see AstronomicalCalendar#getUTCSunset */ + @Deprecated(forRemoval = false) public Instant getSunset() { double sunset = getUTCSunset(GEOMETRIC_ZENITH); if (Double.isNaN(sunset)) { @@ -233,9 +276,9 @@ public Instant getSunset() { * @return the Instant representing the exact sea-level sunset time. If the calculation can't be computed * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one * where it does not set, a null will be returned. See detailed explanation on top of the page. - * @see AstronomicalCalendar#getSunset + * @see AstronomicalCalendar#getSunsetWithElevation * @see AstronomicalCalendar#getUTCSeaLevelSunset - * @see #getSunset() + * @see #getSunsetWithElevation() */ public Instant getSeaLevelSunset() { double sunset = getUTCSeaLevelSunset(GEOMETRIC_ZENITH); @@ -317,15 +360,15 @@ public static Instant getTimeOffset(Instant time, long offsetMillis) { /** * A utility method that returns the time of an offset by degrees below or above the horizon of - * {@link #getSunrise() sunrise}. Note that the degree offset is from the vertical, so for a calculation of 14° + * {@link #getSunriseWithElevation() sunrise}. Note that the degree offset is from the vertical, so for a calculation of 14° * before sunrise, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter. * * @param offsetZenith - * the degrees before {@link #getSunrise()} to use in the calculation. For time after sunrise use + * the degrees before {@link #getSunriseWithElevation()} to use in the calculation. For time after sunrise use * negative numbers. Note that the degree offset is from the vertical, so for a calculation of 14° * before sunrise, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a * parameter. - * @return The {@link java.time.Instant} of the offset after (or before) {@link #getSunrise()}. If the calculation + * @return The {@link java.time.Instant} of the offset after (or before) {@link #getSunriseWithElevation()}. If the calculation * can't be computed such as in the Arctic Circle where there is at least one day a year where the sun does * not rise, and one where it does not set, a null will be returned. See detailed explanation * on top of the page. @@ -337,15 +380,15 @@ public Instant getSunriseOffsetByDegrees(double offsetZenith) { } /** - * A utility method that returns the time of an offset by degrees below or above the horizon of {@link #getSunset() + * A utility method that returns the time of an offset by degrees below or above the horizon of {@link #getSunsetWithElevation() * sunset}. Note that the degree offset is from the vertical, so for a calculation of 14° after sunset, an * offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter. * * @param offsetZenith - * the degrees after {@link #getSunset()} to use in the calculation. For time before sunset use negative + * the degrees after {@link #getSunsetWithElevation()} to use in the calculation. For time before sunset use negative * numbers. Note that the degree offset is from the vertical, so for a calculation of 14° after * sunset, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter. - * @return The {@link java.time.Instant} of the offset after (or before) {@link #getSunset()}. If the calculation can't + * @return The {@link java.time.Instant} of the offset after (or before) {@link #getSunsetWithElevation()}. If the calculation can't * be computed such as in the Arctic Circle where there is at least one day a year where the sun does not * rise, and one where it does not set, a null will be returned. See detailed explanation on * top of the page. @@ -466,8 +509,8 @@ public long getTemporalHour() { /** * A utility method that will allow the calculation of a temporal (solar) hour based on the sunrise and sunset * passed as parameters to this method. An example of the use of this method would be the calculation of a - * elevation adjusted temporal hour by passing in {@link #getSunrise() sunrise} and - * {@link #getSunset() sunset} as parameters. + * elevation adjusted temporal hour by passing in {@link #getSunriseWithElevation() sunrise} and + * {@link #getSunsetWithElevation() sunset} as parameters. * * @param startOfDay * The start of the day. diff --git a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java index e16acf23..17648b19 100644 --- a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java @@ -17,8 +17,6 @@ import java.time.LocalDate; import java.util.Calendar; // FIXME remove once FORWARD can be refactored. -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.time.Instant; import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; @@ -33,7 +31,7 @@ * API. The real power of this API is the ease in calculating zmanim that are not part of the library. The methods for * zmanim calculations not present in this class or it's superclass {@link ZmanimCalendar} are contained in the * {@link AstronomicalCalendar}, the base class of the calendars in our API since they are generic methods for calculating - * time based on degrees or time before or after {@link #getSunrise() sunrise} and {@link #getSunset() sunset} and are of interest + * time based on degrees or time before or after {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} and are of interest * for calculation beyond zmanim calculations. Here are some examples. *

First create the Calendar for the location you would like to calculate: * @@ -168,7 +166,7 @@ public class ComprehensiveZmanimCalendar extends ZmanimCalendar { /** * The zenith of 10.2° below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for * calculating misheyakir according to some opinions. This calculation is based on the position of the sun - * 45 minutes before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux which * calculates to 10.2° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -179,7 +177,7 @@ public class ComprehensiveZmanimCalendar extends ZmanimCalendar { /** * The zenith of 11° below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for * calculating misheyakir according to some opinions. This calculation is based on the position of the sun - * 48 minutes before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux, which * calculates to 11° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -190,7 +188,7 @@ public class ComprehensiveZmanimCalendar extends ZmanimCalendar { /** * The zenith of 11.5° below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for * calculating misheyakir according to some opinions. This calculation is based on the position of the sun - * 52 minutes before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux, which * calculates to 11.5° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -201,7 +199,7 @@ public class ComprehensiveZmanimCalendar extends ZmanimCalendar { /** * The zenith of 12.85° below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This is used for calculating * misheyakir according to some opinions. This calculation is based on the position of the sun slightly less - * than 57 minutes before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux, which * calculates to 12.85° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -324,7 +322,7 @@ public class ComprehensiveZmanimCalendar extends ZmanimCalendar { /** * The zenith of 6° below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for * calculating tzais / nightfall based on the opinion of the Baal Hatanya. This calculation is based on the - * position of the sun 24 minutes after {@link #getSunset() sunset} in Jerusalem around the equinox / equilux, which * is 6° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -568,11 +566,11 @@ public long getShaahZmanis72Minutes() { /** * Method to return a shaah zmanis (temporal hour) according to the opinion of the Magen Avraham (MGA) based on alos being - * {@link #getAlos72Zmanis() 72} minutes zmaniyos before {@link #getSunrise() sunrise}. This calculation + * {@link #getAlos72Zmanis() 72} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This calculation * divides the day based on the opinion of the MGA that the day runs from dawn to dusk. Dawn for this calculation * is 72 minutes zmaniyos before sunrise and dusk is 72 minutes zmaniyos after sunset. This day * is split into 12 equal parts with each part being a shaah zmanis. This is identical to 1/10th of the day - * from {@link #getSunrise() sunrise} to {@link #getSunset() sunset}. + * from {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset}. * * @return the long millisecond length of a shaah zmanis. If the calculation can't be computed * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one @@ -603,11 +601,11 @@ public long getShaahZmanis90Minutes() { /** * Method to return a shaah zmanis (temporal hour) according to the opinion of the Magen Avraham (MGA) based on alos being - * {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunrise() sunrise}. This calculation divides + * {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This calculation divides * the day based on the opinion of the MGA that the day runs from dawn to dusk. Dawn for this calculation is 90 minutes * zmaniyos before sunrise and dusk is 90 minutes zmaniyos after sunset. This day is split into 12 equal - * parts with each part being a shaah zmanis. This is 1/8th of the day from {@link #getSunrise() sunrise} to - * {@link #getSunset() sunset}. + * parts with each part being a shaah zmanis. This is 1/8th of the day from {@link #getSunriseWithElevation() sunrise} to + * {@link #getSunsetWithElevation() sunset}. * * @return the long millisecond length of a shaah zmanis. If the calculation can't be computed * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one @@ -623,11 +621,11 @@ public long getShaahZmanis90MinutesZmanis() { /** * Method to return a shaah zmanis (temporal hour) according to the opinion of the Magen Avraham (MGA) based on alos being {@link - * #getAlos96Zmanis() 96} minutes zmaniyos before {@link #getSunrise() sunrise}. This calculation divides the + * #getAlos96Zmanis() 96} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This calculation divides the * day based on the opinion of the MGA that the day runs from dawn to dusk. Dawn for this calculation is 96 minutes * zmaniyos before sunrise and dusk is 96 minutes zmaniyos after sunset. This day is split into 12 * equal parts with each part being a shaah zmanis. This is identical to 1/7.5th of the day from - * {@link #getSunrise() sunrise} to {@link #getSunset() sunset}. + * {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset}. * * @return the long millisecond length of a shaah zmanis. If the calculation can't be computed * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one @@ -644,8 +642,8 @@ public long getShaahZmanis96MinutesZmanis() { * Method to return a shaah zmanis (temporal hour) according to the opinion of the * Chacham Yosef Harari-Raful of Yeshivat Ateret Torah calculated with alos being 1/10th * of sunrise to sunset day, or {@link #getAlos72Zmanis() 72} minutes zmaniyos of such a day before - * {@link #getSunrise() sunrise}, and tzais is usually calculated as {@link #getTzaisAteretTorah() 40 - * minutes} (configurable to any offset via {@link #setAteretTorahSunsetOffset(double)}) after {@link #getSunset() + * {@link #getSunriseWithElevation() sunrise}, and tzais is usually calculated as {@link #getTzaisAteretTorah() 40 + * minutes} (configurable to any offset via {@link #setAteretTorahSunsetOffset(double)}) after {@link #getSunsetWithElevation() * sunset}. This day is split into 12 equal parts with each part being a shaah zmanis. Note that with this * system, chatzos (midday) will not be the point that the sun is {@link #getSunTransit() halfway across * the sky}. @@ -746,11 +744,11 @@ public long getShaahZmanis120Minutes() { /** * Method to return a shaah zmanis (temporal hour) according to the opinion of the Magen Avraham (MGA) based on alos being {@link - * #getAlos120Zmanis() 120} minutes zmaniyos before {@link #getSunrise() sunrise}. This calculation divides + * #getAlos120Zmanis() 120} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This calculation divides * the day based on the opinion of the MGA that the day runs from dawn to dusk. Dawn for this calculation is * 120 minutes zmaniyos before sunrise and dusk is 120 minutes zmaniyos after sunset. This day is * split into 12 equal parts with each part being a shaah zmanis. This is identical to 1/6th of the day from - * {@link #getSunrise() sunrise} to {@link #getSunset() sunset}. Since zmanim that use this method are + * {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset}. Since zmanim that use this method are * extremely late or early and at a point when the sky is a long time past the 18° point where the darkest point * is reached, zmanim that use this should only be used lechumra such as delaying the start of * nighttime mitzvos. @@ -820,7 +818,7 @@ public Instant getPlagHamincha120Minutes() { } /** - * Method to return alos (dawn) calculated as 60 minutes before {@link #getSunrise() sunrise} or + * Method to return alos (dawn) calculated as 60 minutes before {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting). This is the * time to walk the distance of 4 mil at 15 minutes a mil. This seems to be the opinion of the @@ -860,7 +858,7 @@ public Instant getAlos60() { * sunrise. This is based on an 18-minute mil so the time for 4 mil is * 72 minutes which is 1/10th of a day (12 * 60 = 720) based on the day being from {@link #getSeaLevelSunrise() sea - * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() + * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()} setting). The actual calculation is {@link * #getElevationAdjustedSunrise()} - ({@link #getShaahZmanisGra()} * 1.2). This calculation is used in the calendars * published by the Hisachdus Harabanim D'Artzos Habris @@ -877,7 +875,7 @@ public Instant getAlos72Zmanis() { } /** - * Method to return alos (dawn) calculated using 96 minutes before {@link #getSunrise() sunrise} or + * Method to return alos (dawn) calculated using 96 minutes before {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting) that is based * on the time to walk the distance of 4 mil at 24 minutes a mil. @@ -897,12 +895,12 @@ public Instant getAlos96() { /** * Method to return alos (dawn) calculated using 90 minutes zmaniyos or 1/8th of the day before - * {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link + * {@link #getSunriseWithElevation() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link * #isUseElevation()} setting). This is based on a 22.5-minute mil so the time for 4 * mil is 90 minutes which is 1/8th of a day (12 * 60) / 8 = 90. The day is calculated from {@link - * #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunrise() - * sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()}. The actual calculation used + * #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() + * sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()}. The actual calculation used * is {@link #getElevationAdjustedSunrise()} - ({@link #getShaahZmanisGra()} * 1.5). * * @return the Instant representing the time. If the calculation can't be computed such as in the Arctic @@ -917,11 +915,11 @@ public Instant getAlos90Zmanis() { /** * This method returns alos (dawn) calculated using 96 minutes zmaniyos or 1/7.5th of the day before - * {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link + * {@link #getSunriseWithElevation() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link * #isUseElevation()} setting). This is based on a 24-minute mil so the time for 4 mil is 96 * minutes which is 1/7.5th of a day (12 * 60 / 7.5 = 96). The day is calculated from {@link #getSeaLevelSunrise() sea - * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() + * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()}. The actual calculation used is {@link #getElevationAdjustedSunrise()} * - ({@link #getShaahZmanisGra()} * 1.6). * @@ -936,7 +934,7 @@ public Instant getAlos96Zmanis() { } /** - * Method to return alos (dawn) calculated using 90 minutes before {@link #getSunrise() sunrise} or + * Method to return alos (dawn) calculated using 90 minutes before {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting) based * on the time to walk the distance of 4 mil at 22.5 minutes a @@ -956,7 +954,7 @@ public Instant getAlos90() { /** * This method should be used lechumra only and returns alos (dawn) calculated using 120 minutes - * before {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link + * before {@link #getSunriseWithElevation() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link * #isUseElevation()} setting) based on the time to walk the distance of 5 mil (Ula) at 24 minutes a * mil. Time based offset calculations for alos are based on the* opinion of the lechumra only and method returns alos (dawn) calculated using - * 120 minutes zmaniyos or 1/6th of the day before {@link #getSunrise() sunrise} or {@link + * 120 minutes zmaniyos or 1/6th of the day before {@link #getSunriseWithElevation() sunrise} or {@link * #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting). This is based * on a 24-minute mil so * the time for 5 mil is 120 minutes which is 1/6th of a day (12 * 60 / 6 = 120). The day is calculated * from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or - * {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()}. The - * actual calculation used is {@link #getSunrise()} - ({@link #getShaahZmanisGra()} * 2). Since this time is + * {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()}. The + * actual calculation used is {@link #getSunriseWithElevation()} - ({@link #getShaahZmanisGra()} * 2). Since this time is * extremely early, it should only be used lechumra, such * as not eating after this time on a fast day, and not as the start time for mitzvos that can only be * performed during the day. @@ -1117,7 +1115,7 @@ public Instant getAlos16Point1Degrees() { /** * This method returns misheyakir based on the position of the sun {@link #ZENITH_12_POINT_85 12.85°} * below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This is based on the position of the sun slightly - * later than 57 minutes before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux. This * zman is mentioned for use bish'as hadchak in the Birur Halacha Tinyana and misheyakir based on the position of the sun when it is {@link #ZENITH_11_DEGREES * 11.5°} below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for calculating * misheyakir according to some opinions. This calculation is based on the position of the sun 52 minutes - * before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux, * which calculates to 11.5° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @todo recalculate. @@ -1181,7 +1179,7 @@ public Instant getMisheyakir11Point5Degrees() { * This method returns misheyakir based on the position of the sun when it is {@link #ZENITH_11_DEGREES * 11°} below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for calculating * misheyakir according to some opinions. This calculation is based on the position of the sun 48 minutes - * before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux, * which calculates to 11° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -1199,7 +1197,7 @@ public Instant getMisheyakir11Degrees() { * This method returns misheyakir based on the position of the sun when it is {@link #ZENITH_10_POINT_2 * 10.2°} below {@link #GEOMETRIC_ZENITH geometric zenith} (90°). This calculation is used for calculating * misheyakir according to some opinions. This calculation is based on the position of the sun 45 minutes - * before {@link #getSunrise() sunrise} in Jerusalem around the equinox which calculates * to 10.2° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -1277,7 +1275,7 @@ public Instant getMisheyakir9Point5Degrees() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based on - * alos being {@link #getAlos19Point8Degrees() 19.8°} before {@link #getSunrise() sunrise}. This + * alos being {@link #getAlos19Point8Degrees() 19.8°} before {@link #getSunriseWithElevation() sunrise}. This * time is 3 {@link #getShaahZmanis19Point8Degrees() shaos zmaniyos} (solar hours) after {@link * #getAlos19Point8Degrees() dawn} based on the opinion of the MGA that the day is calculated from dawn to nightfall * with both being 19.8° below sunrise or sunset. This returns the time of 3 * @@ -1297,7 +1295,7 @@ public Instant getSofZmanShmaMGA19Point8Degrees() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunrise() sunrise}. This time + * on alos being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunriseWithElevation() sunrise}. This time * is 3 {@link #getShaahZmanis16Point1Degrees() shaos zmaniyos} (solar hours) after * {@link #getAlos16Point1Degrees() dawn} based on the opinion of the MGA that the day is calculated from * dawn to nightfall with both being 16.1° below sunrise or sunset. This returns the time of @@ -1317,7 +1315,7 @@ public Instant getSofZmanShmaMGA16Point1Degrees() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos18Degrees() 18°} before {@link #getSunrise() sunrise}. This time is 3 + * on alos being {@link #getAlos18Degrees() 18°} before {@link #getSunriseWithElevation() sunrise}. This time is 3 * {@link #getShaahZmanis18Degrees() shaos zmaniyos} (solar hours) after {@link #getAlos18Degrees() dawn} * based on the opinion of the MGA that the day is calculated from dawn to nightfall with both being 18° * below sunrise or sunset. This returns the time of 3 * {@link #getShaahZmanis18Degrees()} after @@ -1337,7 +1335,7 @@ public Instant getSofZmanShmaMGA18Degrees() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based on - * alos being {@link #getAlos72() 72} minutes before {@link #getSunrise() sunrise}. This time is 3 {@link + * alos being {@link #getAlos72() 72} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 3 {@link * #getShaahZmanis72Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos72() dawn} based on the opinion * of the MGA that the day is calculated from a {@link #getAlos72() dawn} of 72 minutes before sunrise to * {@link #getTzais72() nightfall} of 72 minutes after sunset. This returns the time of 3 * {@link @@ -1361,7 +1359,7 @@ public Instant getSofZmanShmaMGA72Minutes() { * This method returns the latest zman krias shema (time to recite Shema in the morning) according * to the opinion of the Magen Avraham (MGA) based * on alos being {@link #getAlos72Zmanis() 72} minutes zmaniyos, or 1/10th of the day before - * {@link #getSunrise() sunrise}. This time is 3 {@link #getShaahZmanis90MinutesZmanis() shaos zmaniyos} + * {@link #getSunriseWithElevation() sunrise}. This time is 3 {@link #getShaahZmanis90MinutesZmanis() shaos zmaniyos} * (solar hours) after {@link #getAlos72Zmanis() dawn} based on the opinion of the MGA that the day is calculated * from a {@link #getAlos72Zmanis() dawn} of 72 minutes zmaniyos, or 1/10th of the day before * {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getTzais72Zmanis() nightfall} of 72 minutes @@ -1383,7 +1381,7 @@ public Instant getSofZmanShmaMGA72MinutesZmanis() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according * to the opinion of the Magen Avraham (MGA) based on - * alos being {@link #getAlos90() 90} minutes before {@link #getSunrise() sunrise}. This time is 3 + * alos being {@link #getAlos90() 90} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 3 * {@link #getShaahZmanis90Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos90() dawn} based on * the opinion of the MGA that the day is calculated from a {@link #getAlos90() dawn} of 90 minutes before sunrise to * {@link #getTzais90() nightfall} of 90 minutes after sunset. This returns the time of 3 * @@ -1404,7 +1402,7 @@ public Instant getSofZmanShmaMGA90Minutes() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunrise() + * on alos being {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunriseWithElevation() * sunrise}. This time is 3 {@link #getShaahZmanis90MinutesZmanis() shaos zmaniyos} (solar hours) after * {@link #getAlos90Zmanis() dawn} based on the opinion of the MGA that the day is calculated from a {@link * #getAlos90Zmanis() dawn} of 90 minutes zmaniyos before sunrise to {@link #getTzais90Zmanis() nightfall} @@ -1426,7 +1424,7 @@ public Instant getSofZmanShmaMGA90MinutesZmanis() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos96() 96} minutes before {@link #getSunrise() sunrise}. This time is 3 + * on alos being {@link #getAlos96() 96} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 3 * {@link #getShaahZmanis96Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos96() dawn} based on * the opinion of the MGA that the day is calculated from a {@link #getAlos96() dawn} of 96 minutes before * sunrise to {@link #getTzais96() nightfall} of 96 minutes after sunset. This returns the time of 3 * {@link @@ -1447,7 +1445,7 @@ public Instant getSofZmanShmaMGA96Minutes() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos90Zmanis() 96} minutes zmaniyos before {@link #getSunrise() + * on alos being {@link #getAlos90Zmanis() 96} minutes zmaniyos before {@link #getSunriseWithElevation() * sunrise}. This time is 3 {@link #getShaahZmanis96MinutesZmanis() shaos zmaniyos} (solar hours) after * {@link #getAlos96Zmanis() dawn} based on the opinion of the MGA that the day is calculated from a {@link * #getAlos96Zmanis() dawn} of 96 minutes zmaniyos before sunrise to {@link #getTzais90Zmanis() nightfall} @@ -1498,7 +1496,7 @@ public Instant getSofZmanShma3HoursBeforeChatzos() { /** * This method returns the latest zman krias shema (time to recite Shema in the morning) according to the * opinion of the Magen Avraham (MGA) based - * on alos being {@link #getAlos120() 120} minutes or 1/6th of the day before {@link #getSunrise() sunrise}. + * on alos being {@link #getAlos120() 120} minutes or 1/6th of the day before {@link #getSunriseWithElevation() sunrise}. * This time is 3 {@link #getShaahZmanis120Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos120() * dawn} based on the opinion of the MGA that the day is calculated from a {@link #getAlos120() dawn} of 120 minutes * before sunrise to {@link #getTzais120() nightfall} of 120 minutes after sunset. This returns the time of 3 @@ -1569,7 +1567,7 @@ public Instant getSofZmanShmaAlos16Point1ToTzaisGeonim7Point083Degrees() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos19Point8Degrees() 19.8°} before {@link #getSunrise() sunrise}. This time + * alos being {@link #getAlos19Point8Degrees() 19.8°} before {@link #getSunriseWithElevation() sunrise}. This time * is 4 {@link #getShaahZmanis19Point8Degrees() shaos zmaniyos} (solar hours) after {@link * #getAlos19Point8Degrees() dawn} based on the opinion of the MGA that the day is calculated from dawn to * nightfall with both being 19.8° below sunrise or sunset. This returns the time of 4 * {@link @@ -1591,7 +1589,7 @@ public Instant getSofZmanTfilaMGA19Point8Degrees() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunrise() sunrise}. This time + * alos being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunriseWithElevation() sunrise}. This time * is 4 {@link #getShaahZmanis16Point1Degrees() shaos zmaniyos} (solar hours) after {@link * #getAlos16Point1Degrees() dawn} based on the opinion of the MGA that the day is calculated from dawn to * nightfall with both being 16.1° below sunrise or sunset. This returns the time of 4 * {@link @@ -1612,7 +1610,7 @@ public Instant getSofZmanTfilaMGA16Point1Degrees() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos18Degrees() 18°} before {@link #getSunrise() sunrise}. This time is 4 + * alos being {@link #getAlos18Degrees() 18°} before {@link #getSunriseWithElevation() sunrise}. This time is 4 * {@link #getShaahZmanis18Degrees() shaos zmaniyos} (solar hours) after {@link #getAlos18Degrees() dawn} * based on the opinion of the MGA that the day is calculated from dawn to nightfall with both being 18° * below sunrise or sunset. This returns the time of 4 * {@link #getShaahZmanis18Degrees()} after @@ -1633,7 +1631,7 @@ public Instant getSofZmanTfilaMGA18Degrees() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos72() 72} minutes before {@link #getSunrise() sunrise}. This time is 4 + * alos being {@link #getAlos72() 72} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 4 * {@link #getShaahZmanis72Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos72() dawn} based on * the opinion of the MGA that the day is calculated from a {@link #getAlos72() dawn} of 72 minutes before * sunrise to {@link #getTzais72() nightfall} of 72 minutes after sunset. This returns the time of 4 * @@ -1655,7 +1653,7 @@ public Instant getSofZmanTfilaMGA72Minutes() { /** * This method returns the latest zman tfila (time to the morning prayers) according to the opinion of the * Magen Avraham (MGA) based on alos - * being {@link #getAlos72Zmanis() 72} minutes zmaniyos before {@link #getSunrise() sunrise}. This time is 4 + * being {@link #getAlos72Zmanis() 72} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This time is 4 * {@link #getShaahZmanis72MinutesZmanis() shaos zmaniyos} (solar hours) after {@link #getAlos72Zmanis() dawn} * based on the opinion of the MGA that the day is calculated from a {@link #getAlos72Zmanis() dawn} of 72 * minutes zmaniyos before sunrise to {@link #getTzais72Zmanis() nightfall} of 72 minutes zmaniyos @@ -1675,7 +1673,7 @@ public Instant getSofZmanTfilaMGA72MinutesZmanis() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos90() 90} minutes before {@link #getSunrise() sunrise}. This time is 4 + * alos being {@link #getAlos90() 90} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 4 * {@link #getShaahZmanis90Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos90() dawn} based on * the opinion of the MGA that the day is calculated from a {@link #getAlos90() dawn} of 90 minutes before sunrise to * {@link #getTzais90() nightfall} of 90 minutes after sunset. This returns the time of 4 * @@ -1695,7 +1693,7 @@ public Instant getSofZmanTfilaMGA90Minutes() { /** * This method returns the latest zman tfila (time to the morning prayers) according to the opinion of the * Magen Avraham (MGA) based on alos - * being {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunrise() sunrise}. This time is + * being {@link #getAlos90Zmanis() 90} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This time is * 4 {@link #getShaahZmanis90MinutesZmanis() shaos zmaniyos} (solar hours) after {@link #getAlos90Zmanis() * dawn} based on the opinion of the MGA that the day is calculated from a {@link #getAlos90Zmanis() dawn} * of 90 minutes zmaniyos before sunrise to {@link #getTzais90Zmanis() nightfall} of 90 minutes @@ -1716,7 +1714,7 @@ public Instant getSofZmanTfilaMGA90MinutesZmanis() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos96() 96} minutes before {@link #getSunrise() sunrise}. This time is 4 + * alos being {@link #getAlos96() 96} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 4 * {@link #getShaahZmanis96Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos96() dawn} based on * the opinion of the MGA that the day is calculated from a {@link #getAlos96() dawn} of 96 minutes before * sunrise to {@link #getTzais96() nightfall} of 96 minutes after sunset. This returns the time of 4 * @@ -1736,7 +1734,7 @@ public Instant getSofZmanTfilaMGA96Minutes() { /** * This method returns the latest zman tfila (time to the morning prayers) according to the opinion of the * Magen Avraham (MGA) based on alos - * being {@link #getAlos96Zmanis() 96} minutes zmaniyos before {@link #getSunrise() sunrise}. This time is + * being {@link #getAlos96Zmanis() 96} minutes zmaniyos before {@link #getSunriseWithElevation() sunrise}. This time is * 4 {@link #getShaahZmanis96MinutesZmanis() shaos zmaniyos} (solar hours) after {@link #getAlos96Zmanis() * dawn} based on the opinion of the MGA that the day is calculated from a {@link #getAlos96Zmanis() dawn} * of 96 minutes zmaniyos before sunrise to {@link #getTzais96Zmanis() nightfall} of 96 minutes @@ -1757,7 +1755,7 @@ public Instant getSofZmanTfilaMGA96MinutesZmanis() { /** * This method returns the latest zman tfila (time to recite the morning prayers) according to the opinion * of the Magen Avraham (MGA) based on - * alos being {@link #getAlos120() 120} minutes before {@link #getSunrise() sunrise} . This time is 4 + * alos being {@link #getAlos120() 120} minutes before {@link #getSunriseWithElevation() sunrise} . This time is 4 * {@link #getShaahZmanis120Minutes() shaos zmaniyos} (solar hours) after {@link #getAlos120() dawn} * based on the opinion of the MGA that the day is calculated from a {@link #getAlos120() dawn} of 120 * minutes before sunrise to {@link #getTzais120() nightfall} of 120 minutes after sunset. This returns the time of @@ -2250,7 +2248,7 @@ public Instant getPlagHamincha18Degrees() { /** * This method should be used lechumra only and returns the time of plag hamincha based on the opinion - * that the day starts at {@link #getAlos16Point1Degrees() alos 16.1°} and ends at {@link #getSunset() sunset}. + * that the day starts at {@link #getAlos16Point1Degrees() alos 16.1°} and ends at {@link #getSunsetWithElevation() sunset}. * 10.75 shaos zmaniyos are calculated based on this day and added to {@link #getAlos16Point1Degrees() * alos} to reach this time. This time is 10.75 shaos zmaniyos (temporal hours) after {@link * #getAlos16Point1Degrees() dawn} based on the opinion that the day is calculated from a {@link #getAlos16Point1Degrees() @@ -2784,7 +2782,7 @@ public Instant getTzaisGeonim9Point75Degrees() { * "https://he.wikipedia.org/wiki/%D7%9E%D7%9C%D7%9B%D7%99%D7%90%D7%9C_%D7%A6%D7%91%D7%99_%D7%98%D7%A0%D7%A0%D7%91%D7%95%D7%99%D7%9D" * >Divrei Malkiel that the time to walk the distance of a mil is 15 minutes, for a total of 60 minutes - * for 4 mil after {@link #getSunset() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link + * for 4 mil after {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link * #isUseElevation()} setting). See detailed documentation explaining the 60 minute concept at {@link #getAlos60()}. * * @return the Instant representing 60 minutes after sea level sunset. If the calculation can't be @@ -3047,7 +3045,7 @@ public Instant getTzais96Zmanis() { } /** - * Method to return tzais (dusk) calculated as 90 minutes after {@link #getSunset() sunset} or {@link + * Method to return tzais (dusk) calculated as 90 minutes after {@link #getSunsetWithElevation() sunset} or {@link * #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} setting). This method returns * tzais (nightfall) based on the opinion of the Magen Avraham that the time to walk the distance of a mil according to the mil * according to the Rambam's opinion is 2/5 of an hour (24 minutes) * for a total of 120 minutes based on the opinion of Ula who calculated tzais as 5 mil after {@link - * #getSunset() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} setting). + * #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} setting). * A similar calculation {@link #getTzais26Degrees()} uses degree-based calculations based on this 120 minute calculation. * Since the zman is extremely late and at a point that is long past the 18° point where the darkest point is * reached, it should only be used lechumra, such as delaying the start of nighttime mitzvos. @@ -3192,7 +3190,7 @@ public Instant getTzais19Point8Degrees() { } /** - * A method to return tzais (dusk) calculated as 96 minutes after {@link #getSunset() sunset} or {@link + * A method to return tzais (dusk) calculated as 96 minutes after {@link #getSunsetWithElevation() sunset} or {@link * #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} setting). For information on how * this is calculated see the comments on {@link #getAlos96()}. * @@ -3583,7 +3581,7 @@ public Instant getSofZmanAchilasChametzGRA() { /** * This method returns the latest time one is allowed eating chametz on Erev Pesach according to the * opinion of the Magen Avraham (MGA) based on alos - * being {@link #getAlos72() 72} minutes before {@link #getSunrise() sunrise}. This time is identical to the + * being {@link #getAlos72() 72} minutes before {@link #getSunriseWithElevation() sunrise}. This time is identical to the * {@link #getSofZmanTfilaMGA72Minutes() Sof zman tfilah MGA 72 minutes}. This time is 4 {@link #getShaahZmanisMGA() * shaos zmaniyos} (temporal hours) after {@link #getAlos72() dawn} based on the opinion of the MGA that the day is * calculated from a {@link #getAlos72() dawn} of 72 minutes before sunrise to {@link #getTzais72() nightfall} of 72 minutes @@ -3613,7 +3611,7 @@ public Instant getSofZmanAchilasChametzMGA72Minutes() { /** * This method returns the latest time one is allowed eating chametz on Erev Pesach according to the * opinion of the Magen Avraham (MGA) based on alos - * being {@link #getAlos72Zmanis() 72 zmaniyos} minutes before {@link #getSunrise() sunrise}. This time is identical to the + * being {@link #getAlos72Zmanis() 72 zmaniyos} minutes before {@link #getSunriseWithElevation() sunrise}. This time is identical to the * {@link #getSofZmanTfilaMGA72MinutesZmanis() Sof zman tfilah MGA 72 minutes zmanis}. This time is 4 {@link #getShaahZmanis72MinutesZmanis() * shaos zmaniyos} (temporal hours) after {@link #getAlos72() dawn} based on the opinion of the MGA that the day is * calculated from a {@link #getAlos72Zmanis() dawn} of 72 minutes zmanis before sunrise to {@link #getTzais72Zmanis() nightfall} of 72 minutes zmanis @@ -3644,7 +3642,7 @@ public Instant getSofZmanAchilasChametzMGA72MinutesZmanis() { /** * This method returns the latest time one is allowed eating chametz on Erev Pesach according to the * opinion of the Magen Avraham (MGA) based on alos - * being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunrise() sunrise}. This time is 4 {@link + * being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunriseWithElevation() sunrise}. This time is 4 {@link * #getShaahZmanis16Point1Degrees() shaos zmaniyos} (solar hours) after {@link #getAlos16Point1Degrees() dawn} * based on the opinion of the MGA that the day is calculated from dawn to nightfall with both being 16.1° * below sunrise or sunset. This returns the time of 4 {@link #getShaahZmanis16Point1Degrees()} after @@ -3700,7 +3698,7 @@ public Instant getSofZmanBiurChametzGRA() { * FIXME adjust for synchronous * This method returns the latest time for burning chametz on Erev Pesach according to the opinion of * the Magen Avraham (MGA) based on alos - * being {@link #getAlos72() 72} minutes before {@link #getSunrise() sunrise}. This time is 5 {@link + * being {@link #getAlos72() 72} minutes before {@link #getSunriseWithElevation() sunrise}. This time is 5 {@link * #getShaahZmanisMGA() shaos zmaniyos} (temporal hours) after {@link #getAlos72() dawn} based on the opinion of * the MGA that the day is calculated from a {@link #getAlos72() dawn} of 72 minutes before sunrise to {@link * #getTzais72() nightfall} of 72 minutes after sunset. This returns the time of 5 * {@link #getShaahZmanisMGA()} after @@ -3728,7 +3726,7 @@ public Instant getSofZmanBiurChametzMGA72Minutes() { * FIXME adjust for synchronous * This method returns the latest time for burning chametz on Erev Pesach according to the opinion of * the Magen Avraham (MGA) based on alos - * being {@link #getAlos72Zmanis() 72} minutes zmanis before {@link #getSunrise() sunrise}. This time is 5 {@link + * being {@link #getAlos72Zmanis() 72} minutes zmanis before {@link #getSunriseWithElevation() sunrise}. This time is 5 {@link * #getShaahZmanis72MinutesZmanis() shaos zmaniyos} (temporal hours) after {@link #getAlos72Zmanis() dawn} based on the opinion of * the MGA that the day is calculated from a {@link #getAlos72Zmanis() dawn} of 72 minutes zmanis before sunrise to {@link * #getTzais72Zmanis() nightfall} of 72 minutes zmanis after sunset. This returns the time of 5 * {@link #getShaahZmanis72MinutesZmanis()} after @@ -3756,7 +3754,7 @@ public Instant getSofZmanBiurChametzMGA72MinutesZmanis() { * FIXME adjust for synchronous * This method returns the latest time for burning chametz on Erev Pesach according to the opinion * of the Magen Avraham (MGA) based on alos - * being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunrise() sunrise}. This time is 5 + * being {@link #getAlos16Point1Degrees() 16.1°} before {@link #getSunriseWithElevation() sunrise}. This time is 5 * {@link #getShaahZmanis16Point1Degrees() shaos zmaniyos} (solar hours) after {@link #getAlos16Point1Degrees() * dawn} based on the opinion of the MGA that the day is calculated from dawn to nightfall with both being 16.1° * below sunrise or sunset. This returns the time of 5 {@link #getShaahZmanis16Point1Degrees()} after @@ -3812,7 +3810,7 @@ public Instant getSofZmanBiurChametzMGA16Point1Degrees() { * computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one * where it does not set, a null will be returned. See detailed explanation on top of the page. * - * @see #getSunrise() + * @see #getSunriseWithElevation() * @see #getSeaLevelSunrise() * @see #getSunsetBaalHatanya() * @see #ZENITH_1_POINT_583 @@ -3843,7 +3841,7 @@ private Instant getSunriseBaalHatanya() { * rise, and one where it does not set, a null will be returned. See detailed explanation on top of * the {@link AstronomicalCalendar} documentation. * - * @see #getSunset() + * @see #getSunsetWithElevation() * @see #getSeaLevelSunset() * @see #getSunriseBaalHatanya() * @see #ZENITH_1_POINT_583 @@ -3885,7 +3883,7 @@ public long getShaahZmanisBaalHatanya() { /** * Returns the Baal Hatanya's alos * (dawn) calculated as the time when the sun is 16.9° below the eastern {@link #GEOMETRIC_ZENITH geometric horizon} - * before {@link #getSunrise() sunrise}. For more information the source of 16.9° see {@link #ZENITH_16_POINT_9}. + * before {@link #getSunriseWithElevation() sunrise}. For more information the source of 16.9° see {@link #ZENITH_16_POINT_9}. * * @see #ZENITH_16_POINT_9 * @return The Instant of dawn. If the calculation can't be computed such as northern and southern @@ -4068,7 +4066,7 @@ public Instant getPlagHaminchaBaalHatanya() { /** * A method that returns tzais (nightfall) when the sun is 6° below the western geometric horizon - * (90°) after {@link #getSunset() sunset}. For information on the source of this calculation see + * (90°) after {@link #getSunsetWithElevation() sunset}. For information on the source of this calculation see * {@link #ZENITH_6_DEGREES}. * * @return The Instant of nightfall. If the calculation can't be computed such as northern and southern @@ -4167,7 +4165,7 @@ public Instant getSofZmanShmaMGA72MinutesToFixedLocalChatzos() { * This method returns Rav Moshe Feinstein's opinion of the * calculation of sof zman krias shema (latest time to recite Shema in the morning) according to the * opinion of the GRA that the day is calculated from - * sunrise to sunset, but calculated using the first half of the day only. The half a day starts at {@link #getSunrise() + * sunrise to sunset, but calculated using the first half of the day only. The half a day starts at {@link #getSunriseWithElevation() * sunrise} and ends at {@link #getFixedLocalChatzos() fixed local chatzos}. Sof zman Shema is 3 shaos * zmaniyos (solar hours) after sunrise or half of this half-day. * @@ -4175,7 +4173,7 @@ public Instant getSofZmanShmaMGA72MinutesToFixedLocalChatzos() { * as northern and southern locations even south of the Arctic Circle and north of the Antarctic Circle * where the sun may not reach low enough below the horizon for this calculation, a null will be * returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation. - * @see #getSunrise() + * @see #getSunriseWithElevation() * @see #getFixedLocalChatzos() * @see #getHalfDayBasedZman(Instant, Instant, double) */ @@ -4188,14 +4186,14 @@ public Instant getSofZmanShmaGRASunriseToFixedLocalChatzos() { * calculation of sof zman tfila (zman tfilah (the latest time to recite the morning prayers)) * according to the opinion of the GRA that the day is * calculated from sunrise to sunset, but calculated using the first half of the day only. The half a day starts at - * {@link #getSunrise() sunrise} and ends at {@link #getFixedLocalChatzos() fixed local chatzos}. Sof zman tefila + * {@link #getSunriseWithElevation() sunrise} and ends at {@link #getFixedLocalChatzos() fixed local chatzos}. Sof zman tefila * is 4 shaos zmaniyos (solar hours) after sunrise or 2/3 of this half-day. * * @return the Instant of the latest zman krias shema. If the calculation can't be computed such * as northern and southern locations even south of the Arctic Circle and north of the Antarctic Circle * where the sun may not reach low enough below the horizon for this calculation, a null will be * returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation. - * @see #getSunrise() + * @see #getSunriseWithElevation() * @see #getFixedLocalChatzos() * @see #getHalfDayBasedZman(Instant, Instant, double) */ @@ -4265,7 +4263,7 @@ public Instant getPlagHaminchaGRAFixedLocalChatzosToSunset() { } /** - * Method to return tzais (dusk) calculated as 50 minutes after {@link #getSunset() sunset} or {@link + * Method to return tzais (dusk) calculated as 50 minutes after {@link #getSunsetWithElevation() sunset} or {@link * #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} setting). This method returns * tzais (nightfall) based on the opinion of Rabbi Moshe Feinstein for the New York area. This time should * not be used for latitudes other than ones similar to the latitude of the NY area. @@ -4284,7 +4282,7 @@ public Instant getTzais50() { * {@link #getMinchaKetana()} or is 9 * shaos zmaniyos (solar hours) after the start of * the day, calculated according to the GRA using a day starting at * sunrise and ending at sunset. This is the time that eating or other activity can't begin prior to praying mincha. - * The calculation used is 9 * {@link #getShaahZmanisGra()} after {@link #getSunrise() sunrise} or {@link + * The calculation used is 9 * {@link #getShaahZmanisGra()} after {@link #getSunriseWithElevation() sunrise} or {@link * #getElevationAdjustedSunrise() elevation adjusted sunrise} (depending on the {@link #isUseElevation()} setting). See the * Mechaber and Mishna Berurah 232 and 249:2. diff --git a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java index 19f06be3..1e4b0078 100644 --- a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java @@ -39,8 +39,8 @@ * "https://www.worldcat.org/oclc/919472094">Shimush Zekeinim, Ch. 1, page 17 states that obstructing horizons should * be factored into zmanim calculations. The setting defaults to false (elevation will not be used for * zmanim calculations besides sunrise and sunset), unless the setting is changed to true in {@link - * #setUseElevation(boolean)}. This will impact sunrise and sunset-based zmanim such as {@link #getSunrise()}, - * {@link #getSunset()}, {@link #getSofZmanShmaGRA()}, alos-based zmanim such as {@link #getSofZmanShmaMGA()} + * #setUseElevation(boolean)}. This will impact sunrise and sunset-based zmanim such as {@link #getSunriseWithElevation()}, + * {@link #getSunsetWithElevation()}, {@link #getSofZmanShmaGRA()}, alos-based zmanim such as {@link #getSofZmanShmaMGA()} * that are based on a fixed offset of sunrise or sunset and zmanim based on a percentage of the day such as * {@link ComprehensiveZmanimCalendar#getSofZmanShmaMGA90MinutesZmanis()} that are based on sunrise and sunset. Even when set to * true it will not impact zmanim that are a degree-based offset of sunrise and sunset, such as {@link @@ -88,8 +88,8 @@ public class ZmanimCalendar extends AstronomicalCalendar { * "https://www.worldcat.org/oclc/919472094">Shimush Zekeinim, Ch. 1, page 17 states that obstructing horizons * should be factored into zmanim calculations.The setting defaults to false (elevation will not be used for * zmanim calculations), unless the setting is changed to true in {@link #setUseElevation(boolean)}. This will - * impact sunrise and sunset based zmanim such as {@link #getSunrise()}, {@link #getSunset()}, - * {@link #getSofZmanShmaGRA()}, alos based zmanim such as {@link #getSofZmanShmaMGA()} that are based on a + * impact sunrise and sunset based zmanim such as {@link #getSofZmanShmaGRA()}, + * alos based zmanim such as {@link #getSofZmanShmaMGA()} that are based on a * fixed offset of sunrise or sunset and zmanim based on a percentage of the day such as {@link * ComprehensiveZmanimCalendar#getSofZmanShmaMGA90MinutesZmanis()} that are based on sunrise and sunset. It will not impact * zmanim that are a degree based offset of sunrise and sunset, such as @@ -220,7 +220,7 @@ public void setUseAstronomicalChatzosForOtherZmanim(boolean useAstronomicalChatz * and sunrise (and sunset to nightfall) is 72 minutes, the time that is takes to walk 4 mil at 18 minutes a mil (Rambam and others). The sun's position below the horizon 72 minutes - * before {@link #getSunrise() sunrise} in Jerusalem around the equinox / equilux is * 16.1° below {@link #GEOMETRIC_ZENITH geometric zenith}. * @@ -240,7 +240,7 @@ public void setUseAstronomicalChatzosForOtherZmanim(boolean useAstronomicalChatz /** * The zenith of 8.5° below geometric zenith (90°). This calculation is used for calculating alos * (dawn) and tzais (nightfall) in some opinions. This calculation is based on the sun's position below the - * horizon 36 minutes after {@link #getSunset() sunset} in Jerusalem around the equinox / equilux, which * is 8.5° below {@link #GEOMETRIC_ZENITH geometric zenith}. The Ohr Meir considers this the time that 3 small stars are visible, @@ -259,39 +259,39 @@ public void setUseAstronomicalChatzosForOtherZmanim(boolean useAstronomicalChatz /** * This method will return {@link #getSeaLevelSunrise() sea level sunrise} if {@link #isUseElevation()} is false (the - * default), or elevation adjusted {@link AstronomicalCalendar#getSunrise()} if it is true. This allows relevant zmanim + * default), or elevation adjusted {@link AstronomicalCalendar#getSunriseWithElevation()} if it is true. This allows relevant zmanim * in this and extending classes (such as the {@link ComprehensiveZmanimCalendar}) to automatically adjust to the elevation setting. * * @return {@link #getSeaLevelSunrise()} if {@link #isUseElevation()} is false (the default), or elevation adjusted - * {@link AstronomicalCalendar#getSunrise()} if it is true. - * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunrise() + * {@link AstronomicalCalendar#getSunriseWithElevation()} if it is true. + * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunriseWithElevation() */ protected Instant getElevationAdjustedSunrise() { if (isUseElevation()) { - return super.getSunrise(); + return super.getSunriseWithElevation(); } return getSeaLevelSunrise(); } /** * This method will return {@link #getSeaLevelSunrise() sea level sunrise} if {@link #isUseElevation()} is false (the default), - * or elevation adjusted {@link AstronomicalCalendar#getSunrise()} if it is true. This allows relevant zmanim + * or elevation adjusted {@link AstronomicalCalendar#getSunriseWithElevation()} if it is true. This allows relevant zmanim * in this and extending classes (such as the {@link ComprehensiveZmanimCalendar}) to automatically adjust to the elevation setting. * * @return {@link #getSeaLevelSunset()} if {@link #isUseElevation()} is false (the default), or elevation adjusted - * {@link AstronomicalCalendar#getSunset()} if it is true. - * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunset() + * {@link AstronomicalCalendar#getSunsetWithElevation()} if it is true. + * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunsetWithElevation() */ protected Instant getElevationAdjustedSunset() { if (isUseElevation()) { - return super.getSunset(); + return super.getSunsetWithElevation(); } return getSeaLevelSunset(); } /** * A method that returns tzais (nightfall) when the sun is {@link #ZENITH_8_POINT_5 8.5°} below the - * {@link #GEOMETRIC_ZENITH geometric horizon} (90°) after {@link #getSunset() sunset}, a time that Rabbi Meir + * {@link #GEOMETRIC_ZENITH geometric horizon} (90°) after {@link #getSunsetWithElevation() sunset}, a time that Rabbi Meir * Posen in his the Ohr Meir calculated that 3 small * stars are visible, which is later than the required 3 medium stars. See the {@link #ZENITH_8_POINT_5} constant. * @@ -310,11 +310,11 @@ public Instant getTzais() { /** * Returns alos (dawn) based on the time when the sun is {@link #ZENITH_16_POINT_1 16.1°} below the - * eastern {@link #GEOMETRIC_ZENITH geometric horizon} before {@link #getSunrise() sunrise}. This is based on the + * eastern {@link #GEOMETRIC_ZENITH geometric horizon} before {@link #getSunriseWithElevation() sunrise}. This is based on the * calculation that the time between dawn and sunrise (and sunset to nightfall) is 72 minutes, the time that is * takes to walk 4 mil at * 18 minutes a mil (Rambam and others). The sun's position - * below the horizon 72 minutes before {@link #getSunrise() sunrise} in Jerusalem on the around the equinox / equilux is * 16.1° below {@link #GEOMETRIC_ZENITH}. * @@ -331,7 +331,7 @@ public Instant getAlosHashachar() { } /** - * Method to return alos (dawn) calculated as 72 minutes before {@link #getSunrise() sunrise} or + * Method to return alos (dawn) calculated as 72 minutes before {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting). This time * is based on the time to walk the distance of 4 mil at 18 minutes a mil. The @@ -425,7 +425,7 @@ public Instant getChatzosAsHalfDay() { * hours), and the latest zman krias shema is calculated as 3 of those shaos zmaniyos after the beginning of * the day. If {@link #isUseAstronomicalChatzosForOtherZmanim()} is true, the 3 shaos zmaniyos will be * based on 1/6 of the time between sunrise and {@link #getSunTransit() astronomical chatzos}. As an example, passing - * {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea level sunrise} and {@link + * {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() sea level sunrise} and {@link * #getSeaLevelSunset() sea level sunset} to this method (or {@link #getElevationAdjustedSunrise()} and {@link * #getElevationAdjustedSunset()} that is driven off the {@link #isUseElevation()} setting) will return sof zman krias * shema according to the opinion of the GRA. In cases @@ -479,11 +479,11 @@ public Instant getSofZmanShma(Instant startOfDay, Instant endOfDay) { /** * This method returns the latest zman krias shema (time to recite shema in the morning) that is 3 * - * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunrise() sunrise} or + * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according * to the GRA. * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level - * sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the + * sunset} or from {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the * {@link #isUseElevation()} setting). * * @see #getSofZmanShma(Instant, Instant) @@ -504,7 +504,7 @@ public Instant getSofZmanShmaGRA() { * {@link #getShaahZmanisMGA() shaos zmaniyos} (solar hours) after {@link #getAlos72()}, according to the * Magen Avraham (MGA). The day is calculated * from 72 minutes before {@link #getSeaLevelSunrise() sea level sunrise} to 72 minutes after {@link - * #getSeaLevelSunset() sea level sunset} or from 72 minutes before {@link #getSunrise() sunrise} to {@link #getSunset() + * #getSeaLevelSunset() sea level sunset} or from 72 minutes before {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()} setting). * * @return the Instant of the latest zman shema. If the calculation can't be computed such as in @@ -528,7 +528,7 @@ public Instant getSofZmanShmaMGA() { * 235:3, the Pri Megadim in Orach * Chaim 261:2 (see the Biur Halacha) and others (see Hazmanim Bahalacha 17:3 and 17:5) the 72 minutes are standard * clock minutes any time of the year in any location. Depending on the {@link #isUseElevation()} setting, a 72-minute - * offset from either {@link #getSunset() sunset} or {@link #getSeaLevelSunset() sea level sunset} is used. + * offset from either {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunset() sea level sunset} is used. * * @see ComprehensiveZmanimCalendar#getTzais16Point1Degrees() * @return the Instant representing 72 minutes after sunset. If the calculation can't be @@ -565,7 +565,7 @@ public Instant getCandleLighting() { * end of the day passed to this method. * The time from the start of day to the end of day are divided into 12 shaos zmaniyos (temporal hours), * and sof zman tfila is calculated as 4 of those shaos zmaniyos after the beginning of the day. - * As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() + * As an example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() * sea level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} * elevation setting) to this method will return zman tfilah according to the opinion of the GRA. This method's synchronous parameter indicates if the start @@ -621,11 +621,11 @@ public Instant getSofZmanTfila(Instant startOfDay, Instant endOfDay) { /** * This method returns the latest zman tfila (time to recite shema in the morning) that is 4 * - * {@link #getShaahZmanisGra() shaos zmaniyos }(solar hours) after {@link #getSunrise() sunrise} or + * {@link #getShaahZmanisGra() shaos zmaniyos }(solar hours) after {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according * to the GRA. * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level - * sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the + * sunset} or from {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the * {@link #isUseElevation()} setting). * * @see #getSofZmanTfila(Instant, Instant) @@ -645,7 +645,7 @@ public Instant getSofZmanTfilaGRA() { * {@link #getShaahZmanisMGA() shaos zmaniyos} (solar hours) after {@link #getAlos72()}, according to the * Magen Avraham (MGA). The day is calculated * from 72 minutes before {@link #getSeaLevelSunrise() sea level sunrise} to 72 minutes after {@link - * #getSeaLevelSunset() sea level sunset} or from 72 minutes before {@link #getSunrise() sunrise} to {@link #getSunset() + * #getSeaLevelSunset() sea level sunset} or from 72 minutes before {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()} setting). * * @return the Instant of the latest zman tfila. If the calculation can't be computed such as in @@ -665,7 +665,7 @@ public Instant getSofZmanTfilaMGA() { * is 6.5 * shaos zmaniyos (temporal hours) after the start of the day, calculated using the start and end of the * day passed to this method. The time from the start of day to the end of day are divided into 12 shaos zmaniyos * (temporal hours), and mincha gedola is calculated as 6.5 of those shaos zmaniyos after the beginning - * of the day. As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link + * of the day. As an example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link * #getSeaLevelSunrise() sea level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link * #isUseElevation()} elevation setting) to this method will return mincha gedola according to the opinion of the * GRA. Alternatively, this method uses {@link @@ -731,14 +731,14 @@ public Instant getMinchaGedola(Instant startOfDay, Instant endOfDay) { /** * This method returns the latest mincha gedola,the earliest time one can pray mincha that is 6.5 * - * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunrise() sunrise} or + * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according * to the GRA. Mincha gedola is the earliest * time one can pray mincha. The Ramba"m is of the opinion that it is better to delay mincha until * {@link #getMinchaKetana() mincha ketana} while the Ra"sh, Tur, GRA and others are of the * opinion that mincha can be prayed lechatchila starting at mincha gedola. * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level - * sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()} + * sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()} * setting). * @todo Consider adjusting this to calculate the time as half an hour zmaniyos after either {@link * #getSunTransit() astronomical chatzos} or {@link #getChatzosAsHalfDay() chatzos as half a day} @@ -763,7 +763,7 @@ public Instant getMinchaGedola() { * start of the day, calculated using the start and end of the day passed to this method. * The time from the start of day to the end of day are divided into 12 shaos zmaniyos (temporal hours), and * samuch lemincha ketana is calculated as 9 of those shaos zmaniyos after the beginning of the day. - * For example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea + * For example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() sea * level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation * setting) to this method will return samuch lemincha ketana according to the opinion of the * GRA. See the shaos zmaniyos (temporal hours), and * mincha ketana is calculated as 9.5 of those shaos zmaniyos after the beginning of the day. As an - * example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea + * example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() sea * level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} * elevation setting) to this method will return mincha ketana according to the opinion of the * GRA. This method's synchronous parameter indicates if the start @@ -881,12 +881,12 @@ public Instant getMinchaKetana(Instant startOfDay, Instant endOfDay) { /** * This method returns mincha ketana,the preferred earliest time to pray mincha in the * opinion of the Rambam and others, that is 9.5 - * * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunrise() sunrise} or + * * {@link #getShaahZmanisGra() shaos zmaniyos} (solar hours) after {@link #getSunriseWithElevation() sunrise} or * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according * to the GRA. For more information on this see the * documentation on {@link #getMinchaGedola() mincha gedola}. * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level - * sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()} + * sunset} or from {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()} * setting. * * @see #getMinchaKetana(Instant, Instant) @@ -908,7 +908,7 @@ public Instant getMinchaKetana() { * the day passed to the method. * The time from the start of day to the end of day are divided into 12 shaos zmaniyos (temporal hours), and * plag hamincha is calculated as 10.75 of those shaos zmaniyos after the beginning of the day. As an - * example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea level + * example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() sea level * sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation * setting) to this method will return plag mincha according to the opinion of the * GRA. This method's synchronous parameter indicates if the start @@ -962,11 +962,11 @@ public Instant getPlagHamincha(Instant startOfDay, Instant endOfDay) { /** * This method returns plag hamincha, that is 10.75 * {@link #getShaahZmanisGra() shaos zmaniyos} - * (solar hours) after {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on + * (solar hours) after {@link #getSunriseWithElevation() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on * the {@link #isUseElevation()} setting), according to the GRA. Plag hamincha is the earliest time that Shabbos can be started. * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level - * sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()} + * sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()} * * @see #getPlagHamincha(Instant, Instant, boolean) * @see #getPlagHamincha(Instant, Instant) @@ -984,7 +984,7 @@ public Instant getPlagHamincha() { * A method that returns a shaah zmanis ({@link #getTemporalHour(Instant, Instant) temporal hour}) according to * the opinion of the GRA. This calculation divides the day * based on the opinion of the GRA that the day runs from from {@link #getSeaLevelSunrise() sea level - * sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() + * sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()} setting). The day is split into 12 equal parts with each one * being a shaah zmanis. This method is similar to {@link #getTemporalHour()}, but can account for elevation. * @@ -1005,9 +1005,9 @@ public long getShaahZmanisGra() { * A method that returns a shaah zmanis (temporal hour) according to the opinion of the Magen Avraham (MGA) based on a 72-minute alos * and tzais. This calculation divides the day that runs from dawn to dusk (for sof zman krias shema and - * tfila). Dawn for this calculation is 72 minutes before {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() + * tfila). Dawn for this calculation is 72 minutes before {@link #getSunriseWithElevation() sunrise} or {@link #getSeaLevelSunrise() * sea level sunrise} (depending on the {@link #isUseElevation()} elevation setting) and dusk is 72 minutes after {@link - * #getSunset() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation + * #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation * setting). This day is split into 12 equal parts with each part being a shaah zmanis. Alternate methods of calculating * a shaah zmanis according to the Magen Avraham (MGA) are available in the subclass {@link ComprehensiveZmanimCalendar}. * @@ -1103,7 +1103,7 @@ public boolean isAssurBemlacha(Instant currentTime, Instant tzais, boolean inIsr * A generic utility method for calculating any shaah zmanis (temporal hour) based zman with the * day defined as the start and end of day (or night) and the number of shaos zmaniyos passed to the * method. This simplifies the code in other methods such as {@link #getPlagHamincha(Instant, Instant)} and cuts down on - * code replication. As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link + * code replication. As an example, passing {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link * #getSeaLevelSunrise() sea level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the * {@link #isUseElevation()} elevation setting) and 10.75 hours to this method will return plag mincha * according to the opinion of the GRA. diff --git a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/RegressionTestFileWriter.java b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/RegressionTestFileWriter.java index d498431d..485df83b 100644 --- a/src/test/java/com/kosherjava/zmanim/hebrewcalendar/RegressionTestFileWriter.java +++ b/src/test/java/com/kosherjava/zmanim/hebrewcalendar/RegressionTestFileWriter.java @@ -1,7 +1,6 @@ package com.kosherjava.zmanim.hebrewcalendar; import com.kosherjava.zmanim.ComprehensiveZmanimCalendar; -import com.kosherjava.zmanim.util.AstronomicalCalculator; import com.kosherjava.zmanim.util.GeoLocation; import java.io.*; @@ -54,7 +53,7 @@ public static void main(String[] args) throws IOException { zcal.getAlos18Degrees(), zcal.getAlos19Degrees(), zcal.getAlos19Point8Degrees(), zcal.getAlos16Point1Degrees(), zcal.getMisheyakir11Point5Degrees(), zcal.getMisheyakir11Degrees(), zcal.getMisheyakir10Point2Degrees(), zcal.getMisheyakir7Point65Degrees(), - zcal.getMisheyakir9Point5Degrees(), zcal.getSunrise(), zcal.getSeaLevelSunrise(), + zcal.getMisheyakir9Point5Degrees(), zcal.getSunriseWithElevation(), zcal.getSeaLevelSunrise(), zcal.getSofZmanShmaMGA16Point1Degrees(), zcal.getSofZmanShmaMGA72Minutes(), zcal.getSofZmanShmaMGA72MinutesZmanis(), zcal.getSofZmanShmaMGA90Minutes(), zcal.getSofZmanShmaMGA90MinutesZmanis(), zcal.getSofZmanShmaMGA96Minutes(), From baaaba53b232ac334ed4b6ff358136b172d6eb67 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 16 Mar 2026 16:07:50 -0400 Subject: [PATCH 4/9] Rename getElevationAdjustedSunset/rise to avoid further confusion. --- .../zmanim/ComprehensiveZmanimCalendar.java | 60 +++++++++---------- .../com/kosherjava/zmanim/ZmanimCalendar.java | 28 ++++----- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java index 17648b19..fa2bbb43 100644 --- a/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ComprehensiveZmanimCalendar.java @@ -850,7 +850,7 @@ public Instant getPlagHamincha120Minutes() { * @see #getShaahZmanis60Minutes() */ public Instant getAlos60() { - return getTimeOffset(getElevationAdjustedSunrise(), -60 * MINUTE_MILLIS); + return getTimeOffset(getSunriseBasedOnElevationSetting(), -60 * MINUTE_MILLIS); } /** @@ -860,7 +860,7 @@ public Instant getAlos60() { * 72 minutes which is 1/10th of a day (12 * 60 = 720) based on the day being from {@link #getSeaLevelSunrise() sea * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() * sunset} (depending on the {@link #isUseElevation()} setting). The actual calculation is {@link - * #getElevationAdjustedSunrise()} - ({@link #getShaahZmanisGra()} * 1.2). This calculation is used in the calendars + * #getSunriseBasedOnElevationSetting()} - ({@link #getShaahZmanisGra()} * 1.2). This calculation is used in the calendars * published by the Hisachdus Harabanim D'Artzos Habris * Ve'Canada. * @@ -890,7 +890,7 @@ public Instant getAlos72Zmanis() { * documentation. */ public Instant getAlos96() { - return getTimeOffset(getElevationAdjustedSunrise(), -96 * MINUTE_MILLIS); + return getTimeOffset(getSunriseBasedOnElevationSetting(), -96 * MINUTE_MILLIS); } /** @@ -901,7 +901,7 @@ public Instant getAlos96() { * mil is 90 minutes which is 1/8th of a day (12 * 60) / 8 = 90. The day is calculated from {@link * #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() * sunrise} to {@link #getSunsetWithElevation() sunset} (depending on the {@link #isUseElevation()}. The actual calculation used - * is {@link #getElevationAdjustedSunrise()} - ({@link #getShaahZmanisGra()} * 1.5). + * is {@link #getSunriseBasedOnElevationSetting()} - ({@link #getShaahZmanisGra()} * 1.5). * * @return the Instant representing the time. If the calculation can't be computed such as in the Arctic * Circle where there is at least one day a year where the sun does not rise, and one where it does not set, @@ -920,7 +920,7 @@ public Instant getAlos90Zmanis() { * "https://en.wikipedia.org/wiki/Biblical_and_Talmudic_units_of_measurement">mil so the time for 4 mil is 96 * minutes which is 1/7.5th of a day (12 * 60 / 7.5 = 96). The day is calculated from {@link #getSeaLevelSunrise() sea * level sunrise} to {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunriseWithElevation() sunrise} to {@link #getSunsetWithElevation() - * sunset} (depending on the {@link #isUseElevation()}. The actual calculation used is {@link #getElevationAdjustedSunrise()} + * sunset} (depending on the {@link #isUseElevation()}. The actual calculation used is {@link #getSunriseBasedOnElevationSetting()} * - ({@link #getShaahZmanisGra()} * 1.6). * * @return the Instant representing the time. If the calculation can't be computed such as in the Arctic @@ -949,7 +949,7 @@ public Instant getAlos96Zmanis() { * documentation. */ public Instant getAlos90() { - return getTimeOffset(getElevationAdjustedSunrise(), -90 * MINUTE_MILLIS); + return getTimeOffset(getSunriseBasedOnElevationSetting(), -90 * MINUTE_MILLIS); } /** @@ -979,7 +979,7 @@ public Instant getAlos90() { */ @Deprecated (forRemoval=false) public Instant getAlos120() { - return getTimeOffset(getElevationAdjustedSunrise(), -120 * MINUTE_MILLIS); + return getTimeOffset(getSunriseBasedOnElevationSetting(), -120 * MINUTE_MILLIS); } /** @@ -1538,7 +1538,7 @@ public Instant getSofZmanShmaMGA120Minutes() { * @see #getSeaLevelSunset() */ public Instant getSofZmanShmaAlos16Point1ToSunset() { - return getSofZmanShma(getAlos16Point1Degrees(), getElevationAdjustedSunset(), false); + return getSofZmanShma(getAlos16Point1Degrees(), getSunsetBasedOnElevationSetting(), false); } /** @@ -2271,7 +2271,7 @@ public Instant getPlagHamincha18Degrees() { */ @Deprecated (forRemoval=false) public Instant getPlagAlosToSunset() { - return getPlagHamincha(getAlos16Point1Degrees(), getElevationAdjustedSunset(), false); + return getPlagHamincha(getAlos16Point1Degrees(), getSunsetBasedOnElevationSetting(), false); } /** @@ -2358,7 +2358,7 @@ public Instant getBainHashmashosRT13Point24Degrees() { * */ public Instant getBainHashmashosRT58Point5Minutes() { - return getTimeOffset(getElevationAdjustedSunset(), 58.5 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 58.5 * MINUTE_MILLIS); } /** @@ -2391,11 +2391,11 @@ public Instant getBainHashmashosRT13Point5MinutesBefore7Point083Degrees() { */ public Instant getBainHashmashosRT2Stars() { Instant alos19Point8 = getAlos19Point8Degrees(); - Instant sunrise = getElevationAdjustedSunrise(); + Instant sunrise = getSunriseBasedOnElevationSetting(); if (alos19Point8 == null || sunrise == null) { return null; } - return getTimeOffset(getElevationAdjustedSunset(), (sunrise.toEpochMilli() - alos19Point8.toEpochMilli()) * (5 / 18d)); + return getTimeOffset(getSunsetBasedOnElevationSetting(), (sunrise.toEpochMilli() - alos19Point8.toEpochMilli()) * (5 / 18d)); } /** @@ -2412,7 +2412,7 @@ public Instant getBainHashmashosRT2Stars() { * @see #getBainHashmashosYereim3Point05Degrees() */ public Instant getBainHashmashosYereim18Minutes() { - return getTimeOffset(getElevationAdjustedSunset(), -18 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), -18 * MINUTE_MILLIS); } /** @@ -2465,7 +2465,7 @@ public Instant getBainHashmashosYereim3Point05Degrees() { * @see #getBainHashmashosYereim2Point8Degrees() */ public Instant getBainHashmashosYereim16Point875Minutes() { - return getTimeOffset(getElevationAdjustedSunset(), -16.875 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), -16.875 * MINUTE_MILLIS); } /** @@ -2508,7 +2508,7 @@ public Instant getBainHashmashosYereim2Point8Degrees() { * @see #getBainHashmashosYereim2Point1Degrees() */ public Instant getBainHashmashosYereim13Point5Minutes() { - return getTimeOffset(getElevationAdjustedSunset(), -13.5 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), -13.5 * MINUTE_MILLIS); } /** @@ -2795,7 +2795,7 @@ public Instant getTzaisGeonim9Point75Degrees() { * @see #getShaahZmanis60Minutes() */ public Instant getTzais60() { - return getTimeOffset(getElevationAdjustedSunset(), 60 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 60 * MINUTE_MILLIS); } /** @@ -2815,7 +2815,7 @@ public Instant getTzais60() { * @see #setAteretTorahSunsetOffset(double) */ public Instant getTzaisAteretTorah() { - return getTimeOffset(getElevationAdjustedSunset(), getAteretTorahSunsetOffset() * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), getAteretTorahSunsetOffset() * MINUTE_MILLIS); } /** @@ -3009,9 +3009,9 @@ private Instant getZmanisBasedOffset(double hours) { } if (hours > 0) { - return getTimeOffset(getElevationAdjustedSunset(), (long) (shaahZmanis * hours)); + return getTimeOffset(getSunsetBasedOnElevationSetting(), (long) (shaahZmanis * hours)); } else { - return getTimeOffset(getElevationAdjustedSunrise(), (long) (shaahZmanis * hours)); + return getTimeOffset(getSunriseBasedOnElevationSetting(), (long) (shaahZmanis * hours)); } } @@ -3061,7 +3061,7 @@ public Instant getTzais96Zmanis() { * @see #getAlos90() */ public Instant getTzais90() { - return getTimeOffset(getElevationAdjustedSunset(), 90 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 90 * MINUTE_MILLIS); } /** @@ -3088,7 +3088,7 @@ public Instant getTzais90() { */ @Deprecated (forRemoval=false) public Instant getTzais120() { - return getTimeOffset(getElevationAdjustedSunset(), 120 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 120 * MINUTE_MILLIS); } /** @@ -3201,7 +3201,7 @@ public Instant getTzais19Point8Degrees() { * @see #getAlos96() */ public Instant getTzais96() { - return getTimeOffset(getElevationAdjustedSunset(), 96 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 96 * MINUTE_MILLIS); } /** @@ -3688,7 +3688,7 @@ public Instant getSofZmanBiurChametzGRA() { jewishCalendar.setGregorianDate(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth()); //FIXME - the minus one needs adjustment when the JewishCalendar is changed to use ZonedDateTime if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) { - return getTimeOffset(getElevationAdjustedSunrise(), getShaahZmanisGra() * 5); + return getTimeOffset(getSunriseBasedOnElevationSetting(), getShaahZmanisGra() * 5); } else { return null; } @@ -4178,7 +4178,7 @@ public Instant getSofZmanShmaMGA72MinutesToFixedLocalChatzos() { * @see #getHalfDayBasedZman(Instant, Instant, double) */ public Instant getSofZmanShmaGRASunriseToFixedLocalChatzos() { - return getHalfDayBasedZman(getElevationAdjustedSunrise(), getFixedLocalChatzos(), 3); + return getHalfDayBasedZman(getSunriseBasedOnElevationSetting(), getFixedLocalChatzos(), 3); } /** @@ -4198,7 +4198,7 @@ public Instant getSofZmanShmaGRASunriseToFixedLocalChatzos() { * @see #getHalfDayBasedZman(Instant, Instant, double) */ public Instant getSofZmanTfilaGRASunriseToFixedLocalChatzos() { - return getHalfDayBasedZman(getElevationAdjustedSunrise(), getFixedLocalChatzos(), 4); + return getHalfDayBasedZman(getSunriseBasedOnElevationSetting(), getFixedLocalChatzos(), 4); } /** @@ -4238,7 +4238,7 @@ public Instant getMinchaGedolaGRAFixedLocalChatzos30Minutes() { * @see ZmanimCalendar#getHalfDayBasedZman(Instant, Instant, double) */ public Instant getMinchaKetanaGRAFixedLocalChatzosToSunset() { - return getHalfDayBasedZman(getFixedLocalChatzos(), getElevationAdjustedSunset(), 3.5); + return getHalfDayBasedZman(getFixedLocalChatzos(), getSunsetBasedOnElevationSetting(), 3.5); } /** @@ -4259,7 +4259,7 @@ public Instant getMinchaKetanaGRAFixedLocalChatzosToSunset() { * @see ZmanimCalendar#getHalfDayBasedZman(Instant, Instant, double) */ public Instant getPlagHaminchaGRAFixedLocalChatzosToSunset() { - return getHalfDayBasedZman(getFixedLocalChatzos(), getElevationAdjustedSunset(), 4.75); + return getHalfDayBasedZman(getFixedLocalChatzos(), getSunsetBasedOnElevationSetting(), 4.75); } /** @@ -4274,7 +4274,7 @@ public Instant getPlagHaminchaGRAFixedLocalChatzosToSunset() { * documentation. */ public Instant getTzais50() { - return getTimeOffset(getElevationAdjustedSunset(), 50 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 50 * MINUTE_MILLIS); } /** @@ -4283,7 +4283,7 @@ public Instant getTzais50() { * the day, calculated according to the GRA using a day starting at * sunrise and ending at sunset. This is the time that eating or other activity can't begin prior to praying mincha. * The calculation used is 9 * {@link #getShaahZmanisGra()} after {@link #getSunriseWithElevation() sunrise} or {@link - * #getElevationAdjustedSunrise() elevation adjusted sunrise} (depending on the {@link #isUseElevation()} setting). See the + * #getSunriseBasedOnElevationSetting() elevation adjusted sunrise} (depending on the {@link #isUseElevation()} setting). See the * Mechaber and Mishna Berurah 232 and 249:2. * @@ -4296,7 +4296,7 @@ public Instant getTzais50() { * returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation. */ public Instant getSamuchLeMinchaKetanaGRA() { - return getSamuchLeMinchaKetana(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getSamuchLeMinchaKetana(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** diff --git a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java index 1e4b0078..8cfd7274 100644 --- a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java @@ -266,7 +266,7 @@ public void setUseAstronomicalChatzosForOtherZmanim(boolean useAstronomicalChatz * {@link AstronomicalCalendar#getSunriseWithElevation()} if it is true. * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunriseWithElevation() */ - protected Instant getElevationAdjustedSunrise() { + protected Instant getSunriseBasedOnElevationSetting() { if (isUseElevation()) { return super.getSunriseWithElevation(); } @@ -282,7 +282,7 @@ protected Instant getElevationAdjustedSunrise() { * {@link AstronomicalCalendar#getSunsetWithElevation()} if it is true. * @see com.kosherjava.zmanim.AstronomicalCalendar#getSunsetWithElevation() */ - protected Instant getElevationAdjustedSunset() { + protected Instant getSunsetBasedOnElevationSetting() { if (isUseElevation()) { return super.getSunsetWithElevation(); } @@ -345,7 +345,7 @@ public Instant getAlosHashachar() { * documentation. */ public Instant getAlos72() { - return getTimeOffset(getElevationAdjustedSunrise(), -72 * MINUTE_MILLIS); + return getTimeOffset(getSunriseBasedOnElevationSetting(), -72 * MINUTE_MILLIS); } /** @@ -426,8 +426,8 @@ public Instant getChatzosAsHalfDay() { * the day. If {@link #isUseAstronomicalChatzosForOtherZmanim()} is true, the 3 shaos zmaniyos will be * based on 1/6 of the time between sunrise and {@link #getSunTransit() astronomical chatzos}. As an example, passing * {@link #getSunriseWithElevation() sunrise} and {@link #getSunsetWithElevation() sunset} or {@link #getSeaLevelSunrise() sea level sunrise} and {@link - * #getSeaLevelSunset() sea level sunset} to this method (or {@link #getElevationAdjustedSunrise()} and {@link - * #getElevationAdjustedSunset()} that is driven off the {@link #isUseElevation()} setting) will return sof zman krias + * #getSeaLevelSunset() sea level sunset} to this method (or {@link #getSunriseBasedOnElevationSetting()} and {@link + * #getSunsetBasedOnElevationSetting()} that is driven off the {@link #isUseElevation()} setting) will return sof zman krias * shema according to the opinion of the GRA. In cases * where the start and end dates are not synchronous such as in {@link ComprehensiveZmanimCalendar * #getSofZmanShmaAlos16Point1ToTzaisGeonim7Point083Degrees()} false should be passed to the synchronous parameter @@ -496,7 +496,7 @@ public Instant getSofZmanShma(Instant startOfDay, Instant endOfDay) { * of the {@link AstronomicalCalendar} documentation. */ public Instant getSofZmanShmaGRA() { - return getSofZmanShma(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getSofZmanShma(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** @@ -537,7 +537,7 @@ public Instant getSofZmanShmaMGA() { * {@link AstronomicalCalendar} documentation. */ public Instant getTzais72() { - return getTimeOffset(getElevationAdjustedSunset(), 72 * MINUTE_MILLIS); + return getTimeOffset(getSunsetBasedOnElevationSetting(), 72 * MINUTE_MILLIS); } /** @@ -637,7 +637,7 @@ public Instant getSofZmanTfila(Instant startOfDay, Instant endOfDay) { * {@link AstronomicalCalendar} documentation. */ public Instant getSofZmanTfilaGRA() { - return getSofZmanTfila(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getSofZmanTfila(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** @@ -754,7 +754,7 @@ public Instant getMinchaGedola(Instant startOfDay, Instant endOfDay) { * {@link AstronomicalCalendar} documentation. */ public Instant getMinchaGedola() { - return getMinchaGedola(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getMinchaGedola(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** @@ -899,7 +899,7 @@ public Instant getMinchaKetana(Instant startOfDay, Instant endOfDay) { * {@link AstronomicalCalendar} documentation. */ public Instant getMinchaKetana() { - return getMinchaKetana(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getMinchaKetana(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** @@ -977,7 +977,7 @@ public Instant getPlagHamincha(Instant startOfDay, Instant endOfDay) { * {@link AstronomicalCalendar} documentation. */ public Instant getPlagHamincha() { - return getPlagHamincha(getElevationAdjustedSunrise(), getElevationAdjustedSunset(), true); + return getPlagHamincha(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true); } /** @@ -998,7 +998,7 @@ public Instant getPlagHamincha() { * @see ComprehensiveZmanimCalendar#getShaahZmanisBaalHatanya() */ public long getShaahZmanisGra() { - return getTemporalHour(getElevationAdjustedSunrise(), getElevationAdjustedSunset()); + return getTemporalHour(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting()); } /** @@ -1071,7 +1071,7 @@ public void setCandleLightingOffset(double candleLightingOffset) { /** * This is a utility method to determine if the current Instant passed in has a melacha (work) prohibition. * Since there are many opinions on the time of tzais, the tzais for the current day has to be passed to this - * class. Sunset is the classes current day's {@link #getElevationAdjustedSunset() elevation adjusted sunset} that observes the + * class. Sunset is the classes current day's {@link #getSunsetBasedOnElevationSetting() elevation adjusted sunset} that observes the * {@link #isUseElevation()} settings. The {@link JewishCalendar#getInIsrael()} will be set by the inIsrael parameter. * * @param currentTime the current time @@ -1091,7 +1091,7 @@ public boolean isAssurBemlacha(Instant currentTime, Instant tzais, boolean inIsr jewishCalendar.setInIsrael(inIsrael); - if (jewishCalendar.hasCandleLighting() && currentTime.compareTo(getElevationAdjustedSunset()) >= 0) { //erev shabbos, YT or YT sheni and after shkiah + if (jewishCalendar.hasCandleLighting() && currentTime.compareTo(getSunsetBasedOnElevationSetting()) >= 0) { //erev shabbos, YT or YT sheni and after shkiah return true; } From 50b165837d0c4cc176705424b8ecd51b3ff31227 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 17 Mar 2026 12:20:58 -0400 Subject: [PATCH 5/9] Fix incorrect adjustment for getFixedLocalChatzos --- src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index 618f7336..c58de3e5 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -627,7 +627,7 @@ protected enum SolarEvent { * @param time * The time to be set as the time for the Instant. The time expected is in the format: 18.75 * for 6:45:00 PM.time is sunrise and false if it is sunset - * @param solarEvent the type of {@link SolarEvent} + * @param solarEvent the type of {@link SolarEvent}. * @return The Instant object representation of the time double */ @@ -759,7 +759,7 @@ public Instant getLocalMeanTime(double hours) { double rawOffset = getGeoLocation().getZoneId().getRules().getOffset(getMidnightLastNight().toInstant()).getTotalSeconds() * 1000; double utcTime = hours - rawOffset / (double) HOUR_MILLIS; - Instant instant = getInstantFromTime(utcTime, SolarEvent.SUNRISE); + Instant instant = getInstantFromTime(utcTime, null); return getTimeOffset(instant, -getGeoLocation().getLocalMeanTimeOffset(getMidnightLastNight().toInstant())); } From 19257d6a4b0c29d61d28c18b3b97c555e4ed5f14 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 18 Mar 2026 12:58:03 -0400 Subject: [PATCH 6/9] Update date --- .../zmanim/hebrewcalendar/JewishDate.java | 2668 +++++++---------- 1 file changed, 1130 insertions(+), 1538 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java index d56ee85d..1ede5e88 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java @@ -17,1562 +17,1154 @@ package com.kosherjava.zmanim.hebrewcalendar; import java.time.LocalDate; -import java.time.ZoneId; +import java.time.YearMonth; import java.time.ZonedDateTime; -import java.time.Instant; -import java.util.Calendar; -import java.util.GregorianCalendar; /** - * The JewishDate is the base calendar class, that supports maintenance of a {@link java.util.GregorianCalendar} - * instance along with the corresponding Jewish date. This class can use the standard Java Date and Calendar - * classes for setting and maintaining the dates, but it does not subclass these classes or use them internally - * in any calculations. This class also does not have a concept of a time (which the Date class does). Please - * note that the calendar does not currently support dates prior to 1/1/1 Gregorian. Also keep in mind that the - * Gregorian calendar started on October 15, 1582, so any calculations prior to that are suspect (at least from - * a Gregorian perspective). While 1/1/1 Gregorian and forward are technically supported, any calculations prior to Hillel II's (Hakatan's) calendar (4119 in the Jewish Calendar / 359 - * CE Julian as recorded by Rav Hai Gaon) would be just an - * approximation. - * + * The JewishDate is the base calendar class that maintains a Jewish date together with an absolute date and day of + * week. Gregorian dates are derived from the absolute date as needed, and exposed via the Java time APIs. + * This class does not have a concept of a time of day. Please note that the calendar does not currently support dates + * prior to 1/1/1 Gregorian. Also keep in mind that the Gregorian calendar started on October 15, 1582, so any + * calculations prior to that are suspect (at least from a Gregorian perspective). While 1/1/1 Gregorian and forward + * are technically supported, any calculations prior to Hillel II's + * (Hakatan's) calendar (4119 in the Jewish Calendar / 359 CE Julian as recorded by + * Rav Hai Gaon) would be just an approximation. + * * This open source Java code was written by Avrom Finkelstien from his C++ * code. It was refactored to fit the KosherJava Zmanim API with simplification of the code, enhancements and some bug * fixing. - * + * * Some of Avrom's original C++ code was translated from * C/C++ code in * Calendrical Calculations by Nachum Dershowitz and Edward M. * Reingold, Software-- Practice & Experience, vol. 20, no. 9 (September, 1990), pp. 899- 928. Any method with the mark * "ND+ER" indicates that the method was taken from this source with minor modifications. - * + * * If you are looking for a class that implements a Jewish calendar version of the Calendar class, one is available from * the ICU (International Components for Unicode) project, formerly part of * IBM's DeveloperWorks. - * + * * @see JewishCalendar * @see HebrewDateFormatter - * @see java.util.Date - * @see java.util.Calendar + * @see java.time.LocalDate + * @see java.time.ZonedDateTime * @author © Avrom Finkelstien 2002 * @author © Eliyahu Hershfeld 2011 - 2026 */ public class JewishDate implements Comparable, Cloneable { - /** - * Value of the month field indicating Nissan, the first numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 7th (or 8th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int NISSAN = 1; - - /** - * Value of the month field indicating Iyar, the second numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 8th (or 9th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int IYAR = 2; - - /** - * Value of the month field indicating Sivan, the third numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 9th (or 10th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int SIVAN = 3; - - /** - * Value of the month field indicating Tammuz, the fourth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 10th (or 11th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int TAMMUZ = 4; - - /** - * Value of the month field indicating Av, the fifth numeric month of the year in the Jewish calendar. With the year - * starting at {@link #TISHREI}, it would actually be the 11th (or 12th in a {@link #isJewishLeapYear() leap year}) - * month of the year. - */ - public static final int AV = 5; - - /** - * Value of the month field indicating Elul, the sixth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 12th (or 13th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int ELUL = 6; - - /** - * Value of the month field indicating Tishrei, the seventh numeric month of the year in the Jewish calendar. With - * the year starting at this month, it would actually be the 1st month of the year. - */ - public static final int TISHREI = 7; - - /** - * Value of the month field indicating Cheshvan/marcheshvan, the eighth numeric month of the year in the Jewish - * calendar. With the year starting at {@link #TISHREI}, it would actually be the 2nd month of the year. - */ - public static final int CHESHVAN = 8; - - /** - * Value of the month field indicating Kislev, the ninth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 3rd month of the year. - */ - public static final int KISLEV = 9; - - /** - * Value of the month field indicating Teves, the tenth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 4th month of the year. - */ - public static final int TEVES = 10; - - /** - * Value of the month field indicating Shevat, the eleventh numeric month of the year in the Jewish calendar. With - * the year starting at {@link #TISHREI}, it would actually be the 5th month of the year. - */ - public static final int SHEVAT = 11; - - /** - * Value of the month field indicating Adar (or Adar I in a {@link #isJewishLeapYear() leap year}), the twelfth - * numeric month of the year in the Jewish calendar. With the year starting at {@link #TISHREI}, it would actually - * be the 6th month of the year. - */ - public static final int ADAR = 12; - - /** - * Value of the month field indicating Adar II, the leap (intercalary or embolismic) thirteenth (Undecimber) numeric - * month of the year added in Jewish {@link #isJewishLeapYear() leap year}). The leap years are years 3, 6, 8, 11, - * 14, 17 and 19 of a 19-year cycle. With the year starting at {@link #TISHREI}, it would actually be the 7th month - * of the year. - */ - public static final int ADAR_II = 13; - - /** - * the Jewish epoch using the RD (Rata Die/Fixed Date or Reingold Dershowitz) day used in Calendrical Calculations. - * Day 1 is January 1, 0001 of the Gregorian calendar - */ - private static final int JEWISH_EPOCH = -1373429; - - /** The number of chalakim (18) in a minute.*/ - private static final int CHALAKIM_PER_MINUTE = 18; - /** The number of chalakim (1080) in an hour.*/ - private static final int CHALAKIM_PER_HOUR = 1080; - /** The number of chalakim (25,920) in a 24-hour day .*/ - private static final int CHALAKIM_PER_DAY = 25920; // 24 * 1080 - /** The number of chalakim in an average Jewish month. A month has 29 days, 12 hours and 793 - * chalakim (44 minutes and 3.3 seconds) for a total of 765,433 chalakim*/ - private static final long CHALAKIM_PER_MONTH = 765433; // (29 * 24 + 12) * 1080 + 793 - /** - * Days from the beginning of Sunday till molad BaHaRaD. Calculated as 1 day, 5 hours and 204 chalakim = - * (24 + 5) * 1080 + 204 = 31524 - */ - private static final int CHALAKIM_MOLAD_TOHU = 31524; - - /** - * A short year where both {@link #CHESHVAN} and {@link #KISLEV} are 29 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int CHASERIM = 0; - - /** - * An ordered year where {@link #CHESHVAN} is 29 days and {@link #KISLEV} is 30 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int KESIDRAN = 1; - - /** - * A long year where both {@link #CHESHVAN} and {@link #KISLEV} are 30 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int SHELAIMIM = 2; - - /** the internal Jewish month.*/ - private int jewishMonth; - /** the internal Jewish day.*/ - private int jewishDay; - /** the internal Jewish year.*/ - private int jewishYear; - /** the internal count of molad hours.*/ - private int moladHours; - /** the internal count of molad minutes.*/ - private int moladMinutes; - /** the internal count of molad chalakim.*/ - private int moladChalakim; - - /** - * Returns the molad hours. Only a JewishDate object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or {@link #setMoladHours(int)} will have this field - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad hours - * @see #setMoladHours(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladHours() { - return moladHours; - } - - /** - * Sets the molad hours. - * - * @param moladHours - * the molad hours to set - * @see #getMoladHours() - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladHours(int moladHours) { - this.moladHours = moladHours; - } - - /** - * Returns the molad minutes. Only an object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladMinutes(int)} will have these fields - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad minutes - * @see #setMoladMinutes(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladMinutes() { - return moladMinutes; - } - - /** - * Sets the molad minutes. The expectation is that the traditional minute-less chalakim will be broken out to - * minutes and {@link #setMoladChalakim(int) chalakim / parts} , so 793 (TaShTZaG) parts would have the minutes set to - * 44 and chalakim to 1. - * - * @param moladMinutes - * the molad minutes to set - * @see #getMoladMinutes() - * @see #setMoladChalakim(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladMinutes(int moladMinutes) { - this.moladMinutes = moladMinutes; - } - - /** - * Sets the molad chalakim/parts. The expectation is that the traditional minute-less chalakim will be broken - * out to {@link #setMoladMinutes(int) minutes} and chalakim, so 793 (TaShTZaG) parts would have the minutes set to 44 and - * chalakim to 1. - * - * @param moladChalakim - * the molad chalakim / parts to set - * @see #getMoladChalakim() - * @see #setMoladMinutes(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladChalakim(int moladChalakim) { - this.moladChalakim = moladChalakim; - } - - /** - * Returns the molad chalakim / parts. Only an object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladChalakim(int)} will have these fields - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad chalakim / parts - * @see #setMoladChalakim(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladChalakim() { - return moladChalakim; - } - - /** - * Returns the last day in a gregorian month - * - * @param month - * the Gregorian month - * @return the last day of the Gregorian month - */ - int getLastDayOfGregorianMonth(int month) { - return getLastDayOfGregorianMonth(month, gregorianYear); - } - - /** - * Returns is the year passed in is a Gregorian leap year. - * @param year the Gregorian year - * @return if the year in question is a leap year. - */ - boolean isGregorianLeapYear(int year) { - return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); - } - - /** - * The month, where 1 == January, 2 == February, etc... Note that this is different than Java's Calendar class - * where January == 0. - */ - private int gregorianMonth; - - /** The day of the Gregorian month */ - private int gregorianDayOfMonth; - - /** The Gregorian year */ - private int gregorianYear; - - /** 1 == Sunday, 2 == Monday, etc... */ - private int dayOfWeek; - - /** Returns the absolute date (days since January 1, 0001 of the Gregorian calendar). - * @see #getAbsDate() - * @see #absDateToJewishDate() - */ - private int gregorianAbsDate; - - /** - * Returns the number of days in a given month in a given month and year. - * - * @param month - * the month. As with other cases in this class, this is 1-based, not zero-based. - * @param year - * the year (only impacts February) - * @return the number of days in the month in the given year - */ - private static int getLastDayOfGregorianMonth(int month, int year) { - switch (month) { - case 2: - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - return 29; - } else { - return 28; - } - case 4: - case 6: - case 9: - case 11: - return 30; - default: - return 31; - } - } - - /** - * Computes the Gregorian date from the absolute date. ND+ER - * @param absDate the absolute date - */ - private void absDateToDate(int absDate) { - int year = absDate / 366; // Search forward year by year from approximate year - while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { - year++; - } - - int month = 1; // Search forward month by month from January - while (absDate > gregorianDateToAbsDate(year, month, getLastDayOfGregorianMonth(month, year))) { - month++; - } - - int dayOfMonth = absDate - gregorianDateToAbsDate(year, month, 1) + 1; - setInternalGregorianDate(year, month, dayOfMonth); - } - - /** - * Returns the absolute date (days since January 1, 0001 of the Gregorian calendar). - * - * @return the number of days since January 1, 1 - */ - public int getAbsDate() { - return gregorianAbsDate; - } - - /** - * Computes the absolute date from a Gregorian date. ND+ER - * - * @param year - * the Gregorian year - * @param month - * the Gregorian month. Unlike the Java Calendar where January has the value of 0,This expects a 1 for - * January - * @param dayOfMonth - * the day of the month (1st, 2nd, etc...) - * @return the absolute Gregorian day - */ - private static int gregorianDateToAbsDate(int year, int month, int dayOfMonth) { - int absDate = dayOfMonth; - for (int m = month - 1; m > 0; m--) { - absDate += getLastDayOfGregorianMonth(m, year); // days in prior months of the year - } - return (absDate // days this year - + 365 * (year - 1) // days in previous years ignoring leap days - + (year - 1) / 4 // Julian leap days before this year - - (year - 1) / 100 // minus prior century years - + (year - 1) / 400); // plus prior years divisible by 400 - } - - /** - * Returns if the year is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year cycle are leap years. - * - * @param year - * the Jewish year. - * @return true if it is a leap year - * @see #isJewishLeapYear() - */ - private static boolean isJewishLeapYear(int year) { - return ((7 * year) + 1) % 19 < 7; - } - - /** - * Returns if the year the calendar is set to is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year - * cycle are leap years. - * - * @return true if it is a leap year - * @see #isJewishLeapYear(int) - */ - public boolean isJewishLeapYear() { - return isJewishLeapYear(getJewishYear()); - } - - /** - * Returns the last month of a given Jewish year. This will be 12 on a non {@link #isJewishLeapYear(int) leap year} - * or 13 on a leap year. - * - * @param year - * the Jewish year. - * @return 12 on a non leap year or 13 on a leap year - * @see #isJewishLeapYear(int) - */ - private static int getLastMonthOfJewishYear(int year) { - return isJewishLeapYear(year) ? ADAR_II : ADAR; - } - - /** - * Returns the number of days elapsed from the Sunday prior to the start of the Jewish calendar to the mean - * conjunction of Tishri of the Jewish year. - * - * @param year - * the Jewish year - * @return the number of days elapsed from prior to the molad Tohu BaHaRaD (Be = Monday, Ha = 5 - * hours and RaD = 204 chalakim / parts) prior to the start of the Jewish calendar, to - * the mean conjunction of Tishri of the Jewish year. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 - * chalakim after sunset on Sunday evening). - */ - public static int getJewishCalendarElapsedDays(int year) { - long chalakimSince = getChalakimSinceMoladTohu(year, TISHREI); - int moladDay = (int) (chalakimSince / (long) CHALAKIM_PER_DAY); - int moladParts = (int) (chalakimSince - moladDay * (long) CHALAKIM_PER_DAY); - // delay Rosh Hashana for the 4 dechiyos - return addDechiyos(year, moladDay, moladParts); - } - - /** - * Adds the 4 dechiyos for molad Tishrei. These are: - *

    - *
  1. Lo ADU Rosh - Rosh Hashana can't fall on a Sunday, Wednesday or Friday. If the molad fell on one - * of these days, Rosh Hashana is delayed to the following day.
  2. - *
  3. Molad Zaken - If the molad of Tishrei falls after 12 noon, Rosh Hashana is delayed to the following - * day. If the following day is ADU, it will be delayed an additional day.
  4. - *
  5. GaTRaD - If on a non leap year the molad of Tishrei falls on a Tuesday (Ga) on or after 9 hours - * (T) and (RaD 204 chalakim it is delayed till Thursday (one day delay, plus one day for - * Lo ADU Rosh)
  6. - *
  7. BeTuTaKPaT - if the year following a leap year falls on a Monday (Be) on or after 15 hours - * (Tu) and 589 chalakim (TaKPaT) it is delayed till Tuesday
  8. - *
- * - * @param year the year - * @param moladDay the molad day - * @param moladParts the molad parts - * @return the number of elapsed days in the JewishCalendar adjusted for the 4 dechiyos. - */ - private static int addDechiyos(int year, int moladDay, int moladParts) { - int roshHashanaDay = moladDay; // if no dechiyos - // delay Rosh Hashana for the dechiyos of the Molad - new moon 1 - Molad Zaken, 2- GaTRaD 3- BeTuTaKPaT - if ((moladParts >= 19440) // Dechiya of Molad Zaken - molad is >= midday (18 hours * 1080 chalakim) - || (((moladDay % 7) == 2) // start Dechiya of GaTRaD - Ga = is a Tuesday - && (moladParts >= 9924) // TRaD = 9 hours, 204 parts or later (9 * 1080 + 204) - && !isJewishLeapYear(year)) // of a non-leap year - end Dechiya of GaTRaD - || (((moladDay % 7) == 1) // start Dechiya of BeTuTaKPaT - Be = is on a Monday - && (moladParts >= 16789) // TUTaKPaT part of BeTuTaKPaT = 15 hours, 589 parts or later (15 * 1080 + 589) - && (isJewishLeapYear(year - 1)))) { // in a year following a leap year - end Dechiya of BeTuTaKPaT - roshHashanaDay += 1; // Then postpone Rosh HaShanah one day - } - // start 4th Dechiya - Lo ADU Rosh - Rosh Hashana can't occur on A- sunday, D- Wednesday, U - Friday - if (((roshHashanaDay % 7) == 0)// If Rosh HaShanah would occur on Sunday, - || ((roshHashanaDay % 7) == 3) // or Wednesday, - || ((roshHashanaDay % 7) == 5)) { // or Friday - end 4th Dechiya - Lo ADU Rosh - roshHashanaDay = roshHashanaDay + 1; // Then postpone it one (more) day - } - return roshHashanaDay; - } - - /** - * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - * to the year and month passed in. - * - * @param year - * the Jewish year - * @param month - * the Jewish month the Jewish month, with the month numbers starting from Nissan. Use the JewishDate - * constants such as {@link JewishDate#TISHREI}. - * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - */ - private static long getChalakimSinceMoladTohu(int year, int month) { - // Jewish lunar month = 29 days, 12 hours and 793 chalakim - // chalakim since Molad Tohu BeHaRaD - 1 day, 5 hours and 204 chalakim - int monthOfYear = getJewishMonthOfYear(year, month); - int monthsElapsed = (235 * ((year - 1) / 19)) // Months in complete 19-year lunar (Metonic) cycles so far - + (12 * ((year - 1) % 19)) // Regular months in this cycle - + ((7 * ((year - 1) % 19) + 1) / 19) // Leap months this cycle - + (monthOfYear - 1); // add elapsed months till the start of the molad of the month - // return chalakim prior to BeHaRaD + number of chalakim since - return CHALAKIM_MOLAD_TOHU + (CHALAKIM_PER_MONTH * monthsElapsed); - } - - /** - * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - * to the Jewish year and month that this Object is set to. - * - * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - */ - public long getChalakimSinceMoladTohu() { - return getChalakimSinceMoladTohu(jewishYear, jewishMonth); - } - - /** - * Converts the {@link JewishDate#NISSAN} based constants used by this class to numeric month starting from - * {@link JewishDate#TISHREI}. This is required for molad calculations. - * - * @param year - * The Jewish year - * @param month - * The Jewish Month - * @return the Jewish month of the year starting with Tishrei - */ - private static int getJewishMonthOfYear(int year, int month) { - boolean isLeapYear = isJewishLeapYear(year); - return (month + (isLeapYear ? 6 : 5)) % (isLeapYear ? 13 : 12) + 1; - } - - /** - * Validates the components of a Jewish date for validity. It will throw an {@link IllegalArgumentException} if the Jewish - * date is earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a {@link #isJewishLeapYear(int) - * leap year}), the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 or > 59 or - * chalakim < 0 or > 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the - * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the - * case of 793 / TaShTzaG). - * - * @param year - * the Jewish year to validate. It will reject any year <= 3761 (lower than the year 1 Gregorian). - * @param month - * the Jewish month to validate. It will reject a month < 1 or > 12 (or 13 on a leap year) . - * @param dayOfMonth - * the day of the Jewish month to validate. It will reject any value < 1 or > 30 TODO: check calling - * methods to see if there is any reason that the class can validate that 30 is invalid for some months. - * @param hours - * the hours (for molad calculations). It will reject an hour < 0 or > 23 - * @param minutes - * the minutes (for molad calculations). It will reject a minute < 0 or > 59 - * @param chalakim - * the chalakim / parts (for molad calculations). It will reject a chalakim < 0 or > - * 17. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim - * per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / TaShTzaG) - * - * @throws IllegalArgumentException - * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a leap year), - * the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 or > 59 or chalakim - * < 0 or > 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the - * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek - * in the case of 793 (TaShTzaG). - */ - private static void validateJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { - if (month < NISSAN || month > getLastMonthOfJewishYear(year)) { - throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " - + month + " is invalid for the year " + year + "."); - } - if (dayOfMonth < 1 || dayOfMonth > 30) { - throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth - + " is invalid."); - } - // reject dates prior to 18 Teves, 3761 (1/1/1 AD). This restriction can be relaxed if the date coding is - // changed/corrected - if ((year < 3761) || (year == 3761 && (month >= TISHREI && month < TEVES)) - || (year == 3761 && month == TEVES && dayOfMonth < 18)) { - throw new IllegalArgumentException( - "A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian) can't be set. " + year + ", " + month - + ", " + dayOfMonth + " is invalid."); - } - if (hours < 0 || hours > 23) { - throw new IllegalArgumentException("Hours < 0 or > 23 can't be set. " + hours + " is invalid."); - } - - if (minutes < 0 || minutes > 59) { - throw new IllegalArgumentException("Minutes < 0 or > 59 can't be set. " + minutes + " is invalid."); - } - - if (chalakim < 0 || chalakim > 17) { - throw new IllegalArgumentException( - "Chalakim/parts < 0 or > 17 can't be set. " - + chalakim - + " is invalid. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG)"); - } - } - - /** - * Validates the components of a Gregorian date for validity. It will throw an {@link IllegalArgumentException} if a - * year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in. - * - * @param year - * the Gregorian year to validate. It will reject any year < 1. - * @param month - * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a - * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. - * @param dayOfMonth - * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 - * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to - * see if there is any reason that the class needs days > the maximum. - * @throws IllegalArgumentException - * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in - * @see #validateGregorianYear(int) - * @see #validateGregorianMonth(int) - * @see #validateGregorianDayOfMonth(int) - */ - private static void validateGregorianDate(int year, int month, int dayOfMonth) { - validateGregorianMonth(month); - validateGregorianDayOfMonth(dayOfMonth); - validateGregorianYear(year); - } - - /** - * Validates a Gregorian month for validity. - * - * @param month - * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a - * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. - */ - private static void validateGregorianMonth(int month) { - if (month > 11 || month < 0) { - throw new IllegalArgumentException("The Gregorian month has to be between 0 - 11. " + month - + " is invalid."); - } - } - - /** - * Validates a Gregorian day of month for validity. - * - * @param dayOfMonth - * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 - * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to - * see if there is any reason that the class needs days > the maximum. - */ - private static void validateGregorianDayOfMonth(int dayOfMonth) { - if (dayOfMonth <= 0) { - throw new IllegalArgumentException("The day of month can't be less than 1. " + dayOfMonth + " is invalid."); - } - } - - /** - * Validates a Gregorian year for validity. - * - * @param year - * the Gregorian year to validate. It will reject any year < 1. - */ - private static void validateGregorianYear(int year) { - if (year < 1) { - throw new IllegalArgumentException("Years < 1 can't be calculated. " + year + " is invalid."); - } - } - - /** - * Returns the number of days for a given Jewish year. ND+ER - * - * @param year - * the Jewish year - * @return the number of days for a given Jewish year. - * @see #isCheshvanLong() - * @see #isKislevShort() - */ - public static int getDaysInJewishYear(int year) { - return getJewishCalendarElapsedDays(year + 1) - getJewishCalendarElapsedDays(year); - } - - /** - * Returns the number of days for the current year that the calendar is set to. - * - * @return the number of days for the Object's current Jewish year. - * @see #isCheshvanLong() - * @see #isKislevShort() - * @see #isJewishLeapYear() - */ - public int getDaysInJewishYear() { - return getDaysInJewishYear(getJewishYear()); - } - - /** - * Returns if Cheshvan is long in a given Jewish year. The method name isLong is done since in a Kesidran (ordered) - * year Cheshvan is short. ND+ER - * - * @param year - * the year - * @return true if Cheshvan is long in Jewish year. - * @see #isCheshvanLong() - * @see #getCheshvanKislevKviah() - */ - private static boolean isCheshvanLong(int year) { - return getDaysInJewishYear(year) % 10 == 5; - } - - /** - * Returns if Cheshvan is long (30 days VS 29 days) for the current year that the calendar is set to. The method - * name isLong is done since in a Kesidran (ordered) year Cheshvan is short. - * - * @return true if Cheshvan is long for the current year that the calendar is set to - * @see #isCheshvanLong() - */ - public boolean isCheshvanLong() { - return isCheshvanLong(getJewishYear()); - } - - /** - * Returns if Kislev is short (29 days VS 30 days) in a given Jewish year. The method name isShort is done since in - * a Kesidran (ordered) year Kislev is long. ND+ER - * - * @param year - * the Jewish year - * @return true if Kislev is short for the given Jewish year. - * @see #isKislevShort() - * @see #getCheshvanKislevKviah() - */ - private static boolean isKislevShort(int year) { - return getDaysInJewishYear(year) % 10 == 3; - } - - /** - * Returns if the Kislev is short for the year that this class is set to. The method name isShort is done since in a - * Kesidran (ordered) year Kislev is long. - * - * @return true if Kislev is short for the year that this class is set to - */ - public boolean isKislevShort() { - return isKislevShort(getJewishYear()); - } - - /** - * Returns the Cheshvan and Kislev kviah (whether a Jewish year is short, regular or long). It will return - * {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and Kislev - * is 30 days and {@link #CHASERIM} if both are 29 days. - * - * @return {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and - * Kislev is 30 days and {@link #CHASERIM} if both are 29 days. - * @see #isCheshvanLong() - * @see #isKislevShort() - */ - public int getCheshvanKislevKviah() { - if (isCheshvanLong() && !isKislevShort()) { - return SHELAIMIM; - } else if (!isCheshvanLong() && isKislevShort()) { - return CHASERIM; - } else { - return KESIDRAN; - } - } - - /** - * Returns the number of days of a Jewish month for a given month and year. - * - * @param month - * the Jewish month - * @param year - * the Jewish Year - * @return the number of days for a given Jewish month - */ - private static int getDaysInJewishMonth(int month, int year) { - if ((month == IYAR) || (month == TAMMUZ) || (month == ELUL) || ((month == CHESHVAN) && !(isCheshvanLong(year))) - || ((month == KISLEV) && isKislevShort(year)) || (month == TEVES) - || ((month == ADAR) && !(isJewishLeapYear(year))) || (month == ADAR_II)) { - return 29; - } else { - return 30; - } - } - - /** - * Returns the number of days of the Jewish month that the calendar is currently set to. - * - * @return the number of days for the Jewish month that the calendar is currently set to. - */ - public int getDaysInJewishMonth() { - return getDaysInJewishMonth(getJewishMonth(), getJewishYear()); - } - - /** - * Computes the Jewish date from the absolute date. - */ - private void absDateToJewishDate() { - // Approximation from below - jewishYear = (gregorianAbsDate - JEWISH_EPOCH) / 366; - // Search forward for year from the approximation - while (gregorianAbsDate >= jewishDateToAbsDate(jewishYear + 1, TISHREI, 1)) { - jewishYear++; - } - // Search forward for month from either Tishri or Nissan. - if (gregorianAbsDate < jewishDateToAbsDate(jewishYear, NISSAN, 1)) { - jewishMonth = TISHREI;// Start at Tishri - } else { - jewishMonth = NISSAN;// Start at Nissan - } - while (gregorianAbsDate > jewishDateToAbsDate(jewishYear, jewishMonth, getDaysInJewishMonth())) { - jewishMonth++; - } - // Calculate the day by subtraction - jewishDay = gregorianAbsDate - jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1; - } - - /** - * Returns the absolute date of Jewish date. ND+ER - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. Nissan expects a value of 1 etc. until Adar with a value of 12. - * For a leap year, 13 will be the expected value for Adar II. Use the constants {@link JewishDate#NISSAN} - * etc. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * @return the absolute date of the Jewish date. - */ - private static int jewishDateToAbsDate(int year, int month, int dayOfMonth) { - int elapsed = getDaysSinceStartOfJewishYear(year, month, dayOfMonth); - // add elapsed days this year + Days in prior years + Days elapsed before absolute year 1 - return elapsed + getJewishCalendarElapsedDays(year) + JEWISH_EPOCH; - } - - /** - * Returns the molad for a given year and month. Returns a JewishDate {@link Object} set to the date of the molad - * with the {@link #getMoladHours() hours}, {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() - * chalakim} set. In the current implementation, it sets the molad time based on a midnight date rollover. This - * means that Rosh Chodesh Adar II, 5771 with a molad of 7 chalakim past midnight on Shabbos 29 Adar I / March 5, - * 2011 12:00 AM and 7 chalakim, will have the following values: hours: 0, minutes: 0, Chalakim: 7. - * - * @return a JewishDate {@link Object} set to the date of the molad with the {@link #getMoladHours() hours}, - * {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() chalakim} set. - */ - public JewishDate getMolad() { - JewishDate moladDate = new JewishDate(getChalakimSinceMoladTohu()); - if (moladDate.getMoladHours() >= 6) { - moladDate.forward(Calendar.DATE, 1); - } - moladDate.setMoladHours((moladDate.getMoladHours() + 18) % 24); - return moladDate; - } - - /** - * Returns the number of days from the Jewish epoch from the number of chalakim from the epoch passed in. - * - * @param chalakim - * the number of chalakim since the beginning of Sunday prior to BaHaRaD - * @return the number of days from the Jewish epoch - */ - private static int moladToAbsDate(long chalakim) { - return (int) (chalakim / CHALAKIM_PER_DAY) + JEWISH_EPOCH; - } - - /** - * Constructor that creates a JewishDate based on a molad passed in. The molad would be the number of - * chalakim / parts starting at the beginning of Sunday prior to the Molad Tohu BeHaRaD (Be = - * Monday, Ha = 5 hours and RaD = 204 chalakim / parts) - prior to the start of the Jewish - * calendar. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 chalakim after sunset on Sunday evening). - * - * @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD - */ - public JewishDate(long molad) { - absDateToDate(moladToAbsDate(molad)); - int conjunctionDay = (int) (molad / (long) CHALAKIM_PER_DAY); - int conjunctionParts = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); - setMoladTime(conjunctionParts); - } - - /** - * Sets the molad time (hours minutes and chalakim) based on the number of chalakim since the start of the day. - * - * @param chalakim - * the number of chalakim since the start of the day. - */ - private void setMoladTime(int chalakim) { - int adjustedChalakim = chalakim; - setMoladHours(adjustedChalakim / CHALAKIM_PER_HOUR); - adjustedChalakim = adjustedChalakim - (getMoladHours() * CHALAKIM_PER_HOUR); - setMoladMinutes(adjustedChalakim / CHALAKIM_PER_MINUTE); - setMoladChalakim(adjustedChalakim - moladMinutes * CHALAKIM_PER_MINUTE); - } - - /** - * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. - * - * @param year - * the Jewish year - * @param month - * the Jewish month - * @param dayOfMonth - * the day in the Jewish month - * @return the number of days - */ - private static int getDaysSinceStartOfJewishYear(int year, int month, int dayOfMonth) { - int elapsedDays = dayOfMonth; - // Before Tishrei (from Nissan to Tishrei), add days in prior months - if (month < TISHREI) { - // this year before and after Nissan. - for (int m = TISHREI; m <= getLastMonthOfJewishYear(year); m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - for (int m = NISSAN; m < month; m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - } else { // Add days in prior months this year - for (int m = TISHREI; m < month; m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - } - return elapsedDays; - } - - /** - * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. - * - * @return the number of days - */ - public int getDaysSinceStartOfJewishYear() { - return getDaysSinceStartOfJewishYear(getJewishYear(), getJewishMonth(), getJewishDayOfMonth()); - } - - /** - * Creates a Jewish date based on a Jewish year, month and day of month. - * - * @param jewishYear - * the Jewish year - * @param jewishMonth - * the Jewish month. The method expects a 1 for Nissan ... 12 for Adar and 13 for Adar II. Use the - * constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar II) to avoid any - * confusion. - * @param jewishDayOfMonth - * the Jewish day of month. If 30 is passed in for a month with only 29 days (for example {@link #IYAR}, - * or {@link #KISLEV} in a year that {@link #isKislevShort()}), the 29th (last valid date of the month) - * will be set - * @throws IllegalArgumentException - * if the day of month is < 1 or > 30, or a year of < 0 is passed in. - */ - public JewishDate(int jewishYear, int jewishMonth, int jewishDayOfMonth) { - setJewishDate(jewishYear, jewishMonth, jewishDayOfMonth); - } - - /** - * Default constructor will set a default date to the current system date. - */ - public JewishDate() { - resetDate(); - } - - /** - * A constructor that initializes the date to the {@link java.util.Date Date} parameter. - * - * @param instant - * the Instant to set the calendar to - * @throws IllegalArgumentException - * if the date would fall prior to the January 1, 1 AD - */ - public JewishDate(Instant instant) { - setDate(instant); - } - - /** - * A constructor that initializes the date to the {@link java.util.Calendar Calendar} parameter. - * - * @param zonedDateTime - * the ZonedDateTime to set the calendar to - * @throws IllegalArgumentException - * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} - */ - public JewishDate(ZonedDateTime zonedDateTime) { - setDate(zonedDateTime); - } - - /** - * A constructor that initializes the date to the {@link java.time.LocalDate LocalDate} parameter. - * - * @param localDate - * the LocalDate to set the calendar to - * @throws IllegalArgumentException - * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} - */ - public JewishDate(LocalDate localDate) { - setDate(localDate); - } - - /** - * Sets the date based on a {@link java.util.Calendar Calendar} object. Modifies the Jewish date as well. - * - * @param zonedDateTime - * the ZonedDateTime to set the calendar to - * @throws IllegalArgumentException - * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} - */ - public void setDate(ZonedDateTime zonedDateTime) { - int year = zonedDateTime.getYear(); - - if (year <= 0) { - throw new IllegalArgumentException( - "Calendars with a BC era are not supported. The year " - + year + " BC is invalid." - ); - } - - gregorianYear = year; - gregorianMonth = zonedDateTime.getMonthValue(); // 1 = January - gregorianDayOfMonth = zonedDateTime.getDayOfMonth(); - - // initialize absolute date - gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); - - // convert to Jewish date - absDateToJewishDate(); - - // day of week (same calculation as original) - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; - } - - /** - * Sets the date based on a {@link java.time.Instant Instant} object. Modifies the Jewish date as well. - * - * @param instant - * the Instant to set the calendar to - * @throws IllegalArgumentException - * if the date would fall prior to the year 1 AD - */ - public void setDate(Instant instant) { - setDate(instant.atZone(ZoneId.systemDefault())); - } - - /** - * Sets the date based on a {@link java.time.LocalDate LocalDate} object. Modifies the Jewish date as well. - * - * @param localDate - * the LocalDate to set the calendar to - * @throws IllegalArgumentException - * if the date would fall prior to the year 1 AD - */ - public void setDate(LocalDate localDate) { - ZonedDateTime zdt = localDate.atStartOfDay(ZoneId.systemDefault()); - setDate(zdt); - } - - /** - * Sets the Gregorian Date, and updates the Jewish date accordingly. Like the Java Calendar A value of 0 is expected - * for January. - * - * @param year - * the Gregorian year - * @param month - * the Gregorian month. Like the Java Calendar, this class expects 0 for January - * @param dayOfMonth - * the Gregorian day of month. If this is > the number of days in the month/year, the last valid date of - * the month will be set - * @throws IllegalArgumentException - * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in - */ - public void setGregorianDate(int year, int month, int dayOfMonth) { - validateGregorianDate(year, month, dayOfMonth); - setInternalGregorianDate(year, month + 1, dayOfMonth); - } - - /** - * Sets the hidden internal representation of the Gregorian date , and updates the Jewish date accordingly. While - * public getters and setters have 0 based months matching the Java Calendar classes, This class internally - * represents the Gregorian month starting at 1. When this is called it will not adjust the month to match the Java - * Calendar classes. - * - * @param year the year - * @param month the month - * @param dayOfMonth the day of month - */ - private void setInternalGregorianDate(int year, int month, int dayOfMonth) { - // make sure date is a valid date for the given month, if not, set to last day of month - if (dayOfMonth > getLastDayOfGregorianMonth(month, year)) { - dayOfMonth = getLastDayOfGregorianMonth(month, year); - } - // init month, date, year - gregorianMonth = month; - gregorianDayOfMonth = dayOfMonth; - gregorianYear = year; - - gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); // init date - absDateToJewishDate(); - - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // set day of week - } - - /** - * Sets the Jewish Date and updates the Gregorian date accordingly. - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for - * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar - * II) to avoid any confusion. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * @throws IllegalArgumentException - * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a - * leap year) or the day of month is < 1 or > 30 is passed in - */ - public void setJewishDate(int year, int month, int dayOfMonth) { - setJewishDate(year, month, dayOfMonth, 0, 0, 0); - } - - /** - * Sets the Jewish Date and updates the Gregorian date accordingly. - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for - * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar - * II) to avoid any confusion. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * - * @param hours - * the hour of the day. Used for molad calculations - * @param minutes - * the minutes. Used for molad calculations - * @param chalakim - * the chalakim / parts. Used for molad calculations. The chalakim should not - * exceed 17. Minutes should be used for larger numbers. - * - * @throws IllegalArgumentException - * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a leap year), the day - * of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > 17. For - * larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes (18 - * chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG). - */ - public void setJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { - validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); - - // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 - // days to a month that only has 29) set the date to 29th - if (dayOfMonth > getDaysInJewishMonth(month, year)) { - dayOfMonth = getDaysInJewishMonth(month, year); - } - - jewishMonth = month; - jewishDay = dayOfMonth; - jewishYear = year; - moladHours = hours; - moladMinutes = minutes; - moladChalakim = chalakim; - - gregorianAbsDate = jewishDateToAbsDate(jewishYear, jewishMonth, jewishDay); // reset Gregorian date - absDateToDate(gregorianAbsDate); - - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // reset day of week - } - - /** - * Returns this object's date as a {@link java.util.Calendar} object. - * - * @return The {@link java.util.Calendar} - */ - public Calendar getGregorianCalendar() { - Calendar calendar = Calendar.getInstance(); - calendar.set(getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth()); - return calendar; - } - - /** - * Returns this object's date as a {@link java.time.LocalDate} object. - * - * @return The {@link java.time.LocalDate} - */ - public LocalDate getLocalDate() { - return LocalDate.of(getGregorianYear(), getGregorianMonth() + 1, getGregorianDayOfMonth()); - } - - /** - * Resets this date to the current system date. - */ - public void resetDate() { - ZonedDateTime zdt = ZonedDateTime.now(); - setDate(zdt); - } - - /** - * Returns a string containing the Jewish date in the form, "day Month, year" e.g. "21 Shevat, 5729". For more - * complex formatting, use the formatter classes. - * - * @return the Jewish date in the form "day Month, year" e.g. "21 Shevat, 5729" - * @see HebrewDateFormatter#format(JewishDate) - */ - public String toString() { - return new HebrewDateFormatter().format(this); - } - - /** - * Rolls the date, month or year forward by the amount passed in. It modifies both the Gregorian and Jewish dates accordingly. - * If manipulation beyond the fields supported here is required, use the {@link Calendar} class {@link Calendar#add(int, int)} - * or {@link Calendar#roll(int, int)} methods in the following manner. - * - *
-	 * 
-	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
-	 * 	cal.add(Calendar.MONTH, 3); // add 3 Gregorian months
-	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
-	 * 
-	 * 
- * - * @param field the calendar field to be forwarded. The must be {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} - * @param amount the positive amount to move forward - * @throws IllegalArgumentException if the field is anything besides {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} - * or if the amount is less than 1 - * - * @see #back() - * @see Calendar#add(int, int) - * @see Calendar#roll(int, int) - */ - public void forward(int field, int amount) { //FIXME first param should be converted from the Calendar.DATE - if (field != Calendar.DATE && field != Calendar.MONTH && field != Calendar.YEAR) { - throw new IllegalArgumentException("Unsupported field was passed to Forward. Only Calendar.DATE, Calendar.MONTH or Calendar.YEAR are supported."); - } - if (amount < 1) { - throw new IllegalArgumentException("JewishDate.forward() does not support amounts less than 1. See JewishDate.back()"); - } - if (field == Calendar.DATE) { - // Change Gregorian date - for (int i = 0; i < amount; i++) { - if (gregorianDayOfMonth == getLastDayOfGregorianMonth(gregorianMonth, gregorianYear)) { - gregorianDayOfMonth = 1; - // if last day of year - if (gregorianMonth == 12) { - gregorianYear++; - gregorianMonth = 1; - } else { - gregorianMonth++; - } - } else { // if not last day of month - gregorianDayOfMonth++; - } - - // Change the Jewish Date - if (jewishDay == getDaysInJewishMonth()) { - // if it last day of elul (i.e. last day of Jewish year) - if (jewishMonth == ELUL) { - jewishYear++; - jewishMonth++; - jewishDay = 1; - } else if (jewishMonth == getLastMonthOfJewishYear(jewishYear)) { - // if it is the last day of Adar, or Adar II as case may be - jewishMonth = NISSAN; - jewishDay = 1; - } else { - jewishMonth++; - jewishDay = 1; - } - } else { // if not last date of month - jewishDay++; - } - - if (dayOfWeek == 7) { // if last day of week, loop back to Sunday - dayOfWeek = 1; - } else { - dayOfWeek++; - } - - gregorianAbsDate++; // increment the absolute date - } - } else if (field == Calendar.MONTH) { - forwardJewishMonth(amount); - } else { - setJewishYear(getJewishYear() + amount); - } - } - - /** - * Forward the Jewish date by the number of months passed in. - * FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt with similar to - * the way that the Java Calendar behaves (not that simple since there is a difference between add() or roll(). - * - * @throws IllegalArgumentException if the amount is less than 1 - * @param amount the number of months to roll the month forward - */ - private void forwardJewishMonth(int amount) { - if (amount < 1) { - throw new IllegalArgumentException("the amount of months to forward has to be greater than zero."); - } - for (int i = 0; i < amount; i++) { - if (getJewishMonth() == ELUL) { - setJewishMonth(TISHREI); - setJewishYear(getJewishYear() + 1); - } else if ((! isJewishLeapYear() && getJewishMonth() == ADAR) - || (isJewishLeapYear() && getJewishMonth() == ADAR_II)){ - setJewishMonth(NISSAN); - } else { - setJewishMonth(getJewishMonth() + 1); - } - } - } - - /** - * Rolls the date back by 1 day. It modifies both the Gregorian and Jewish dates accordingly. The API does not - * currently offer the ability to forward more than one day at a time, or to forward by month or year. If such - * manipulation is required use the {@link Calendar} class {@link Calendar#add(int, int)} or - * {@link Calendar#roll(int, int)} methods in the following manner. - * - *
-	 * 
-	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
-	 * 	cal.add(Calendar.MONTH, -3); // subtract 3 Gregorian months
-	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
-	 * 
-	 * 
- * - * @see #back() - * @see Calendar#add(int, int) - * @see Calendar#roll(int, int) - */ - public void back() { - // Change Gregorian date - if (gregorianDayOfMonth == 1) { // if first day of month - if (gregorianMonth == 1) { // if first day of year - gregorianMonth = 12; - gregorianYear--; - } else { - gregorianMonth--; - } - // change to last day of previous month - gregorianDayOfMonth = getLastDayOfGregorianMonth(gregorianMonth, gregorianYear); - } else { - gregorianDayOfMonth--; - } - // change Jewish date - if (jewishDay == 1) { // if first day of the Jewish month - if (jewishMonth == NISSAN) { - jewishMonth = getLastMonthOfJewishYear(jewishYear); - } else if (jewishMonth == TISHREI) { // if Rosh Hashana - jewishYear--; - jewishMonth--; - } else { - jewishMonth--; - } - jewishDay = getDaysInJewishMonth(); - } else { - jewishDay--; - } - - if (dayOfWeek == 1) { // if first day of week, loop back to Saturday - dayOfWeek = 7; - } else { - dayOfWeek--; - } - gregorianAbsDate--; // change the absolute date - } - - /** - * Indicates whether some other object is "equal to" this one. - * @see Object#equals(Object) - */ - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (!(object instanceof JewishDate)) { - return false; - } - JewishDate jewishDate = (JewishDate) object; - return gregorianAbsDate == jewishDate.getAbsDate(); - } - - /** - * Compares two dates as per the compareTo() method in the Comparable interface. Returns a value less than 0 if this - * date is "less than" (before) the date, greater than 0 if this date is "greater than" (after) the date, or 0 if - * they are equal. - */ - public int compareTo(JewishDate jewishDate) { - return Integer.compare(gregorianAbsDate, jewishDate.getAbsDate()); - } - - /** - * Returns the Gregorian month (between 0-11). - * - * @return the Gregorian month (between 0-11). Like the java.util.Calendar, months are 0 based. - */ - public int getGregorianMonth() { - return gregorianMonth - 1; //FIXME - } - - /** - * Returns the Gregorian day of the month. - * - * @return the Gregorian day of the mont - */ - public int getGregorianDayOfMonth() { - return gregorianDayOfMonth; - } - - /** - * Returns the Gregorian year. - * - * @return the Gregorian year - */ - public int getGregorianYear() { - return gregorianYear; - } - - /** - * Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nissan and goes to - * 13 for Adar II - * - * @return the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan and - * goes to 13 for Adar II - */ - public int getJewishMonth() { - return jewishMonth; - } - - /** - * Returns the Jewish day of month. - * - * @return the Jewish day of the month - */ - public int getJewishDayOfMonth() { - return jewishDay; - } - - /** - * Returns the Jewish year. - * - * @return the Jewish year - */ - public int getJewishYear() { - return jewishYear; - } - - /** - * Returns the day of the week as a number between 1-7. - * - * @return the day of the week as a number between 1-7. - */ - public int getDayOfWeek() { - return dayOfWeek; - } - - /** - * Sets the Gregorian month. - * - * @param month - * the Gregorian month - * - * @throws IllegalArgumentException - * if a month < 0 or > 11 is passed in - */ - public void setGregorianMonth(int month) { - validateGregorianMonth(month); - setInternalGregorianDate(gregorianYear, month + 1, gregorianDayOfMonth); //FIXME - } - - /** - * sets the Gregorian year. - * - * @param year - * the Gregorian year. - * @throws IllegalArgumentException - * if a year of < 1 is passed in - */ - public void setGregorianYear(int year) { - validateGregorianYear(year); - setInternalGregorianDate(year, gregorianMonth, gregorianDayOfMonth); - } - - /** - * sets the Gregorian Day of month. - * - * @param dayOfMonth - * the Gregorian Day of month. - * @throws IllegalArgumentException - * if the day of month of < 1 is passed in - */ - public void setGregorianDayOfMonth(int dayOfMonth) { - validateGregorianDayOfMonth(dayOfMonth); - setInternalGregorianDate(gregorianYear, gregorianMonth, dayOfMonth); - } - - /** - * sets the Jewish month. - * - * @param month - * the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan - * and goes to 13 for Adar II - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year) is passed in - */ - public void setJewishMonth(int month) { - setJewishDate(jewishYear, month, jewishDay); - } - - /** - * sets the Jewish year. - * - * @param year - * the Jewish year - * @throws IllegalArgumentException - * if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day - * previously set are < 18 Teves (prior to Jan 1, 1 AD) - */ - public void setJewishYear(int year) { - setJewishDate(year, jewishMonth, jewishDay); - } - - /** - * sets the Jewish day of month. - * - * @param dayOfMonth - * the Jewish day of month - * @throws IllegalArgumentException - * if the day of month is < 1 or > 30 is passed in - */ - public void setJewishDayOfMonth(int dayOfMonth) { - setJewishDate(jewishYear, jewishMonth, dayOfMonth); - } - - /** - * A method that creates a deep copy of the object. - * - * @see Object#clone() - */ - public Object clone() { - JewishDate clone = null; - try { - clone = (JewishDate) super.clone(); - } catch (CloneNotSupportedException cnse) { - // Required by the compiler. Should never be reached since we implement clone() - } - if (clone != null) { - clone.setInternalGregorianDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); - } - return clone; - } - - /** - * Overrides {@link Object#hashCode()}. - * @see Object#hashCode() - */ - public int hashCode() { - int result = 17; - result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash - result += 37 * result + gregorianAbsDate; - return result; - } + /** + * Value of the month field indicating Nissan, the first numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 7th (or 8th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int NISSAN = 1; + + /** + * Value of the month field indicating Iyar, the second numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 8th (or 9th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int IYAR = 2; + + /** + * Value of the month field indicating Sivan, the third numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 9th (or 10th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int SIVAN = 3; + + /** + * Value of the month field indicating Tammuz, the fourth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 10th (or 11th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int TAMMUZ = 4; + + /** + * Value of the month field indicating Av, the fifth numeric month of the year in the Jewish calendar. With the year + * starting at {@link #TISHREI}, it would actually be the 11th (or 12th in a {@link #isJewishLeapYear() leap year}) + * month of the year. + */ + public static final int AV = 5; + + /** + * Value of the month field indicating Elul, the sixth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 12th (or 13th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int ELUL = 6; + + /** + * Value of the month field indicating Tishrei, the seventh numeric month of the year in the Jewish calendar. With + * the year starting at this month, it would actually be the 1st month of the year. + */ + public static final int TISHREI = 7; + + /** + * Value of the month field indicating Cheshvan/marcheshvan, the eighth numeric month of the year in the Jewish + * calendar. With the year starting at {@link #TISHREI}, it would actually be the 2nd month of the year. + */ + public static final int CHESHVAN = 8; + + /** + * Value of the month field indicating Kislev, the ninth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 3rd month of the year. + */ + public static final int KISLEV = 9; + + /** + * Value of the month field indicating Teves, the tenth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 4th month of the year. + */ + public static final int TEVES = 10; + + /** + * Value of the month field indicating Shevat, the eleventh numeric month of the year in the Jewish calendar. With + * the year starting at {@link #TISHREI}, it would actually be the 5th month of the year. + */ + public static final int SHEVAT = 11; + + /** + * Value of the month field indicating Adar (or Adar I in a {@link #isJewishLeapYear() leap year}), the twelfth + * numeric month of the year in the Jewish calendar. With the year starting at {@link #TISHREI}, it would actually + * be the 6th month of the year. + */ + public static final int ADAR = 12; + + /** + * Value of the month field indicating Adar II, the leap (intercalary or embolismic) thirteenth (Undecimber) numeric + * month of the year added in Jewish {@link #isJewishLeapYear() leap year}). The leap years are years 3, 6, 8, 11, + * 14, 17 and 19 of a 19-year cycle. With the year starting at {@link #TISHREI}, it would actually be the 7th month + * of the year. + */ + public static final int ADAR_II = 13; + + /** + * the Jewish epoch using the RD (Rata Die/Fixed Date or Reingold Dershowitz) day used in Calendrical Calculations. + * Day 1 is January 1, 0001 of the Gregorian calendar + */ + private static final int JEWISH_EPOCH = -1373429; + + /** The number of chalakim (18) in a minute.*/ + private static final int CHALAKIM_PER_MINUTE = 18; + /** The number of chalakim (1080) in an hour.*/ + private static final int CHALAKIM_PER_HOUR = 1080; + /** The number of chalakim (25,920) in a 24-hour day .*/ + private static final int CHALAKIM_PER_DAY = 25920; // 24 * 1080 + /** The number of chalakim in an average Jewish month. A month has 29 days, 12 hours and 793 + * chalakim (44 minutes and 3.3 seconds) for a total of 765,433 chalakim*/ + private static final long CHALAKIM_PER_MONTH = 765433; // (29 * 24 + 12) * 1080 + 793 + /** + * Days from the beginning of Sunday till molad BaHaRaD. Calculated as 1 day, 5 hours and 204 chalakim = + * (24 + 5) * 1080 + 204 = 31524 + */ + private static final int CHALAKIM_MOLAD_TOHU = 31524; + + /** + * A short year where both {@link #CHESHVAN} and {@link #KISLEV} are 29 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int CHASERIM = 0; + + /** + * An ordered year where {@link #CHESHVAN} is 29 days and {@link #KISLEV} is 30 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int KESIDRAN = 1; + + /** + * A long year where both {@link #CHESHVAN} and {@link #KISLEV} are 30 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int SHELAIMIM = 2; + + /** the internal Jewish month.*/ + private int jewishMonth; + /** the internal Jewish day.*/ + private int jewishDay; + /** the internal Jewish year.*/ + private int jewishYear; + /** the internal count of molad hours.*/ + private int moladHours; + /** the internal count of molad minutes.*/ + private int moladMinutes; + /** the internal count of molad chalakim.*/ + private int moladChalakim; + /** The absolute day count since January 1, 0001 Gregorian. */ + private int absDate; + /** 1 == Sunday, 2 == Monday, etc... */ + private int dayOfWeek; + + + /** + * Returns the molad hours. Only a JewishDate object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or {@link #setMoladHours(int)} will have this field + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad hours + * @see #setMoladHours(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladHours() { + return moladHours; + } + + /** + * Sets the molad hours. + * + * @param moladHours + * the molad hours to set + * @see #getMoladHours() + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladHours(int moladHours) { + this.moladHours = moladHours; + } + + /** + * Returns the molad minutes. Only an object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladMinutes(int)} will have these fields + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad minutes + * @see #setMoladMinutes(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladMinutes() { + return moladMinutes; + } + + /** + * Sets the molad minutes. The expectation is that the traditional minute-less chalakim will be broken out to + * minutes and {@link #setMoladChalakim(int) chalakim / parts} , so 793 (TaShTZaG) parts would have the minutes set to + * 44 and chalakim to 1. + * + * @param moladMinutes + * the molad minutes to set + * @see #getMoladMinutes() + * @see #setMoladChalakim(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladMinutes(int moladMinutes) { + this.moladMinutes = moladMinutes; + } + + /** + * Sets the molad chalakim/parts. The expectation is that the traditional minute-less chalakim will be broken + * out to {@link #setMoladMinutes(int) minutes} and chalakim, so 793 (TaShTZaG) parts would have the minutes set to 44 and + * chalakim to 1. + * + * @param moladChalakim + * the molad chalakim / parts to set + * @see #getMoladChalakim() + * @see #setMoladMinutes(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladChalakim(int moladChalakim) { + this.moladChalakim = moladChalakim; + } + + /** + * Returns the molad chalakim / parts. Only an object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladChalakim(int)} will have these fields + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad chalakim / parts + * @see #setMoladChalakim(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladChalakim() { + return moladChalakim; + } + + /** + * Computes the Gregorian date from the absolute date. ND+ER + * @param absDate the absolute date + */ + private static LocalDate absDateToDate(int absDate) { + int year = absDate / 366; // Search forward year by year from approximate year + while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { + year++; + } + + + int month = 1; // Search forward month by month from January + while (absDate > gregorianDateToAbsDate(year, month, YearMonth.of(year, month).lengthOfMonth())) { + month++; + } + + int dayOfMonth = absDate - gregorianDateToAbsDate(year, month, 1) + 1; + return LocalDate.of(year,month,dayOfMonth); + } + + /** + * Computes the absolute date from a Gregorian date. ND+ER + * + * @param year + * the Gregorian year + * @param month + * the Gregorian month. Unlike the Java Calendar where January has the value of 0,This expects a 1 for + * January + * @param dayOfMonth + * the day of the month (1st, 2nd, etc...) + * @return the absolute Gregorian day + */ + private static int gregorianDateToAbsDate(int year, int month, int dayOfMonth) { + int absDate = dayOfMonth; + for (int m = month - 1; m > 0; m--) { + absDate += YearMonth.of(year, m).lengthOfMonth(); // days in prior months of the year + } + return (absDate // days this year + + 365 * (year - 1) // days in previous years ignoring leap days + + (year - 1) / 4 // Julian leap days before this year + - (year - 1) / 100 // minus prior century years + + (year - 1) / 400); // plus prior years divisible by 400 + } + + /** + * Returns if the year is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year cycle are leap years. + * + * @param year + * the Jewish year. + * @return true if it is a leap year + * @see #isJewishLeapYear() + */ + private static boolean isJewishLeapYear(int year) { + return ((7 * year) + 1) % 19 < 7; + } + + /** + * Returns if the year the calendar is set to is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year + * cycle are leap years. + * + * @return true if it is a leap year + * @see #isJewishLeapYear(int) + */ + public boolean isJewishLeapYear() { + return isJewishLeapYear(getJewishYear()); + } + + /** + * Returns the last month of a given Jewish year. This will be 12 on a non {@link #isJewishLeapYear(int) leap year} + * or 13 on a leap year. + * + * @param year + * the Jewish year. + * @return 12 on a non leap year or 13 on a leap year + * @see #isJewishLeapYear(int) + */ + private static int getLastMonthOfJewishYear(int year) { + return isJewishLeapYear(year) ? ADAR_II : ADAR; + } + + /** + * Returns the number of days elapsed from the Sunday prior to the start of the Jewish calendar to the mean + * conjunction of Tishri of the Jewish year. + * + * @param year + * the Jewish year + * @return the number of days elapsed from prior to the molad Tohu BaHaRaD (Be = Monday, Ha = 5 + * hours and RaD = 204 chalakim / parts) prior to the start of the Jewish calendar, to + * the mean conjunction of Tishri of the Jewish year. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 + * chalakim after sunset on Sunday evening). + */ + public static int getJewishCalendarElapsedDays(int year) { + long chalakimSince = getChalakimSinceMoladTohu(year, TISHREI); + int moladDay = (int) (chalakimSince / (long) CHALAKIM_PER_DAY); + int moladParts = (int) (chalakimSince - moladDay * (long) CHALAKIM_PER_DAY); + // delay Rosh Hashana for the 4 dechiyos + return addDechiyos(year, moladDay, moladParts); + } + + /** + * Adds the 4 dechiyos for molad Tishrei. These are: + *
    + *
  1. Lo ADU Rosh - Rosh Hashana can't fall on a Sunday, Wednesday or Friday. If the molad fell on one + * of these days, Rosh Hashana is delayed to the following day.
  2. + *
  3. Molad Zaken - If the molad of Tishrei falls after 12 noon, Rosh Hashana is delayed to the following + * day. If the following day is ADU, it will be delayed an additional day.
  4. + *
  5. GaTRaD - If on a non leap year the molad of Tishrei falls on a Tuesday (Ga) on or after 9 hours + * (T) and (RaD 204 chalakim it is delayed till Thursday (one day delay, plus one day for + * Lo ADU Rosh)
  6. + *
  7. BeTuTaKPaT - if the year following a leap year falls on a Monday (Be) on or after 15 hours + * (Tu) and 589 chalakim (TaKPaT) it is delayed till Tuesday
  8. + *
+ * + * @param year the year + * @param moladDay the molad day + * @param moladParts the molad parts + * @return the number of elapsed days in the JewishCalendar adjusted for the 4 dechiyos. + */ + private static int addDechiyos(int year, int moladDay, int moladParts) { + int roshHashanaDay = moladDay; // if no dechiyos + // delay Rosh Hashana for the dechiyos of the Molad - new moon 1 - Molad Zaken, 2- GaTRaD 3- BeTuTaKPaT + if ((moladParts >= 19440) // Dechiya of Molad Zaken - molad is >= midday (18 hours * 1080 chalakim) + || (((moladDay % 7) == 2) // start Dechiya of GaTRaD - Ga = is a Tuesday + && (moladParts >= 9924) // TRaD = 9 hours, 204 parts or later (9 * 1080 + 204) + && !isJewishLeapYear(year)) // of a non-leap year - end Dechiya of GaTRaD + || (((moladDay % 7) == 1) // start Dechiya of BeTuTaKPaT - Be = is on a Monday + && (moladParts >= 16789) // TUTaKPaT part of BeTuTaKPaT = 15 hours, 589 parts or later (15 * 1080 + 589) + && (isJewishLeapYear(year - 1)))) { // in a year following a leap year - end Dechiya of BeTuTaKPaT + roshHashanaDay += 1; // Then postpone Rosh HaShanah one day + } + // start 4th Dechiya - Lo ADU Rosh - Rosh Hashana can't occur on A- sunday, D- Wednesday, U - Friday + if (((roshHashanaDay % 7) == 0)// If Rosh HaShanah would occur on Sunday, + || ((roshHashanaDay % 7) == 3) // or Wednesday, + || ((roshHashanaDay % 7) == 5)) { // or Friday - end 4th Dechiya - Lo ADU Rosh + roshHashanaDay = roshHashanaDay + 1; // Then postpone it one (more) day + } + return roshHashanaDay; + } + + /** + * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + * to the year and month passed in. + * + * @param year + * the Jewish year + * @param month + * the Jewish month the Jewish month, with the month numbers starting from Nissan. Use the JewishDate + * constants such as {@link JewishDate#TISHREI}. + * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + */ + private static long getChalakimSinceMoladTohu(int year, int month) { + // Jewish lunar month = 29 days, 12 hours and 793 chalakim + // chalakim since Molad Tohu BeHaRaD - 1 day, 5 hours and 204 chalakim + int monthOfYear = getJewishMonthOfYear(year, month); + int monthsElapsed = (235 * ((year - 1) / 19)) // Months in complete 19-year lunar (Metonic) cycles so far + + (12 * ((year - 1) % 19)) // Regular months in this cycle + + ((7 * ((year - 1) % 19) + 1) / 19) // Leap months this cycle + + (monthOfYear - 1); // add elapsed months till the start of the molad of the month + // return chalakim prior to BeHaRaD + number of chalakim since + return CHALAKIM_MOLAD_TOHU + (CHALAKIM_PER_MONTH * monthsElapsed); + } + + /** + * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + * to the Jewish year and month that this Object is set to. + * + * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + */ + public long getChalakimSinceMoladTohu() { + return getChalakimSinceMoladTohu(jewishYear, jewishMonth); + } + + /** + * Converts the {@link JewishDate#NISSAN} based constants used by this class to numeric month starting from + * {@link JewishDate#TISHREI}. This is required for molad calculations. + * + * @param year + * The Jewish year + * @param month + * The Jewish Month + * @return the Jewish month of the year starting with Tishrei + */ + private static int getJewishMonthOfYear(int year, int month) { + boolean isLeapYear = isJewishLeapYear(year); + return (month + (isLeapYear ? 6 : 5)) % (isLeapYear ? 13 : 12) + 1; + } + + /** + * Validates the components of a Jewish date for validity. It will throw an {@link IllegalArgumentException} if a + * month < 1 or > 12 (or 13 on a {@link #isJewishLeapYear(int) leap year}), the day of month is < 1 or + * > 30, an hour < 0 or > 23, a minute < 0 or > 59 or chalakim < 0 or > 17. + * For larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes + * (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / + * TaShTzaG). + * + * @param year + * the Jewish year to validate. + * @param month + * the Jewish month to validate. It will reject a month < 1 or > 12 (or 13 on a leap year) . + * @param dayOfMonth + * the day of the Jewish month to validate. It will reject any value < 1 or > 30 TODO: check calling + * methods to see if there is any reason that the class can validate that 30 is invalid for some months. + * @param hours + * the hours (for molad calculations). It will reject an hour < 0 or > 23 + * @param minutes + * the minutes (for molad calculations). It will reject a minute < 0 or > 59 + * @param chalakim + * the chalakim / parts (for molad calculations). It will reject a chalakim < 0 or > + * 17. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim + * per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / TaShTzaG) + * + * @throws IllegalArgumentException + * if a month < 1 or > 12 (or 13 on a leap year), the day of month is < 1 or > 30, + * an hour < 0 or > 23, a minute < 0 or > 59 or chalakim < 0 or > 17. + * For larger a larger number of chalakim such as 793 (TaShTzaG) break the + * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and + * 1 chelek in the case of 793 (TaShTzaG). + */ + private static void validateJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { + validateJewishYear(year); + + if (month < NISSAN || month > getLastMonthOfJewishYear(year)) { + throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " + + month + " is invalid for the year " + year + "."); + } + + if (dayOfMonth < 1 || dayOfMonth > 30) { + // Month-specific overflow is normalized by setJewishDate() + throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth + + " is invalid."); + } + if (hours < 0 || hours > 23) { + throw new IllegalArgumentException("Hours < 0 or > 23 can't be set. " + hours + " is invalid."); + } + + if (minutes < 0 || minutes > 59) { + throw new IllegalArgumentException("Minutes < 0 or > 59 can't be set. " + minutes + " is invalid."); + } + + if (chalakim < 0 || chalakim > 17) { + throw new IllegalArgumentException( + "Chalakim/parts < 0 or > 17 can't be set. " + + chalakim + + " is invalid. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG)"); + } + } + + private static void validateJewishYear(int year) { + if (year < 1) { + throw new IllegalArgumentException("A Jewish year of less than 1 can't be set. " + year + " is invalid."); + } + } + + /** + * Returns the number of days for a given Jewish year. ND+ER + * + * @param year + * the Jewish year + * @return the number of days for a given Jewish year. + * @see #isCheshvanLong() + * @see #isKislevShort() + */ + public static int getDaysInJewishYear(int year) { + return getJewishCalendarElapsedDays(year + 1) - getJewishCalendarElapsedDays(year); + } + + /** + * Returns the number of days for the current year that the calendar is set to. + * + * @return the number of days for the Object's current Jewish year. + * @see #isCheshvanLong() + * @see #isKislevShort() + * @see #isJewishLeapYear() + */ + public int getDaysInJewishYear() { + return getDaysInJewishYear(getJewishYear()); + } + + /** + * Returns if Cheshvan is long in a given Jewish year. The method name isLong is done since in a Kesidran (ordered) + * year Cheshvan is short. ND+ER + * + * @param year + * the year + * @return true if Cheshvan is long in Jewish year. + * @see #isCheshvanLong() + * @see #getCheshvanKislevKviah() + */ + private static boolean isCheshvanLong(int year) { + return getDaysInJewishYear(year) % 10 == 5; + } + + /** + * Returns if Cheshvan is long (30 days VS 29 days) for the current year that the calendar is set to. The method + * name isLong is done since in a Kesidran (ordered) year Cheshvan is short. + * + * @return true if Cheshvan is long for the current year that the calendar is set to + * @see #isCheshvanLong() + */ + public boolean isCheshvanLong() { + return isCheshvanLong(getJewishYear()); + } + + /** + * Returns if Kislev is short (29 days VS 30 days) in a given Jewish year. The method name isShort is done since in + * a Kesidran (ordered) year Kislev is long. ND+ER + * + * @param year + * the Jewish year + * @return true if Kislev is short for the given Jewish year. + * @see #isKislevShort() + * @see #getCheshvanKislevKviah() + */ + private static boolean isKislevShort(int year) { + return getDaysInJewishYear(year) % 10 == 3; + } + + /** + * Returns if the Kislev is short for the year that this class is set to. The method name isShort is done since in a + * Kesidran (ordered) year Kislev is long. + * + * @return true if Kislev is short for the year that this class is set to + */ + public boolean isKislevShort() { + return isKislevShort(getJewishYear()); + } + + /** + * Returns the Cheshvan and Kislev kviah (whether a Jewish year is short, regular or long). It will return + * {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and Kislev + * is 30 days and {@link #CHASERIM} if both are 29 days. + * + * @return {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and + * Kislev is 30 days and {@link #CHASERIM} if both are 29 days. + * @see #isCheshvanLong() + * @see #isKislevShort() + */ + public int getCheshvanKislevKviah() { + if (isCheshvanLong() && !isKislevShort()) { + return SHELAIMIM; + } else if (!isCheshvanLong() && isKislevShort()) { + return CHASERIM; + } else { + return KESIDRAN; + } + } + + /** + * Returns the number of days of a Jewish month for a given month and year. + * + * @param month + * the Jewish month + * @param year + * the Jewish Year + * @return the number of days for a given Jewish month + */ + private static int getDaysInJewishMonth(int month, int year) { + if ((month == IYAR) || (month == TAMMUZ) || (month == ELUL) || ((month == CHESHVAN) && !(isCheshvanLong(year))) + || ((month == KISLEV) && isKislevShort(year)) || (month == TEVES) + || ((month == ADAR) && !(isJewishLeapYear(year))) || (month == ADAR_II)) { + return 29; + } else { + return 30; + } + } + + /** + * Returns the number of days of the Jewish month that the calendar is currently set to. + * + * @return the number of days for the Jewish month that the calendar is currently set to. + */ + public int getDaysInJewishMonth() { + return getDaysInJewishMonth(getJewishMonth(), getJewishYear()); + } + /** + * Computes the Jewish date from the absolute date. + */ + private void setAbsDate(int absDate) { + if (absDate <= 0) { + throw new IllegalStateException("Dates before January 1, 1 Gregorian are not supported."); + } + this.absDate = absDate; + // Approximation from below + jewishYear = (absDate - JEWISH_EPOCH) / 366; + // Search forward for year from the approximation + while (absDate >= jewishDateToAbsDate(jewishYear + 1, TISHREI, 1)) { + jewishYear++; + } + // Search forward for month from either Tishri or Nissan. + if (absDate < jewishDateToAbsDate(jewishYear, NISSAN, 1)) { + jewishMonth = TISHREI;// Start at Tishri + } else { + jewishMonth = NISSAN;// Start at Nissan + } + while (absDate > jewishDateToAbsDate(jewishYear, jewishMonth, getDaysInJewishMonth())) { + jewishMonth++; + } + // Calculate the day by subtraction + jewishDay = absDate - jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1; + + // Set the day of the week + dayOfWeek = Math.abs(absDate % 7) + 1; + } + + /** + * Returns the absolute date of Jewish date. ND+ER + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. Nissan expects a value of 1 etc. until Adar with a value of 12. + * For a leap year, 13 will be the expected value for Adar II. Use the constants {@link JewishDate#NISSAN} + * etc. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * @return the absolute date of the Jewish date. + */ + private static int jewishDateToAbsDate(int year, int month, int dayOfMonth) { + int elapsed = getDaysSinceStartOfJewishYear(year, month, dayOfMonth); + // add elapsed days this year + Days in prior years + Days elapsed before absolute year 1 + return elapsed + getJewishCalendarElapsedDays(year) + JEWISH_EPOCH; + } + + /** + * Returns the molad for a given year and month. Returns a JewishDate {@link Object} set to the date of the molad + * with the {@link #getMoladHours() hours}, {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() + * chalakim} set. In the current implementation, it sets the molad time based on a midnight date rollover. This + * means that Rosh Chodesh Adar II, 5771 with a molad of 7 chalakim past midnight on Shabbos 29 Adar I / March 5, + * 2011 12:00 AM and 7 chalakim, will have the following values: hours: 0, minutes: 0, Chalakim: 7. + * + * @return a JewishDate {@link Object} set to the date of the molad with the {@link #getMoladHours() hours}, + * {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() chalakim} set. + */ + public JewishDate getMolad() { + JewishDate moladDate = new JewishDate(getChalakimSinceMoladTohu()); + if (moladDate.getMoladHours() >= 6) { + moladDate.forward(); + } + moladDate.setMoladHours((moladDate.getMoladHours() + 18) % 24); + return moladDate; + } + + /** + * Returns the number of days from the Jewish epoch from the number of chalakim from the epoch passed in. + * + * @param chalakim + * the number of chalakim since the beginning of Sunday prior to BaHaRaD + * @return the number of days from the Jewish epoch + */ + private static int moladToAbsDate(long chalakim) { + return (int) (chalakim / CHALAKIM_PER_DAY) + JEWISH_EPOCH; + } + + /** + * Constructor that creates a JewishDate based on a molad passed in. The molad would be the number of + * chalakim / parts starting at the beginning of Sunday prior to the Molad Tohu BeHaRaD (Be = + * Monday, Ha = 5 hours and RaD = 204 chalakim / parts) - prior to the start of the Jewish + * calendar. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 chalakim after sunset on Sunday evening). + * + * @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD + */ + public JewishDate(long molad) { + setAbsDate(moladToAbsDate(molad)); + int conjunctionDay = (int) (molad / (long) CHALAKIM_PER_DAY); + int chalakim = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); + setMoladHours(chalakim / CHALAKIM_PER_HOUR); + chalakim = chalakim - (getMoladHours() * CHALAKIM_PER_HOUR); + setMoladMinutes(chalakim / CHALAKIM_PER_MINUTE); + setMoladChalakim(chalakim - moladMinutes * CHALAKIM_PER_MINUTE); + } + + /** + * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. + * + * @param year + * the Jewish year + * @param month + * the Jewish month + * @param dayOfMonth + * the day in the Jewish month + * @return the number of days + */ + private static int getDaysSinceStartOfJewishYear(int year, int month, int dayOfMonth) { + int elapsedDays = dayOfMonth; + // Before Tishrei (from Nissan to Tishrei), add days in prior months + if (month < TISHREI) { + // this year before and after Nissan. + for (int m = TISHREI; m <= getLastMonthOfJewishYear(year); m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + for (int m = NISSAN; m < month; m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + } else { // Add days in prior months this year + for (int m = TISHREI; m < month; m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + } + return elapsedDays; + } + + /** + * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. + * + * @return the number of days + */ + public int getDaysSinceStartOfJewishYear() { + return getDaysSinceStartOfJewishYear(getJewishYear(), getJewishMonth(), getJewishDayOfMonth()); + } + + /** + * Creates a Jewish date based on a Jewish year, month and day of month. + * + * @param jewishYear + * the Jewish year + * @param jewishMonth + * the Jewish month. The method expects a 1 for Nissan ... 12 for Adar and 13 for Adar II. Use the + * constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar II) to avoid any + * confusion. + * @param jewishDayOfMonth + * the Jewish day of month. If 30 is passed in for a month with only 29 days (for example {@link #IYAR}, + * or {@link #KISLEV} in a year that {@link #isKislevShort()}), the 29th (last valid date of the month) + * will be set + * @throws IllegalArgumentException + * if the day of month is < 1 or > 30, or a year of < 0 is passed in. + */ + public JewishDate(int jewishYear, int jewishMonth, int jewishDayOfMonth) { + setJewishDate(jewishYear, jewishMonth, jewishDayOfMonth); + } + + /** + * Default constructor will set a default date to the current system date. + */ + public JewishDate() { + resetDate(); + } + + /** + * A constructor that initializes the date to the {@link java.time.ZonedDateTime ZonedDateTime} parameter. + * + * @param zonedDateTime + * the ZonedDateTime to set the calendar to + * @throws IllegalStateException + * if the resulting date is before January 1, 1 Gregorian + */ + public JewishDate(ZonedDateTime zonedDateTime) { + setLocalDate(zonedDateTime.toLocalDate()); + } + + /** + * A constructor that initializes the date to the {@link java.time.LocalDate LocalDate} parameter. + * + * @param localDate + * the LocalDate to set the calendar to + * @throws IllegalStateException + * if the resulting date is before January 1, 1 Gregorian + */ + public JewishDate(LocalDate localDate) { + setLocalDate(localDate); + } + + /** + * Sets the date based on a {@link java.time.LocalDate LocalDate} object. Modifies the Jewish date as well. + * + * @param localDate + * the LocalDate to set the calendar to + * @throws IllegalStateException + * if the resulting date is before January 1, 1 Gregorian + */ + public void setLocalDate(LocalDate localDate) { + // initialize absolute date + setAbsDate(gregorianDateToAbsDate(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth())); + } + + + + /** + * Sets the Jewish date and updates the derived absolute date and day of week accordingly. + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for + * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar + * II) to avoid any confusion. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * @throws IllegalArgumentException + * if a month < 1 or > 12 (or 13 on a leap year) or the day of month is < 1 or > 30 is passed in + * @throws IllegalStateException + * if the resulting date is before January 1, 1 Gregorian + */ + public void setJewishDate(int year, int month, int dayOfMonth) { + setJewishDate(year, month, dayOfMonth, 0, 0, 0); + } + + /** + * Sets the Jewish date and updates the derived absolute date and day of week accordingly. + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for + * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar + * II) to avoid any confusion. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * + * @param hours + * the hour of the day. Used for molad calculations + * @param minutes + * the minutes. Used for molad calculations + * @param chalakim + * the chalakim / parts. Used for molad calculations. The chalakim should not + * exceed 17. Minutes should be used for larger numbers. + * + * @throws IllegalArgumentException + * if a month < 1 or > 12 (or 13 on a leap year), the day of month is < 1 or > 30, an hour + * < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > 17. For larger a larger + * number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes + * (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of + * 793 (TaShTzaG). + * @throws IllegalStateException + * if the resulting date is before January 1, 1 Gregorian + */ + public void setJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { + validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); + + // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 + // days to a month that only has 29) set the date to 29th + if (dayOfMonth > getDaysInJewishMonth(month, year)) { + dayOfMonth = getDaysInJewishMonth(month, year); + } + + + moladHours = hours; + moladMinutes = minutes; + moladChalakim = chalakim; + setAbsDate(jewishDateToAbsDate(year, month, dayOfMonth)); + } + + /** + * Returns this object's date as a {@link java.time.LocalDate} object. + * + * @return The {@link java.time.LocalDate} + */ + public LocalDate getLocalDate() { + return absDateToDate(absDate); + } + + /** + * Resets this date to the current system date. + */ + public void resetDate() { + ZonedDateTime zdt = ZonedDateTime.now(); + setLocalDate(zdt.toLocalDate()); + } + + /** + * Returns a string containing the Jewish date in the form, "day Month, year" e.g. "21 Shevat, 5729". For more + * complex formatting, use the formatter classes. + * + * @return the Jewish date in the form "day Month, year" e.g. "21 Shevat, 5729" + * @see HebrewDateFormatter#format(JewishDate) + */ + public String toString() { + return new HebrewDateFormatter().format(this); + } + + /** + * Rolls the date forward by one day. + * + * @see #back() + */ + public void forward() { + setAbsDate(absDate+1); + } + + /** + * Forwards the Jewish date by the number of months passed in. + * FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt + * with similar to the way that the Java Calendar behaves. + * + * @throws IllegalArgumentException if the amount is less than 1 + * @param amount the number of months to roll the month forward + */ + public void forwardJewishMonth(int amount) { + if (amount < 1) { + throw new IllegalArgumentException("the amount of months to forward has to be greater than zero."); + } + int currentMonth = getJewishMonth(); + int currentYear = getJewishYear(); + for (int i = 0; i < amount; i++) { + if (currentMonth == ELUL) { + currentMonth = TISHREI; + currentYear++; + } else if ((! isJewishLeapYear(currentYear) && currentMonth == ADAR) + || (isJewishLeapYear(currentYear) && currentMonth == ADAR_II)){ + currentMonth = NISSAN; + } else { + currentMonth++; + } + } + safeSetJewishYearAndMonth(currentYear,currentMonth); + } + /** + * Forwards the Jewish date by the number of years passed in while keeping the current Jewish month when possible. + * + * @throws IllegalArgumentException if the amount is less than 1 + * @param amount the number of years to move forward + */ + public void forwardJewishYear(int amount) { + if (amount < 1) { + throw new IllegalArgumentException("the amount of years to forward has to be greater than zero."); + } + safeSetJewishYearAndMonth(getJewishYear() + amount,getJewishMonth()); + } + private void safeSetJewishYearAndMonth(int year, int month){ + validateJewishYear(year); + if (month > getLastMonthOfJewishYear(year)) { + month = getLastMonthOfJewishYear(year); + } + int max = getDaysInJewishMonth(month,year); + setJewishDate(year, month, Math.min(jewishDay, max)); + } + + /** + * Rolls the date back by one day. + * + * @see #forward() + */ + public void back() { + setAbsDate(absDate-1); + } + + /** + * Indicates whether some other object is "equal to" this one. + * @see Object#equals(Object) + */ + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + JewishDate jewishDate = (JewishDate) object; + return absDate == jewishDate.getAbsDate(); + } + + /** + * Compares two dates as per the compareTo() method in the Comparable interface. Returns a value less than 0 if this + * date is "less than" (before) the date, greater than 0 if this date is "greater than" (after) the date, or 0 if + * they are equal. + */ + public int compareTo(JewishDate jewishDate) { + return Integer.compare(absDate, jewishDate.getAbsDate()); + } + + /** + * Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nissan and goes to + * 13 for Adar II + * + * @return the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan and + * goes to 13 for Adar II + */ + public int getJewishMonth() { + return jewishMonth; + } + + /** + * Returns the Jewish day of month. + * + * @return the Jewish day of the month + */ + public int getJewishDayOfMonth() { + return jewishDay; + } + + /** + * Returns the Jewish year. + * + * @return the Jewish year + */ + public int getJewishYear() { + return jewishYear; + } + + /** + * Returns the absolute date. + * + * @return the absolute date. + */ + public int getAbsDate() { + return absDate; + } + + /** + * Returns the day of the week as a number between 1-7. + * + * @return the day of the week as a number between 1-7. + */ + public int getDayOfWeek() { + return dayOfWeek; + } + + /** + * sets the Jewish month. + * + * @param month + * the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan + * and goes to 13 for Adar II + * @throws IllegalArgumentException + * if a month < 1 or > 12 (or 13 on a leap year) is passed in + */ + public void setJewishMonth(int month) { + setJewishDate(jewishYear, month, jewishDay); + } + + /** + * sets the Jewish year. + * + * @param year + * the Jewish year + * @throws IllegalArgumentException + * if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day + * previously set are < 18 Teves (prior to Jan 1, 1 AD) + */ + public void setJewishYear(int year) { + safeSetJewishYearAndMonth(year, jewishMonth); + } + + /** + * sets the Jewish day of month. + * + * @param dayOfMonth + * the Jewish day of month + * @throws IllegalArgumentException + * if the day of month is < 1 or > 30 is passed in + */ + public void setJewishDayOfMonth(int dayOfMonth) { + setJewishDate(jewishYear, jewishMonth, dayOfMonth); + } + + /** + * A method that creates a deep copy of the object. + * + * @see Object#clone() + */ + public Object clone() { + JewishDate clone = null; + try { + clone = (JewishDate) super.clone(); + } catch (CloneNotSupportedException cnse) { + // Required by the compiler. Should never be reached since we implement clone() + } + if (clone != null) { + clone.setAbsDate(getAbsDate()); + } + return clone; + } + + /** + * Overrides {@link Object#hashCode()}. + * @see Object#hashCode() + */ + public int hashCode() { + int result = 17; + result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash + result += 37 * result + getAbsDate(); + return result; + } } From 94d72eabe681bb196c189ee27e09c76ae570555b Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 18 Mar 2026 14:28:23 -0400 Subject: [PATCH 7/9] revert --- .../zmanim/AstronomicalCalendar.java | 4 +- .../zmanim/hebrewcalendar/JewishDate.java | 2639 ++++++++++------- 2 files changed, 1511 insertions(+), 1132 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index aca523ac..30c98747 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -617,7 +617,7 @@ protected enum SolarEvent { * @param time * The time to be set as the time for the Instant. The time expected is in the format: 18.75 * for 6:45:00 PM.time is sunrise and false if it is sunset - * @param solarEvent the type of {@link SolarEvent}. + * @param solarEvent the type of {@link SolarEvent} * @return The Instant object representation of the time double */ @@ -749,7 +749,7 @@ public Instant getLocalMeanTime(double hours) { double rawOffset = getGeoLocation().getZoneId().getRules().getOffset(getMidnightLastNight().toInstant()).getTotalSeconds() * 1000; double utcTime = hours - rawOffset / (double) HOUR_MILLIS; - Instant instant = getInstantFromTime(utcTime, null); + Instant instant = getInstantFromTime(utcTime, SolarEvent.SUNRISE); return getTimeOffset(instant, -getGeoLocation().getLocalMeanTimeOffset(getMidnightLastNight().toInstant())); } diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java index 1ede5e88..db74d09d 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java @@ -17,1154 +17,1533 @@ package com.kosherjava.zmanim.hebrewcalendar; import java.time.LocalDate; -import java.time.YearMonth; import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.GregorianCalendar; /** - * The JewishDate is the base calendar class that maintains a Jewish date together with an absolute date and day of - * week. Gregorian dates are derived from the absolute date as needed, and exposed via the Java time APIs. - * This class does not have a concept of a time of day. Please note that the calendar does not currently support dates - * prior to 1/1/1 Gregorian. Also keep in mind that the Gregorian calendar started on October 15, 1582, so any - * calculations prior to that are suspect (at least from a Gregorian perspective). While 1/1/1 Gregorian and forward - * are technically supported, any calculations prior to Hillel II's - * (Hakatan's) calendar (4119 in the Jewish Calendar / 359 CE Julian as recorded by - * Rav Hai Gaon) would be just an approximation. - * + * The JewishDate is the base calendar class, that supports maintenance of a {@link java.util.GregorianCalendar} + * instance along with the corresponding Jewish date. This class can use the standard Java Date and Calendar + * classes for setting and maintaining the dates, but it does not subclass these classes or use them internally + * in any calculations. This class also does not have a concept of a time (which the Date class does). Please + * note that the calendar does not currently support dates prior to 1/1/1 Gregorian. Also keep in mind that the + * Gregorian calendar started on October 15, 1582, so any calculations prior to that are suspect (at least from + * a Gregorian perspective). While 1/1/1 Gregorian and forward are technically supported, any calculations prior to Hillel II's (Hakatan's) calendar (4119 in the Jewish Calendar / 359 + * CE Julian as recorded by Rav Hai Gaon) would be just an + * approximation. + * * This open source Java code was written by Avrom Finkelstien from his C++ * code. It was refactored to fit the KosherJava Zmanim API with simplification of the code, enhancements and some bug * fixing. - * + * * Some of Avrom's original C++ code was translated from * C/C++ code in * Calendrical Calculations by Nachum Dershowitz and Edward M. * Reingold, Software-- Practice & Experience, vol. 20, no. 9 (September, 1990), pp. 899- 928. Any method with the mark * "ND+ER" indicates that the method was taken from this source with minor modifications. - * + * * If you are looking for a class that implements a Jewish calendar version of the Calendar class, one is available from * the ICU (International Components for Unicode) project, formerly part of * IBM's DeveloperWorks. - * + * * @see JewishCalendar * @see HebrewDateFormatter - * @see java.time.LocalDate - * @see java.time.ZonedDateTime + * @see java.util.Date + * @see java.util.Calendar * @author © Avrom Finkelstien 2002 * @author © Eliyahu Hershfeld 2011 - 2026 */ public class JewishDate implements Comparable, Cloneable { - /** - * Value of the month field indicating Nissan, the first numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 7th (or 8th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int NISSAN = 1; - - /** - * Value of the month field indicating Iyar, the second numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 8th (or 9th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int IYAR = 2; - - /** - * Value of the month field indicating Sivan, the third numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 9th (or 10th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int SIVAN = 3; - - /** - * Value of the month field indicating Tammuz, the fourth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 10th (or 11th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int TAMMUZ = 4; - - /** - * Value of the month field indicating Av, the fifth numeric month of the year in the Jewish calendar. With the year - * starting at {@link #TISHREI}, it would actually be the 11th (or 12th in a {@link #isJewishLeapYear() leap year}) - * month of the year. - */ - public static final int AV = 5; - - /** - * Value of the month field indicating Elul, the sixth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 12th (or 13th in a {@link #isJewishLeapYear() leap - * year}) month of the year. - */ - public static final int ELUL = 6; - - /** - * Value of the month field indicating Tishrei, the seventh numeric month of the year in the Jewish calendar. With - * the year starting at this month, it would actually be the 1st month of the year. - */ - public static final int TISHREI = 7; - - /** - * Value of the month field indicating Cheshvan/marcheshvan, the eighth numeric month of the year in the Jewish - * calendar. With the year starting at {@link #TISHREI}, it would actually be the 2nd month of the year. - */ - public static final int CHESHVAN = 8; - - /** - * Value of the month field indicating Kislev, the ninth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 3rd month of the year. - */ - public static final int KISLEV = 9; - - /** - * Value of the month field indicating Teves, the tenth numeric month of the year in the Jewish calendar. With the - * year starting at {@link #TISHREI}, it would actually be the 4th month of the year. - */ - public static final int TEVES = 10; - - /** - * Value of the month field indicating Shevat, the eleventh numeric month of the year in the Jewish calendar. With - * the year starting at {@link #TISHREI}, it would actually be the 5th month of the year. - */ - public static final int SHEVAT = 11; - - /** - * Value of the month field indicating Adar (or Adar I in a {@link #isJewishLeapYear() leap year}), the twelfth - * numeric month of the year in the Jewish calendar. With the year starting at {@link #TISHREI}, it would actually - * be the 6th month of the year. - */ - public static final int ADAR = 12; - - /** - * Value of the month field indicating Adar II, the leap (intercalary or embolismic) thirteenth (Undecimber) numeric - * month of the year added in Jewish {@link #isJewishLeapYear() leap year}). The leap years are years 3, 6, 8, 11, - * 14, 17 and 19 of a 19-year cycle. With the year starting at {@link #TISHREI}, it would actually be the 7th month - * of the year. - */ - public static final int ADAR_II = 13; - - /** - * the Jewish epoch using the RD (Rata Die/Fixed Date or Reingold Dershowitz) day used in Calendrical Calculations. - * Day 1 is January 1, 0001 of the Gregorian calendar - */ - private static final int JEWISH_EPOCH = -1373429; - - /** The number of chalakim (18) in a minute.*/ - private static final int CHALAKIM_PER_MINUTE = 18; - /** The number of chalakim (1080) in an hour.*/ - private static final int CHALAKIM_PER_HOUR = 1080; - /** The number of chalakim (25,920) in a 24-hour day .*/ - private static final int CHALAKIM_PER_DAY = 25920; // 24 * 1080 - /** The number of chalakim in an average Jewish month. A month has 29 days, 12 hours and 793 - * chalakim (44 minutes and 3.3 seconds) for a total of 765,433 chalakim*/ - private static final long CHALAKIM_PER_MONTH = 765433; // (29 * 24 + 12) * 1080 + 793 - /** - * Days from the beginning of Sunday till molad BaHaRaD. Calculated as 1 day, 5 hours and 204 chalakim = - * (24 + 5) * 1080 + 204 = 31524 - */ - private static final int CHALAKIM_MOLAD_TOHU = 31524; - - /** - * A short year where both {@link #CHESHVAN} and {@link #KISLEV} are 29 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int CHASERIM = 0; - - /** - * An ordered year where {@link #CHESHVAN} is 29 days and {@link #KISLEV} is 30 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int KESIDRAN = 1; - - /** - * A long year where both {@link #CHESHVAN} and {@link #KISLEV} are 30 days. - * - * @see #getCheshvanKislevKviah() - * @see HebrewDateFormatter#getFormattedKviah(int) - */ - public static final int SHELAIMIM = 2; - - /** the internal Jewish month.*/ - private int jewishMonth; - /** the internal Jewish day.*/ - private int jewishDay; - /** the internal Jewish year.*/ - private int jewishYear; - /** the internal count of molad hours.*/ - private int moladHours; - /** the internal count of molad minutes.*/ - private int moladMinutes; - /** the internal count of molad chalakim.*/ - private int moladChalakim; - /** The absolute day count since January 1, 0001 Gregorian. */ - private int absDate; - /** 1 == Sunday, 2 == Monday, etc... */ - private int dayOfWeek; - - - /** - * Returns the molad hours. Only a JewishDate object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or {@link #setMoladHours(int)} will have this field - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad hours - * @see #setMoladHours(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladHours() { - return moladHours; - } - - /** - * Sets the molad hours. - * - * @param moladHours - * the molad hours to set - * @see #getMoladHours() - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladHours(int moladHours) { - this.moladHours = moladHours; - } - - /** - * Returns the molad minutes. Only an object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladMinutes(int)} will have these fields - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad minutes - * @see #setMoladMinutes(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladMinutes() { - return moladMinutes; - } - - /** - * Sets the molad minutes. The expectation is that the traditional minute-less chalakim will be broken out to - * minutes and {@link #setMoladChalakim(int) chalakim / parts} , so 793 (TaShTZaG) parts would have the minutes set to - * 44 and chalakim to 1. - * - * @param moladMinutes - * the molad minutes to set - * @see #getMoladMinutes() - * @see #setMoladChalakim(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladMinutes(int moladMinutes) { - this.moladMinutes = moladMinutes; - } - - /** - * Sets the molad chalakim/parts. The expectation is that the traditional minute-less chalakim will be broken - * out to {@link #setMoladMinutes(int) minutes} and chalakim, so 793 (TaShTZaG) parts would have the minutes set to 44 and - * chalakim to 1. - * - * @param moladChalakim - * the molad chalakim / parts to set - * @see #getMoladChalakim() - * @see #setMoladMinutes(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - * - */ - public void setMoladChalakim(int moladChalakim) { - this.moladChalakim = moladChalakim; - } - - /** - * Returns the molad chalakim / parts. Only an object populated with {@link #getMolad()}, - * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladChalakim(int)} will have these fields - * populated. A regular JewishDate object will have this field set to 0. - * - * @return the molad chalakim / parts - * @see #setMoladChalakim(int) - * @see #getMolad() - * @see #setJewishDate(int, int, int, int, int, int) - */ - public int getMoladChalakim() { - return moladChalakim; - } - - /** - * Computes the Gregorian date from the absolute date. ND+ER - * @param absDate the absolute date - */ - private static LocalDate absDateToDate(int absDate) { - int year = absDate / 366; // Search forward year by year from approximate year - while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { - year++; - } - - - int month = 1; // Search forward month by month from January - while (absDate > gregorianDateToAbsDate(year, month, YearMonth.of(year, month).lengthOfMonth())) { - month++; - } - - int dayOfMonth = absDate - gregorianDateToAbsDate(year, month, 1) + 1; - return LocalDate.of(year,month,dayOfMonth); - } - - /** - * Computes the absolute date from a Gregorian date. ND+ER - * - * @param year - * the Gregorian year - * @param month - * the Gregorian month. Unlike the Java Calendar where January has the value of 0,This expects a 1 for - * January - * @param dayOfMonth - * the day of the month (1st, 2nd, etc...) - * @return the absolute Gregorian day - */ - private static int gregorianDateToAbsDate(int year, int month, int dayOfMonth) { - int absDate = dayOfMonth; - for (int m = month - 1; m > 0; m--) { - absDate += YearMonth.of(year, m).lengthOfMonth(); // days in prior months of the year - } - return (absDate // days this year - + 365 * (year - 1) // days in previous years ignoring leap days - + (year - 1) / 4 // Julian leap days before this year - - (year - 1) / 100 // minus prior century years - + (year - 1) / 400); // plus prior years divisible by 400 - } - - /** - * Returns if the year is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year cycle are leap years. - * - * @param year - * the Jewish year. - * @return true if it is a leap year - * @see #isJewishLeapYear() - */ - private static boolean isJewishLeapYear(int year) { - return ((7 * year) + 1) % 19 < 7; - } - - /** - * Returns if the year the calendar is set to is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year - * cycle are leap years. - * - * @return true if it is a leap year - * @see #isJewishLeapYear(int) - */ - public boolean isJewishLeapYear() { - return isJewishLeapYear(getJewishYear()); - } - - /** - * Returns the last month of a given Jewish year. This will be 12 on a non {@link #isJewishLeapYear(int) leap year} - * or 13 on a leap year. - * - * @param year - * the Jewish year. - * @return 12 on a non leap year or 13 on a leap year - * @see #isJewishLeapYear(int) - */ - private static int getLastMonthOfJewishYear(int year) { - return isJewishLeapYear(year) ? ADAR_II : ADAR; - } - - /** - * Returns the number of days elapsed from the Sunday prior to the start of the Jewish calendar to the mean - * conjunction of Tishri of the Jewish year. - * - * @param year - * the Jewish year - * @return the number of days elapsed from prior to the molad Tohu BaHaRaD (Be = Monday, Ha = 5 - * hours and RaD = 204 chalakim / parts) prior to the start of the Jewish calendar, to - * the mean conjunction of Tishri of the Jewish year. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 - * chalakim after sunset on Sunday evening). - */ - public static int getJewishCalendarElapsedDays(int year) { - long chalakimSince = getChalakimSinceMoladTohu(year, TISHREI); - int moladDay = (int) (chalakimSince / (long) CHALAKIM_PER_DAY); - int moladParts = (int) (chalakimSince - moladDay * (long) CHALAKIM_PER_DAY); - // delay Rosh Hashana for the 4 dechiyos - return addDechiyos(year, moladDay, moladParts); - } - - /** - * Adds the 4 dechiyos for molad Tishrei. These are: - *
    - *
  1. Lo ADU Rosh - Rosh Hashana can't fall on a Sunday, Wednesday or Friday. If the molad fell on one - * of these days, Rosh Hashana is delayed to the following day.
  2. - *
  3. Molad Zaken - If the molad of Tishrei falls after 12 noon, Rosh Hashana is delayed to the following - * day. If the following day is ADU, it will be delayed an additional day.
  4. - *
  5. GaTRaD - If on a non leap year the molad of Tishrei falls on a Tuesday (Ga) on or after 9 hours - * (T) and (RaD 204 chalakim it is delayed till Thursday (one day delay, plus one day for - * Lo ADU Rosh)
  6. - *
  7. BeTuTaKPaT - if the year following a leap year falls on a Monday (Be) on or after 15 hours - * (Tu) and 589 chalakim (TaKPaT) it is delayed till Tuesday
  8. - *
- * - * @param year the year - * @param moladDay the molad day - * @param moladParts the molad parts - * @return the number of elapsed days in the JewishCalendar adjusted for the 4 dechiyos. - */ - private static int addDechiyos(int year, int moladDay, int moladParts) { - int roshHashanaDay = moladDay; // if no dechiyos - // delay Rosh Hashana for the dechiyos of the Molad - new moon 1 - Molad Zaken, 2- GaTRaD 3- BeTuTaKPaT - if ((moladParts >= 19440) // Dechiya of Molad Zaken - molad is >= midday (18 hours * 1080 chalakim) - || (((moladDay % 7) == 2) // start Dechiya of GaTRaD - Ga = is a Tuesday - && (moladParts >= 9924) // TRaD = 9 hours, 204 parts or later (9 * 1080 + 204) - && !isJewishLeapYear(year)) // of a non-leap year - end Dechiya of GaTRaD - || (((moladDay % 7) == 1) // start Dechiya of BeTuTaKPaT - Be = is on a Monday - && (moladParts >= 16789) // TUTaKPaT part of BeTuTaKPaT = 15 hours, 589 parts or later (15 * 1080 + 589) - && (isJewishLeapYear(year - 1)))) { // in a year following a leap year - end Dechiya of BeTuTaKPaT - roshHashanaDay += 1; // Then postpone Rosh HaShanah one day - } - // start 4th Dechiya - Lo ADU Rosh - Rosh Hashana can't occur on A- sunday, D- Wednesday, U - Friday - if (((roshHashanaDay % 7) == 0)// If Rosh HaShanah would occur on Sunday, - || ((roshHashanaDay % 7) == 3) // or Wednesday, - || ((roshHashanaDay % 7) == 5)) { // or Friday - end 4th Dechiya - Lo ADU Rosh - roshHashanaDay = roshHashanaDay + 1; // Then postpone it one (more) day - } - return roshHashanaDay; - } - - /** - * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - * to the year and month passed in. - * - * @param year - * the Jewish year - * @param month - * the Jewish month the Jewish month, with the month numbers starting from Nissan. Use the JewishDate - * constants such as {@link JewishDate#TISHREI}. - * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - */ - private static long getChalakimSinceMoladTohu(int year, int month) { - // Jewish lunar month = 29 days, 12 hours and 793 chalakim - // chalakim since Molad Tohu BeHaRaD - 1 day, 5 hours and 204 chalakim - int monthOfYear = getJewishMonthOfYear(year, month); - int monthsElapsed = (235 * ((year - 1) / 19)) // Months in complete 19-year lunar (Metonic) cycles so far - + (12 * ((year - 1) % 19)) // Regular months in this cycle - + ((7 * ((year - 1) % 19) + 1) / 19) // Leap months this cycle - + (monthOfYear - 1); // add elapsed months till the start of the molad of the month - // return chalakim prior to BeHaRaD + number of chalakim since - return CHALAKIM_MOLAD_TOHU + (CHALAKIM_PER_MONTH * monthsElapsed); - } - - /** - * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - * to the Jewish year and month that this Object is set to. - * - * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu - */ - public long getChalakimSinceMoladTohu() { - return getChalakimSinceMoladTohu(jewishYear, jewishMonth); - } - - /** - * Converts the {@link JewishDate#NISSAN} based constants used by this class to numeric month starting from - * {@link JewishDate#TISHREI}. This is required for molad calculations. - * - * @param year - * The Jewish year - * @param month - * The Jewish Month - * @return the Jewish month of the year starting with Tishrei - */ - private static int getJewishMonthOfYear(int year, int month) { - boolean isLeapYear = isJewishLeapYear(year); - return (month + (isLeapYear ? 6 : 5)) % (isLeapYear ? 13 : 12) + 1; - } - - /** - * Validates the components of a Jewish date for validity. It will throw an {@link IllegalArgumentException} if a - * month < 1 or > 12 (or 13 on a {@link #isJewishLeapYear(int) leap year}), the day of month is < 1 or - * > 30, an hour < 0 or > 23, a minute < 0 or > 59 or chalakim < 0 or > 17. - * For larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes - * (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / - * TaShTzaG). - * - * @param year - * the Jewish year to validate. - * @param month - * the Jewish month to validate. It will reject a month < 1 or > 12 (or 13 on a leap year) . - * @param dayOfMonth - * the day of the Jewish month to validate. It will reject any value < 1 or > 30 TODO: check calling - * methods to see if there is any reason that the class can validate that 30 is invalid for some months. - * @param hours - * the hours (for molad calculations). It will reject an hour < 0 or > 23 - * @param minutes - * the minutes (for molad calculations). It will reject a minute < 0 or > 59 - * @param chalakim - * the chalakim / parts (for molad calculations). It will reject a chalakim < 0 or > - * 17. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim - * per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / TaShTzaG) - * - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year), the day of month is < 1 or > 30, - * an hour < 0 or > 23, a minute < 0 or > 59 or chalakim < 0 or > 17. - * For larger a larger number of chalakim such as 793 (TaShTzaG) break the - * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and - * 1 chelek in the case of 793 (TaShTzaG). - */ - private static void validateJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { - validateJewishYear(year); - - if (month < NISSAN || month > getLastMonthOfJewishYear(year)) { - throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " - + month + " is invalid for the year " + year + "."); - } - - if (dayOfMonth < 1 || dayOfMonth > 30) { - // Month-specific overflow is normalized by setJewishDate() - throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth - + " is invalid."); - } - if (hours < 0 || hours > 23) { - throw new IllegalArgumentException("Hours < 0 or > 23 can't be set. " + hours + " is invalid."); - } - - if (minutes < 0 || minutes > 59) { - throw new IllegalArgumentException("Minutes < 0 or > 59 can't be set. " + minutes + " is invalid."); - } - - if (chalakim < 0 || chalakim > 17) { - throw new IllegalArgumentException( - "Chalakim/parts < 0 or > 17 can't be set. " - + chalakim - + " is invalid. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG)"); - } - } - - private static void validateJewishYear(int year) { - if (year < 1) { - throw new IllegalArgumentException("A Jewish year of less than 1 can't be set. " + year + " is invalid."); - } - } - - /** - * Returns the number of days for a given Jewish year. ND+ER - * - * @param year - * the Jewish year - * @return the number of days for a given Jewish year. - * @see #isCheshvanLong() - * @see #isKislevShort() - */ - public static int getDaysInJewishYear(int year) { - return getJewishCalendarElapsedDays(year + 1) - getJewishCalendarElapsedDays(year); - } - - /** - * Returns the number of days for the current year that the calendar is set to. - * - * @return the number of days for the Object's current Jewish year. - * @see #isCheshvanLong() - * @see #isKislevShort() - * @see #isJewishLeapYear() - */ - public int getDaysInJewishYear() { - return getDaysInJewishYear(getJewishYear()); - } - - /** - * Returns if Cheshvan is long in a given Jewish year. The method name isLong is done since in a Kesidran (ordered) - * year Cheshvan is short. ND+ER - * - * @param year - * the year - * @return true if Cheshvan is long in Jewish year. - * @see #isCheshvanLong() - * @see #getCheshvanKislevKviah() - */ - private static boolean isCheshvanLong(int year) { - return getDaysInJewishYear(year) % 10 == 5; - } - - /** - * Returns if Cheshvan is long (30 days VS 29 days) for the current year that the calendar is set to. The method - * name isLong is done since in a Kesidran (ordered) year Cheshvan is short. - * - * @return true if Cheshvan is long for the current year that the calendar is set to - * @see #isCheshvanLong() - */ - public boolean isCheshvanLong() { - return isCheshvanLong(getJewishYear()); - } - - /** - * Returns if Kislev is short (29 days VS 30 days) in a given Jewish year. The method name isShort is done since in - * a Kesidran (ordered) year Kislev is long. ND+ER - * - * @param year - * the Jewish year - * @return true if Kislev is short for the given Jewish year. - * @see #isKislevShort() - * @see #getCheshvanKislevKviah() - */ - private static boolean isKislevShort(int year) { - return getDaysInJewishYear(year) % 10 == 3; - } - - /** - * Returns if the Kislev is short for the year that this class is set to. The method name isShort is done since in a - * Kesidran (ordered) year Kislev is long. - * - * @return true if Kislev is short for the year that this class is set to - */ - public boolean isKislevShort() { - return isKislevShort(getJewishYear()); - } - - /** - * Returns the Cheshvan and Kislev kviah (whether a Jewish year is short, regular or long). It will return - * {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and Kislev - * is 30 days and {@link #CHASERIM} if both are 29 days. - * - * @return {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and - * Kislev is 30 days and {@link #CHASERIM} if both are 29 days. - * @see #isCheshvanLong() - * @see #isKislevShort() - */ - public int getCheshvanKislevKviah() { - if (isCheshvanLong() && !isKislevShort()) { - return SHELAIMIM; - } else if (!isCheshvanLong() && isKislevShort()) { - return CHASERIM; - } else { - return KESIDRAN; - } - } - - /** - * Returns the number of days of a Jewish month for a given month and year. - * - * @param month - * the Jewish month - * @param year - * the Jewish Year - * @return the number of days for a given Jewish month - */ - private static int getDaysInJewishMonth(int month, int year) { - if ((month == IYAR) || (month == TAMMUZ) || (month == ELUL) || ((month == CHESHVAN) && !(isCheshvanLong(year))) - || ((month == KISLEV) && isKislevShort(year)) || (month == TEVES) - || ((month == ADAR) && !(isJewishLeapYear(year))) || (month == ADAR_II)) { - return 29; - } else { - return 30; - } - } - - /** - * Returns the number of days of the Jewish month that the calendar is currently set to. - * - * @return the number of days for the Jewish month that the calendar is currently set to. - */ - public int getDaysInJewishMonth() { - return getDaysInJewishMonth(getJewishMonth(), getJewishYear()); - } - /** - * Computes the Jewish date from the absolute date. - */ - private void setAbsDate(int absDate) { - if (absDate <= 0) { - throw new IllegalStateException("Dates before January 1, 1 Gregorian are not supported."); - } - this.absDate = absDate; - // Approximation from below - jewishYear = (absDate - JEWISH_EPOCH) / 366; - // Search forward for year from the approximation - while (absDate >= jewishDateToAbsDate(jewishYear + 1, TISHREI, 1)) { - jewishYear++; - } - // Search forward for month from either Tishri or Nissan. - if (absDate < jewishDateToAbsDate(jewishYear, NISSAN, 1)) { - jewishMonth = TISHREI;// Start at Tishri - } else { - jewishMonth = NISSAN;// Start at Nissan - } - while (absDate > jewishDateToAbsDate(jewishYear, jewishMonth, getDaysInJewishMonth())) { - jewishMonth++; - } - // Calculate the day by subtraction - jewishDay = absDate - jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1; - - // Set the day of the week - dayOfWeek = Math.abs(absDate % 7) + 1; - } - - /** - * Returns the absolute date of Jewish date. ND+ER - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. Nissan expects a value of 1 etc. until Adar with a value of 12. - * For a leap year, 13 will be the expected value for Adar II. Use the constants {@link JewishDate#NISSAN} - * etc. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * @return the absolute date of the Jewish date. - */ - private static int jewishDateToAbsDate(int year, int month, int dayOfMonth) { - int elapsed = getDaysSinceStartOfJewishYear(year, month, dayOfMonth); - // add elapsed days this year + Days in prior years + Days elapsed before absolute year 1 - return elapsed + getJewishCalendarElapsedDays(year) + JEWISH_EPOCH; - } - - /** - * Returns the molad for a given year and month. Returns a JewishDate {@link Object} set to the date of the molad - * with the {@link #getMoladHours() hours}, {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() - * chalakim} set. In the current implementation, it sets the molad time based on a midnight date rollover. This - * means that Rosh Chodesh Adar II, 5771 with a molad of 7 chalakim past midnight on Shabbos 29 Adar I / March 5, - * 2011 12:00 AM and 7 chalakim, will have the following values: hours: 0, minutes: 0, Chalakim: 7. - * - * @return a JewishDate {@link Object} set to the date of the molad with the {@link #getMoladHours() hours}, - * {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() chalakim} set. - */ - public JewishDate getMolad() { - JewishDate moladDate = new JewishDate(getChalakimSinceMoladTohu()); - if (moladDate.getMoladHours() >= 6) { - moladDate.forward(); - } - moladDate.setMoladHours((moladDate.getMoladHours() + 18) % 24); - return moladDate; - } - - /** - * Returns the number of days from the Jewish epoch from the number of chalakim from the epoch passed in. - * - * @param chalakim - * the number of chalakim since the beginning of Sunday prior to BaHaRaD - * @return the number of days from the Jewish epoch - */ - private static int moladToAbsDate(long chalakim) { - return (int) (chalakim / CHALAKIM_PER_DAY) + JEWISH_EPOCH; - } - - /** - * Constructor that creates a JewishDate based on a molad passed in. The molad would be the number of - * chalakim / parts starting at the beginning of Sunday prior to the Molad Tohu BeHaRaD (Be = - * Monday, Ha = 5 hours and RaD = 204 chalakim / parts) - prior to the start of the Jewish - * calendar. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 chalakim after sunset on Sunday evening). - * - * @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD - */ - public JewishDate(long molad) { - setAbsDate(moladToAbsDate(molad)); - int conjunctionDay = (int) (molad / (long) CHALAKIM_PER_DAY); - int chalakim = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); - setMoladHours(chalakim / CHALAKIM_PER_HOUR); - chalakim = chalakim - (getMoladHours() * CHALAKIM_PER_HOUR); - setMoladMinutes(chalakim / CHALAKIM_PER_MINUTE); - setMoladChalakim(chalakim - moladMinutes * CHALAKIM_PER_MINUTE); - } - - /** - * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. - * - * @param year - * the Jewish year - * @param month - * the Jewish month - * @param dayOfMonth - * the day in the Jewish month - * @return the number of days - */ - private static int getDaysSinceStartOfJewishYear(int year, int month, int dayOfMonth) { - int elapsedDays = dayOfMonth; - // Before Tishrei (from Nissan to Tishrei), add days in prior months - if (month < TISHREI) { - // this year before and after Nissan. - for (int m = TISHREI; m <= getLastMonthOfJewishYear(year); m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - for (int m = NISSAN; m < month; m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - } else { // Add days in prior months this year - for (int m = TISHREI; m < month; m++) { - elapsedDays += getDaysInJewishMonth(m, year); - } - } - return elapsedDays; - } - - /** - * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. - * - * @return the number of days - */ - public int getDaysSinceStartOfJewishYear() { - return getDaysSinceStartOfJewishYear(getJewishYear(), getJewishMonth(), getJewishDayOfMonth()); - } - - /** - * Creates a Jewish date based on a Jewish year, month and day of month. - * - * @param jewishYear - * the Jewish year - * @param jewishMonth - * the Jewish month. The method expects a 1 for Nissan ... 12 for Adar and 13 for Adar II. Use the - * constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar II) to avoid any - * confusion. - * @param jewishDayOfMonth - * the Jewish day of month. If 30 is passed in for a month with only 29 days (for example {@link #IYAR}, - * or {@link #KISLEV} in a year that {@link #isKislevShort()}), the 29th (last valid date of the month) - * will be set - * @throws IllegalArgumentException - * if the day of month is < 1 or > 30, or a year of < 0 is passed in. - */ - public JewishDate(int jewishYear, int jewishMonth, int jewishDayOfMonth) { - setJewishDate(jewishYear, jewishMonth, jewishDayOfMonth); - } - - /** - * Default constructor will set a default date to the current system date. - */ - public JewishDate() { - resetDate(); - } - - /** - * A constructor that initializes the date to the {@link java.time.ZonedDateTime ZonedDateTime} parameter. - * - * @param zonedDateTime - * the ZonedDateTime to set the calendar to - * @throws IllegalStateException - * if the resulting date is before January 1, 1 Gregorian - */ - public JewishDate(ZonedDateTime zonedDateTime) { - setLocalDate(zonedDateTime.toLocalDate()); - } - - /** - * A constructor that initializes the date to the {@link java.time.LocalDate LocalDate} parameter. - * - * @param localDate - * the LocalDate to set the calendar to - * @throws IllegalStateException - * if the resulting date is before January 1, 1 Gregorian - */ - public JewishDate(LocalDate localDate) { - setLocalDate(localDate); - } - - /** - * Sets the date based on a {@link java.time.LocalDate LocalDate} object. Modifies the Jewish date as well. - * - * @param localDate - * the LocalDate to set the calendar to - * @throws IllegalStateException - * if the resulting date is before January 1, 1 Gregorian - */ - public void setLocalDate(LocalDate localDate) { - // initialize absolute date - setAbsDate(gregorianDateToAbsDate(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth())); - } - - - - /** - * Sets the Jewish date and updates the derived absolute date and day of week accordingly. - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for - * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar - * II) to avoid any confusion. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year) or the day of month is < 1 or > 30 is passed in - * @throws IllegalStateException - * if the resulting date is before January 1, 1 Gregorian - */ - public void setJewishDate(int year, int month, int dayOfMonth) { - setJewishDate(year, month, dayOfMonth, 0, 0, 0); - } - - /** - * Sets the Jewish date and updates the derived absolute date and day of week accordingly. - * - * @param year - * the Jewish year. The year can't be negative - * @param month - * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for - * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar - * II) to avoid any confusion. - * @param dayOfMonth - * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only - * has 29 days, the day will be set as 29. - * - * @param hours - * the hour of the day. Used for molad calculations - * @param minutes - * the minutes. Used for molad calculations - * @param chalakim - * the chalakim / parts. Used for molad calculations. The chalakim should not - * exceed 17. Minutes should be used for larger numbers. - * - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year), the day of month is < 1 or > 30, an hour - * < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > 17. For larger a larger - * number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes - * (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of - * 793 (TaShTzaG). - * @throws IllegalStateException - * if the resulting date is before January 1, 1 Gregorian - */ - public void setJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { - validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); - - // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 - // days to a month that only has 29) set the date to 29th - if (dayOfMonth > getDaysInJewishMonth(month, year)) { - dayOfMonth = getDaysInJewishMonth(month, year); - } - - - moladHours = hours; - moladMinutes = minutes; - moladChalakim = chalakim; - setAbsDate(jewishDateToAbsDate(year, month, dayOfMonth)); - } - - /** - * Returns this object's date as a {@link java.time.LocalDate} object. - * - * @return The {@link java.time.LocalDate} - */ - public LocalDate getLocalDate() { - return absDateToDate(absDate); - } - - /** - * Resets this date to the current system date. - */ - public void resetDate() { - ZonedDateTime zdt = ZonedDateTime.now(); - setLocalDate(zdt.toLocalDate()); - } - - /** - * Returns a string containing the Jewish date in the form, "day Month, year" e.g. "21 Shevat, 5729". For more - * complex formatting, use the formatter classes. - * - * @return the Jewish date in the form "day Month, year" e.g. "21 Shevat, 5729" - * @see HebrewDateFormatter#format(JewishDate) - */ - public String toString() { - return new HebrewDateFormatter().format(this); - } - - /** - * Rolls the date forward by one day. - * - * @see #back() - */ - public void forward() { - setAbsDate(absDate+1); - } - - /** - * Forwards the Jewish date by the number of months passed in. - * FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt - * with similar to the way that the Java Calendar behaves. - * - * @throws IllegalArgumentException if the amount is less than 1 - * @param amount the number of months to roll the month forward - */ - public void forwardJewishMonth(int amount) { - if (amount < 1) { - throw new IllegalArgumentException("the amount of months to forward has to be greater than zero."); - } - int currentMonth = getJewishMonth(); - int currentYear = getJewishYear(); - for (int i = 0; i < amount; i++) { - if (currentMonth == ELUL) { - currentMonth = TISHREI; - currentYear++; - } else if ((! isJewishLeapYear(currentYear) && currentMonth == ADAR) - || (isJewishLeapYear(currentYear) && currentMonth == ADAR_II)){ - currentMonth = NISSAN; - } else { - currentMonth++; - } - } - safeSetJewishYearAndMonth(currentYear,currentMonth); - } - /** - * Forwards the Jewish date by the number of years passed in while keeping the current Jewish month when possible. - * - * @throws IllegalArgumentException if the amount is less than 1 - * @param amount the number of years to move forward - */ - public void forwardJewishYear(int amount) { - if (amount < 1) { - throw new IllegalArgumentException("the amount of years to forward has to be greater than zero."); - } - safeSetJewishYearAndMonth(getJewishYear() + amount,getJewishMonth()); - } - private void safeSetJewishYearAndMonth(int year, int month){ - validateJewishYear(year); - if (month > getLastMonthOfJewishYear(year)) { - month = getLastMonthOfJewishYear(year); - } - int max = getDaysInJewishMonth(month,year); - setJewishDate(year, month, Math.min(jewishDay, max)); - } - - /** - * Rolls the date back by one day. - * - * @see #forward() - */ - public void back() { - setAbsDate(absDate-1); - } - - /** - * Indicates whether some other object is "equal to" this one. - * @see Object#equals(Object) - */ - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - JewishDate jewishDate = (JewishDate) object; - return absDate == jewishDate.getAbsDate(); - } - - /** - * Compares two dates as per the compareTo() method in the Comparable interface. Returns a value less than 0 if this - * date is "less than" (before) the date, greater than 0 if this date is "greater than" (after) the date, or 0 if - * they are equal. - */ - public int compareTo(JewishDate jewishDate) { - return Integer.compare(absDate, jewishDate.getAbsDate()); - } - - /** - * Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nissan and goes to - * 13 for Adar II - * - * @return the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan and - * goes to 13 for Adar II - */ - public int getJewishMonth() { - return jewishMonth; - } - - /** - * Returns the Jewish day of month. - * - * @return the Jewish day of the month - */ - public int getJewishDayOfMonth() { - return jewishDay; - } - - /** - * Returns the Jewish year. - * - * @return the Jewish year - */ - public int getJewishYear() { - return jewishYear; - } - - /** - * Returns the absolute date. - * - * @return the absolute date. - */ - public int getAbsDate() { - return absDate; - } - - /** - * Returns the day of the week as a number between 1-7. - * - * @return the day of the week as a number between 1-7. - */ - public int getDayOfWeek() { - return dayOfWeek; - } - - /** - * sets the Jewish month. - * - * @param month - * the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan - * and goes to 13 for Adar II - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year) is passed in - */ - public void setJewishMonth(int month) { - setJewishDate(jewishYear, month, jewishDay); - } - - /** - * sets the Jewish year. - * - * @param year - * the Jewish year - * @throws IllegalArgumentException - * if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day - * previously set are < 18 Teves (prior to Jan 1, 1 AD) - */ - public void setJewishYear(int year) { - safeSetJewishYearAndMonth(year, jewishMonth); - } - - /** - * sets the Jewish day of month. - * - * @param dayOfMonth - * the Jewish day of month - * @throws IllegalArgumentException - * if the day of month is < 1 or > 30 is passed in - */ - public void setJewishDayOfMonth(int dayOfMonth) { - setJewishDate(jewishYear, jewishMonth, dayOfMonth); - } - - /** - * A method that creates a deep copy of the object. - * - * @see Object#clone() - */ - public Object clone() { - JewishDate clone = null; - try { - clone = (JewishDate) super.clone(); - } catch (CloneNotSupportedException cnse) { - // Required by the compiler. Should never be reached since we implement clone() - } - if (clone != null) { - clone.setAbsDate(getAbsDate()); - } - return clone; - } - - /** - * Overrides {@link Object#hashCode()}. - * @see Object#hashCode() - */ - public int hashCode() { - int result = 17; - result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash - result += 37 * result + getAbsDate(); - return result; - } + /** + * Value of the month field indicating Nissan, the first numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 7th (or 8th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int NISSAN = 1; + + /** + * Value of the month field indicating Iyar, the second numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 8th (or 9th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int IYAR = 2; + + /** + * Value of the month field indicating Sivan, the third numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 9th (or 10th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int SIVAN = 3; + + /** + * Value of the month field indicating Tammuz, the fourth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 10th (or 11th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int TAMMUZ = 4; + + /** + * Value of the month field indicating Av, the fifth numeric month of the year in the Jewish calendar. With the year + * starting at {@link #TISHREI}, it would actually be the 11th (or 12th in a {@link #isJewishLeapYear() leap year}) + * month of the year. + */ + public static final int AV = 5; + + /** + * Value of the month field indicating Elul, the sixth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 12th (or 13th in a {@link #isJewishLeapYear() leap + * year}) month of the year. + */ + public static final int ELUL = 6; + + /** + * Value of the month field indicating Tishrei, the seventh numeric month of the year in the Jewish calendar. With + * the year starting at this month, it would actually be the 1st month of the year. + */ + public static final int TISHREI = 7; + + /** + * Value of the month field indicating Cheshvan/marcheshvan, the eighth numeric month of the year in the Jewish + * calendar. With the year starting at {@link #TISHREI}, it would actually be the 2nd month of the year. + */ + public static final int CHESHVAN = 8; + + /** + * Value of the month field indicating Kislev, the ninth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 3rd month of the year. + */ + public static final int KISLEV = 9; + + /** + * Value of the month field indicating Teves, the tenth numeric month of the year in the Jewish calendar. With the + * year starting at {@link #TISHREI}, it would actually be the 4th month of the year. + */ + public static final int TEVES = 10; + + /** + * Value of the month field indicating Shevat, the eleventh numeric month of the year in the Jewish calendar. With + * the year starting at {@link #TISHREI}, it would actually be the 5th month of the year. + */ + public static final int SHEVAT = 11; + + /** + * Value of the month field indicating Adar (or Adar I in a {@link #isJewishLeapYear() leap year}), the twelfth + * numeric month of the year in the Jewish calendar. With the year starting at {@link #TISHREI}, it would actually + * be the 6th month of the year. + */ + public static final int ADAR = 12; + + /** + * Value of the month field indicating Adar II, the leap (intercalary or embolismic) thirteenth (Undecimber) numeric + * month of the year added in Jewish {@link #isJewishLeapYear() leap year}). The leap years are years 3, 6, 8, 11, + * 14, 17 and 19 of a 19-year cycle. With the year starting at {@link #TISHREI}, it would actually be the 7th month + * of the year. + */ + public static final int ADAR_II = 13; + + /** + * the Jewish epoch using the RD (Rata Die/Fixed Date or Reingold Dershowitz) day used in Calendrical Calculations. + * Day 1 is January 1, 0001 of the Gregorian calendar + */ + private static final int JEWISH_EPOCH = -1373429; + + /** The number of chalakim (18) in a minute.*/ + private static final int CHALAKIM_PER_MINUTE = 18; + /** The number of chalakim (1080) in an hour.*/ + private static final int CHALAKIM_PER_HOUR = 1080; + /** The number of chalakim (25,920) in a 24-hour day .*/ + private static final int CHALAKIM_PER_DAY = 25920; // 24 * 1080 + /** The number of chalakim in an average Jewish month. A month has 29 days, 12 hours and 793 + * chalakim (44 minutes and 3.3 seconds) for a total of 765,433 chalakim*/ + private static final long CHALAKIM_PER_MONTH = 765433; // (29 * 24 + 12) * 1080 + 793 + /** + * Days from the beginning of Sunday till molad BaHaRaD. Calculated as 1 day, 5 hours and 204 chalakim = + * (24 + 5) * 1080 + 204 = 31524 + */ + private static final int CHALAKIM_MOLAD_TOHU = 31524; + + /** + * A short year where both {@link #CHESHVAN} and {@link #KISLEV} are 29 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int CHASERIM = 0; + + /** + * An ordered year where {@link #CHESHVAN} is 29 days and {@link #KISLEV} is 30 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int KESIDRAN = 1; + + /** + * A long year where both {@link #CHESHVAN} and {@link #KISLEV} are 30 days. + * + * @see #getCheshvanKislevKviah() + * @see HebrewDateFormatter#getFormattedKviah(int) + */ + public static final int SHELAIMIM = 2; + + /** the internal Jewish month.*/ + private int jewishMonth; + /** the internal Jewish day.*/ + private int jewishDay; + /** the internal Jewish year.*/ + private int jewishYear; + /** the internal count of molad hours.*/ + private int moladHours; + /** the internal count of molad minutes.*/ + private int moladMinutes; + /** the internal count of molad chalakim.*/ + private int moladChalakim; + + /** + * Returns the molad hours. Only a JewishDate object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or {@link #setMoladHours(int)} will have this field + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad hours + * @see #setMoladHours(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladHours() { + return moladHours; + } + + /** + * Sets the molad hours. + * + * @param moladHours + * the molad hours to set + * @see #getMoladHours() + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladHours(int moladHours) { + this.moladHours = moladHours; + } + + /** + * Returns the molad minutes. Only an object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladMinutes(int)} will have these fields + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad minutes + * @see #setMoladMinutes(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladMinutes() { + return moladMinutes; + } + + /** + * Sets the molad minutes. The expectation is that the traditional minute-less chalakim will be broken out to + * minutes and {@link #setMoladChalakim(int) chalakim / parts} , so 793 (TaShTZaG) parts would have the minutes set to + * 44 and chalakim to 1. + * + * @param moladMinutes + * the molad minutes to set + * @see #getMoladMinutes() + * @see #setMoladChalakim(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladMinutes(int moladMinutes) { + this.moladMinutes = moladMinutes; + } + + /** + * Sets the molad chalakim/parts. The expectation is that the traditional minute-less chalakim will be broken + * out to {@link #setMoladMinutes(int) minutes} and chalakim, so 793 (TaShTZaG) parts would have the minutes set to 44 and + * chalakim to 1. + * + * @param moladChalakim + * the molad chalakim / parts to set + * @see #getMoladChalakim() + * @see #setMoladMinutes(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + * + */ + public void setMoladChalakim(int moladChalakim) { + this.moladChalakim = moladChalakim; + } + + /** + * Returns the molad chalakim / parts. Only an object populated with {@link #getMolad()}, + * {@link #setJewishDate(int, int, int, int, int, int)} or or {@link #setMoladChalakim(int)} will have these fields + * populated. A regular JewishDate object will have this field set to 0. + * + * @return the molad chalakim / parts + * @see #setMoladChalakim(int) + * @see #getMolad() + * @see #setJewishDate(int, int, int, int, int, int) + */ + public int getMoladChalakim() { + return moladChalakim; + } + + /** + * Returns the last day in a gregorian month + * + * @param month + * the Gregorian month + * @return the last day of the Gregorian month + */ + int getLastDayOfGregorianMonth(int month) { + return getLastDayOfGregorianMonth(month, gregorianYear); + } + + /** + * Returns is the year passed in is a Gregorian leap year. + * @param year the Gregorian year + * @return if the year in question is a leap year. + */ + boolean isGregorianLeapYear(int year) { + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + } + + /** + * The month, where 1 == January, 2 == February, etc... Note that this is different than Java's Calendar class + * where January == 0. + */ + private int gregorianMonth; + + /** The day of the Gregorian month */ + private int gregorianDayOfMonth; + + /** The Gregorian year */ + private int gregorianYear; + + /** 1 == Sunday, 2 == Monday, etc... */ + private int dayOfWeek; + + /** Returns the absolute date (days since January 1, 0001 of the Gregorian calendar). + * @see #getAbsDate() + * @see #absDateToJewishDate() + */ + private int gregorianAbsDate; + + /** + * Returns the number of days in a given month in a given month and year. + * + * @param month + * the month. As with other cases in this class, this is 1-based, not zero-based. + * @param year + * the year (only impacts February) + * @return the number of days in the month in the given year + */ + private static int getLastDayOfGregorianMonth(int month, int year) { + switch (month) { + case 2: + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + return 29; + } else { + return 28; + } + case 4: + case 6: + case 9: + case 11: + return 30; + default: + return 31; + } + } + + /** + * Computes the Gregorian date from the absolute date. ND+ER + * @param absDate the absolute date + */ + private void absDateToDate(int absDate) { + int year = absDate / 366; // Search forward year by year from approximate year + while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { + year++; + } + + int month = 1; // Search forward month by month from January + while (absDate > gregorianDateToAbsDate(year, month, getLastDayOfGregorianMonth(month, year))) { + month++; + } + + int dayOfMonth = absDate - gregorianDateToAbsDate(year, month, 1) + 1; + setInternalGregorianDate(year, month, dayOfMonth); + } + + /** + * Returns the absolute date (days since January 1, 0001 of the Gregorian calendar). + * + * @return the number of days since January 1, 1 + */ + public int getAbsDate() { + return gregorianAbsDate; + } + + /** + * Computes the absolute date from a Gregorian date. ND+ER + * + * @param year + * the Gregorian year + * @param month + * the Gregorian month. Unlike the Java Calendar where January has the value of 0,This expects a 1 for + * January + * @param dayOfMonth + * the day of the month (1st, 2nd, etc...) + * @return the absolute Gregorian day + */ + private static int gregorianDateToAbsDate(int year, int month, int dayOfMonth) { + int absDate = dayOfMonth; + for (int m = month - 1; m > 0; m--) { + absDate += getLastDayOfGregorianMonth(m, year); // days in prior months of the year + } + return (absDate // days this year + + 365 * (year - 1) // days in previous years ignoring leap days + + (year - 1) / 4 // Julian leap days before this year + - (year - 1) / 100 // minus prior century years + + (year - 1) / 400); // plus prior years divisible by 400 + } + + /** + * Returns if the year is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year cycle are leap years. + * + * @param year + * the Jewish year. + * @return true if it is a leap year + * @see #isJewishLeapYear() + */ + private static boolean isJewishLeapYear(int year) { + return ((7 * year) + 1) % 19 < 7; + } + + /** + * Returns if the year the calendar is set to is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19-year + * cycle are leap years. + * + * @return true if it is a leap year + * @see #isJewishLeapYear(int) + */ + public boolean isJewishLeapYear() { + return isJewishLeapYear(getJewishYear()); + } + + /** + * Returns the last month of a given Jewish year. This will be 12 on a non {@link #isJewishLeapYear(int) leap year} + * or 13 on a leap year. + * + * @param year + * the Jewish year. + * @return 12 on a non leap year or 13 on a leap year + * @see #isJewishLeapYear(int) + */ + private static int getLastMonthOfJewishYear(int year) { + return isJewishLeapYear(year) ? ADAR_II : ADAR; + } + + /** + * Returns the number of days elapsed from the Sunday prior to the start of the Jewish calendar to the mean + * conjunction of Tishri of the Jewish year. + * + * @param year + * the Jewish year + * @return the number of days elapsed from prior to the molad Tohu BaHaRaD (Be = Monday, Ha = 5 + * hours and RaD = 204 chalakim / parts) prior to the start of the Jewish calendar, to + * the mean conjunction of Tishri of the Jewish year. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 + * chalakim after sunset on Sunday evening). + */ + public static int getJewishCalendarElapsedDays(int year) { + long chalakimSince = getChalakimSinceMoladTohu(year, TISHREI); + int moladDay = (int) (chalakimSince / (long) CHALAKIM_PER_DAY); + int moladParts = (int) (chalakimSince - moladDay * (long) CHALAKIM_PER_DAY); + // delay Rosh Hashana for the 4 dechiyos + return addDechiyos(year, moladDay, moladParts); + } + + /** + * Adds the 4 dechiyos for molad Tishrei. These are: + *
    + *
  1. Lo ADU Rosh - Rosh Hashana can't fall on a Sunday, Wednesday or Friday. If the molad fell on one + * of these days, Rosh Hashana is delayed to the following day.
  2. + *
  3. Molad Zaken - If the molad of Tishrei falls after 12 noon, Rosh Hashana is delayed to the following + * day. If the following day is ADU, it will be delayed an additional day.
  4. + *
  5. GaTRaD - If on a non leap year the molad of Tishrei falls on a Tuesday (Ga) on or after 9 hours + * (T) and (RaD 204 chalakim it is delayed till Thursday (one day delay, plus one day for + * Lo ADU Rosh)
  6. + *
  7. BeTuTaKPaT - if the year following a leap year falls on a Monday (Be) on or after 15 hours + * (Tu) and 589 chalakim (TaKPaT) it is delayed till Tuesday
  8. + *
+ * + * @param year the year + * @param moladDay the molad day + * @param moladParts the molad parts + * @return the number of elapsed days in the JewishCalendar adjusted for the 4 dechiyos. + */ + private static int addDechiyos(int year, int moladDay, int moladParts) { + int roshHashanaDay = moladDay; // if no dechiyos + // delay Rosh Hashana for the dechiyos of the Molad - new moon 1 - Molad Zaken, 2- GaTRaD 3- BeTuTaKPaT + if ((moladParts >= 19440) // Dechiya of Molad Zaken - molad is >= midday (18 hours * 1080 chalakim) + || (((moladDay % 7) == 2) // start Dechiya of GaTRaD - Ga = is a Tuesday + && (moladParts >= 9924) // TRaD = 9 hours, 204 parts or later (9 * 1080 + 204) + && !isJewishLeapYear(year)) // of a non-leap year - end Dechiya of GaTRaD + || (((moladDay % 7) == 1) // start Dechiya of BeTuTaKPaT - Be = is on a Monday + && (moladParts >= 16789) // TUTaKPaT part of BeTuTaKPaT = 15 hours, 589 parts or later (15 * 1080 + 589) + && (isJewishLeapYear(year - 1)))) { // in a year following a leap year - end Dechiya of BeTuTaKPaT + roshHashanaDay += 1; // Then postpone Rosh HaShanah one day + } + // start 4th Dechiya - Lo ADU Rosh - Rosh Hashana can't occur on A- sunday, D- Wednesday, U - Friday + if (((roshHashanaDay % 7) == 0)// If Rosh HaShanah would occur on Sunday, + || ((roshHashanaDay % 7) == 3) // or Wednesday, + || ((roshHashanaDay % 7) == 5)) { // or Friday - end 4th Dechiya - Lo ADU Rosh + roshHashanaDay = roshHashanaDay + 1; // Then postpone it one (more) day + } + return roshHashanaDay; + } + + /** + * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + * to the year and month passed in. + * + * @param year + * the Jewish year + * @param month + * the Jewish month the Jewish month, with the month numbers starting from Nissan. Use the JewishDate + * constants such as {@link JewishDate#TISHREI}. + * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + */ + private static long getChalakimSinceMoladTohu(int year, int month) { + // Jewish lunar month = 29 days, 12 hours and 793 chalakim + // chalakim since Molad Tohu BeHaRaD - 1 day, 5 hours and 204 chalakim + int monthOfYear = getJewishMonthOfYear(year, month); + int monthsElapsed = (235 * ((year - 1) / 19)) // Months in complete 19-year lunar (Metonic) cycles so far + + (12 * ((year - 1) % 19)) // Regular months in this cycle + + ((7 * ((year - 1) % 19) + 1) / 19) // Leap months this cycle + + (monthOfYear - 1); // add elapsed months till the start of the molad of the month + // return chalakim prior to BeHaRaD + number of chalakim since + return CHALAKIM_MOLAD_TOHU + (CHALAKIM_PER_MONTH * monthsElapsed); + } + + /** + * Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + * to the Jewish year and month that this Object is set to. + * + * @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu + */ + public long getChalakimSinceMoladTohu() { + return getChalakimSinceMoladTohu(jewishYear, jewishMonth); + } + + /** + * Converts the {@link JewishDate#NISSAN} based constants used by this class to numeric month starting from + * {@link JewishDate#TISHREI}. This is required for molad calculations. + * + * @param year + * The Jewish year + * @param month + * The Jewish Month + * @return the Jewish month of the year starting with Tishrei + */ + private static int getJewishMonthOfYear(int year, int month) { + boolean isLeapYear = isJewishLeapYear(year); + return (month + (isLeapYear ? 6 : 5)) % (isLeapYear ? 13 : 12) + 1; + } + + /** + * Validates the components of a Jewish date for validity. It will throw an {@link IllegalArgumentException} if the Jewish + * date is earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a {@link #isJewishLeapYear(int) + * leap year}), the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 or > 59 or + * chalakim < 0 or > 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the + * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the + * case of 793 / TaShTzaG). + * + * @param year + * the Jewish year to validate. It will reject any year <= 3761 (lower than the year 1 Gregorian). + * @param month + * the Jewish month to validate. It will reject a month < 1 or > 12 (or 13 on a leap year) . + * @param dayOfMonth + * the day of the Jewish month to validate. It will reject any value < 1 or > 30 TODO: check calling + * methods to see if there is any reason that the class can validate that 30 is invalid for some months. + * @param hours + * the hours (for molad calculations). It will reject an hour < 0 or > 23 + * @param minutes + * the minutes (for molad calculations). It will reject a minute < 0 or > 59 + * @param chalakim + * the chalakim / parts (for molad calculations). It will reject a chalakim < 0 or > + * 17. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim + * per minutes, so it would be 44 minutes and 1 chelek in the case of 793 / TaShTzaG) + * + * @throws IllegalArgumentException + * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a leap year), + * the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 or > 59 or chalakim + * < 0 or > 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the + * chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek + * in the case of 793 (TaShTzaG). + */ + private static void validateJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { + if (month < NISSAN || month > getLastMonthOfJewishYear(year)) { + throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " + + month + " is invalid for the year " + year + "."); + } + if (dayOfMonth < 1 || dayOfMonth > 30) { + throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth + + " is invalid."); + } + // reject dates prior to 18 Teves, 3761 (1/1/1 AD). This restriction can be relaxed if the date coding is + // changed/corrected + if ((year < 3761) || (year == 3761 && (month >= TISHREI && month < TEVES)) + || (year == 3761 && month == TEVES && dayOfMonth < 18)) { + throw new IllegalArgumentException( + "A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian) can't be set. " + year + ", " + month + + ", " + dayOfMonth + " is invalid."); + } + if (hours < 0 || hours > 23) { + throw new IllegalArgumentException("Hours < 0 or > 23 can't be set. " + hours + " is invalid."); + } + + if (minutes < 0 || minutes > 59) { + throw new IllegalArgumentException("Minutes < 0 or > 59 can't be set. " + minutes + " is invalid."); + } + + if (chalakim < 0 || chalakim > 17) { + throw new IllegalArgumentException( + "Chalakim/parts < 0 or > 17 can't be set. " + + chalakim + + " is invalid. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG)"); + } + } + + /** + * Validates the components of a Gregorian date for validity. It will throw an {@link IllegalArgumentException} if a + * year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in. + * + * @param year + * the Gregorian year to validate. It will reject any year < 1. + * @param month + * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a + * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. + * @param dayOfMonth + * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 + * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to + * see if there is any reason that the class needs days > the maximum. + * @throws IllegalArgumentException + * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in + * @see #validateGregorianYear(int) + * @see #validateGregorianMonth(int) + * @see #validateGregorianDayOfMonth(int) + */ + private static void validateGregorianDate(int year, int month, int dayOfMonth) { + validateGregorianMonth(month); + validateGregorianDayOfMonth(dayOfMonth); + validateGregorianYear(year); + } + + /** + * Validates a Gregorian month for validity. + * + * @param month + * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a + * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. + */ + private static void validateGregorianMonth(int month) { + if (month > 11 || month < 0) { + throw new IllegalArgumentException("The Gregorian month has to be between 0 - 11. " + month + + " is invalid."); + } + } + + /** + * Validates a Gregorian day of month for validity. + * + * @param dayOfMonth + * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 + * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to + * see if there is any reason that the class needs days > the maximum. + */ + private static void validateGregorianDayOfMonth(int dayOfMonth) { + if (dayOfMonth <= 0) { + throw new IllegalArgumentException("The day of month can't be less than 1. " + dayOfMonth + " is invalid."); + } + } + + /** + * Validates a Gregorian year for validity. + * + * @param year + * the Gregorian year to validate. It will reject any year < 1. + */ + private static void validateGregorianYear(int year) { + if (year < 1) { + throw new IllegalArgumentException("Years < 1 can't be calculated. " + year + " is invalid."); + } + } + + /** + * Returns the number of days for a given Jewish year. ND+ER + * + * @param year + * the Jewish year + * @return the number of days for a given Jewish year. + * @see #isCheshvanLong() + * @see #isKislevShort() + */ + public static int getDaysInJewishYear(int year) { + return getJewishCalendarElapsedDays(year + 1) - getJewishCalendarElapsedDays(year); + } + + /** + * Returns the number of days for the current year that the calendar is set to. + * + * @return the number of days for the Object's current Jewish year. + * @see #isCheshvanLong() + * @see #isKislevShort() + * @see #isJewishLeapYear() + */ + public int getDaysInJewishYear() { + return getDaysInJewishYear(getJewishYear()); + } + + /** + * Returns if Cheshvan is long in a given Jewish year. The method name isLong is done since in a Kesidran (ordered) + * year Cheshvan is short. ND+ER + * + * @param year + * the year + * @return true if Cheshvan is long in Jewish year. + * @see #isCheshvanLong() + * @see #getCheshvanKislevKviah() + */ + private static boolean isCheshvanLong(int year) { + return getDaysInJewishYear(year) % 10 == 5; + } + + /** + * Returns if Cheshvan is long (30 days VS 29 days) for the current year that the calendar is set to. The method + * name isLong is done since in a Kesidran (ordered) year Cheshvan is short. + * + * @return true if Cheshvan is long for the current year that the calendar is set to + * @see #isCheshvanLong() + */ + public boolean isCheshvanLong() { + return isCheshvanLong(getJewishYear()); + } + + /** + * Returns if Kislev is short (29 days VS 30 days) in a given Jewish year. The method name isShort is done since in + * a Kesidran (ordered) year Kislev is long. ND+ER + * + * @param year + * the Jewish year + * @return true if Kislev is short for the given Jewish year. + * @see #isKislevShort() + * @see #getCheshvanKislevKviah() + */ + private static boolean isKislevShort(int year) { + return getDaysInJewishYear(year) % 10 == 3; + } + + /** + * Returns if the Kislev is short for the year that this class is set to. The method name isShort is done since in a + * Kesidran (ordered) year Kislev is long. + * + * @return true if Kislev is short for the year that this class is set to + */ + public boolean isKislevShort() { + return isKislevShort(getJewishYear()); + } + + /** + * Returns the Cheshvan and Kislev kviah (whether a Jewish year is short, regular or long). It will return + * {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and Kislev + * is 30 days and {@link #CHASERIM} if both are 29 days. + * + * @return {@link #SHELAIMIM} if both cheshvan and kislev are 30 days, {@link #KESIDRAN} if Cheshvan is 29 days and + * Kislev is 30 days and {@link #CHASERIM} if both are 29 days. + * @see #isCheshvanLong() + * @see #isKislevShort() + */ + public int getCheshvanKislevKviah() { + if (isCheshvanLong() && !isKislevShort()) { + return SHELAIMIM; + } else if (!isCheshvanLong() && isKislevShort()) { + return CHASERIM; + } else { + return KESIDRAN; + } + } + + /** + * Returns the number of days of a Jewish month for a given month and year. + * + * @param month + * the Jewish month + * @param year + * the Jewish Year + * @return the number of days for a given Jewish month + */ + private static int getDaysInJewishMonth(int month, int year) { + if ((month == IYAR) || (month == TAMMUZ) || (month == ELUL) || ((month == CHESHVAN) && !(isCheshvanLong(year))) + || ((month == KISLEV) && isKislevShort(year)) || (month == TEVES) + || ((month == ADAR) && !(isJewishLeapYear(year))) || (month == ADAR_II)) { + return 29; + } else { + return 30; + } + } + + /** + * Returns the number of days of the Jewish month that the calendar is currently set to. + * + * @return the number of days for the Jewish month that the calendar is currently set to. + */ + public int getDaysInJewishMonth() { + return getDaysInJewishMonth(getJewishMonth(), getJewishYear()); + } + + /** + * Computes the Jewish date from the absolute date. + */ + private void absDateToJewishDate() { + // Approximation from below + jewishYear = (gregorianAbsDate - JEWISH_EPOCH) / 366; + // Search forward for year from the approximation + while (gregorianAbsDate >= jewishDateToAbsDate(jewishYear + 1, TISHREI, 1)) { + jewishYear++; + } + // Search forward for month from either Tishri or Nissan. + if (gregorianAbsDate < jewishDateToAbsDate(jewishYear, NISSAN, 1)) { + jewishMonth = TISHREI;// Start at Tishri + } else { + jewishMonth = NISSAN;// Start at Nissan + } + while (gregorianAbsDate > jewishDateToAbsDate(jewishYear, jewishMonth, getDaysInJewishMonth())) { + jewishMonth++; + } + // Calculate the day by subtraction + jewishDay = gregorianAbsDate - jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1; + } + + /** + * Returns the absolute date of Jewish date. ND+ER + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. Nissan expects a value of 1 etc. until Adar with a value of 12. + * For a leap year, 13 will be the expected value for Adar II. Use the constants {@link JewishDate#NISSAN} + * etc. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * @return the absolute date of the Jewish date. + */ + private static int jewishDateToAbsDate(int year, int month, int dayOfMonth) { + int elapsed = getDaysSinceStartOfJewishYear(year, month, dayOfMonth); + // add elapsed days this year + Days in prior years + Days elapsed before absolute year 1 + return elapsed + getJewishCalendarElapsedDays(year) + JEWISH_EPOCH; + } + + /** + * Returns the molad for a given year and month. Returns a JewishDate {@link Object} set to the date of the molad + * with the {@link #getMoladHours() hours}, {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() + * chalakim} set. In the current implementation, it sets the molad time based on a midnight date rollover. This + * means that Rosh Chodesh Adar II, 5771 with a molad of 7 chalakim past midnight on Shabbos 29 Adar I / March 5, + * 2011 12:00 AM and 7 chalakim, will have the following values: hours: 0, minutes: 0, Chalakim: 7. + * + * @return a JewishDate {@link Object} set to the date of the molad with the {@link #getMoladHours() hours}, + * {@link #getMoladMinutes() minutes} and {@link #getMoladChalakim() chalakim} set. + */ + public JewishDate getMolad() { + JewishDate moladDate = new JewishDate(getChalakimSinceMoladTohu()); + if (moladDate.getMoladHours() >= 6) { + moladDate.forward(Calendar.DATE, 1); + } + moladDate.setMoladHours((moladDate.getMoladHours() + 18) % 24); + return moladDate; + } + + /** + * Returns the number of days from the Jewish epoch from the number of chalakim from the epoch passed in. + * + * @param chalakim + * the number of chalakim since the beginning of Sunday prior to BaHaRaD + * @return the number of days from the Jewish epoch + */ + private static int moladToAbsDate(long chalakim) { + return (int) (chalakim / CHALAKIM_PER_DAY) + JEWISH_EPOCH; + } + + /** + * Constructor that creates a JewishDate based on a molad passed in. The molad would be the number of + * chalakim / parts starting at the beginning of Sunday prior to the Molad Tohu BeHaRaD (Be = + * Monday, Ha = 5 hours and RaD = 204 chalakim / parts) - prior to the start of the Jewish + * calendar. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 chalakim after sunset on Sunday evening). + * + * @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD + */ + public JewishDate(long molad) { + absDateToDate(moladToAbsDate(molad)); + int conjunctionDay = (int) (molad / (long) CHALAKIM_PER_DAY); + int conjunctionParts = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); + setMoladTime(conjunctionParts); + } + + /** + * Sets the molad time (hours minutes and chalakim) based on the number of chalakim since the start of the day. + * + * @param chalakim + * the number of chalakim since the start of the day. + */ + private void setMoladTime(int chalakim) { + int adjustedChalakim = chalakim; + setMoladHours(adjustedChalakim / CHALAKIM_PER_HOUR); + adjustedChalakim = adjustedChalakim - (getMoladHours() * CHALAKIM_PER_HOUR); + setMoladMinutes(adjustedChalakim / CHALAKIM_PER_MINUTE); + setMoladChalakim(adjustedChalakim - moladMinutes * CHALAKIM_PER_MINUTE); + } + + /** + * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. + * + * @param year + * the Jewish year + * @param month + * the Jewish month + * @param dayOfMonth + * the day in the Jewish month + * @return the number of days + */ + private static int getDaysSinceStartOfJewishYear(int year, int month, int dayOfMonth) { + int elapsedDays = dayOfMonth; + // Before Tishrei (from Nissan to Tishrei), add days in prior months + if (month < TISHREI) { + // this year before and after Nissan. + for (int m = TISHREI; m <= getLastMonthOfJewishYear(year); m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + for (int m = NISSAN; m < month; m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + } else { // Add days in prior months this year + for (int m = TISHREI; m < month; m++) { + elapsedDays += getDaysInJewishMonth(m, year); + } + } + return elapsedDays; + } + + /** + * returns the number of days from Rosh Hashana of the date passed in, to the full date passed in. + * + * @return the number of days + */ + public int getDaysSinceStartOfJewishYear() { + return getDaysSinceStartOfJewishYear(getJewishYear(), getJewishMonth(), getJewishDayOfMonth()); + } + + /** + * Creates a Jewish date based on a Jewish year, month and day of month. + * + * @param jewishYear + * the Jewish year + * @param jewishMonth + * the Jewish month. The method expects a 1 for Nissan ... 12 for Adar and 13 for Adar II. Use the + * constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar II) to avoid any + * confusion. + * @param jewishDayOfMonth + * the Jewish day of month. If 30 is passed in for a month with only 29 days (for example {@link #IYAR}, + * or {@link #KISLEV} in a year that {@link #isKislevShort()}), the 29th (last valid date of the month) + * will be set + * @throws IllegalArgumentException + * if the day of month is < 1 or > 30, or a year of < 0 is passed in. + */ + public JewishDate(int jewishYear, int jewishMonth, int jewishDayOfMonth) { + setJewishDate(jewishYear, jewishMonth, jewishDayOfMonth); + } + + /** + * Default constructor will set a default date to the current system date. + */ + public JewishDate() { + resetDate(); + } + + /** + * A constructor that initializes the date to the {@link java.util.Calendar Calendar} parameter. + * + * @param zonedDateTime + * the ZonedDateTime to set the calendar to + * @throws IllegalArgumentException + * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} + */ + public JewishDate(ZonedDateTime zonedDateTime) { + setGregorianDate(zonedDateTime); + } + + /** + * A constructor that initializes the date to the {@link java.time.LocalDate LocalDate} parameter. + * + * @param localDate + * the LocalDate to set the calendar to + * @throws IllegalArgumentException + * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} + */ + public JewishDate(LocalDate localDate) { + setGregorianDate(localDate); + } + + /** + * Sets the date based on a {@link java.util.Calendar Calendar} object. Modifies the Jewish date as well. + * + * @param zonedDateTime + * the ZonedDateTime to set the calendar to + * @throws IllegalArgumentException + * if the {@link Calendar#ERA} is {@link GregorianCalendar#BC} + */ + public void setGregorianDate(ZonedDateTime zonedDateTime) { + setGregorianDate(zonedDateTime.toLocalDate()); + } + + /** + * Sets the date based on a {@link java.time.LocalDate LocalDate} object. Modifies the Jewish date as well. + * + * @param localDate + * the LocalDate to set the calendar to + * @throws IllegalArgumentException + * if the date would fall prior to the year 1 AD + */ + public void setGregorianDate(LocalDate localDate) { + if (localDate.getYear() <= 0) { + throw new IllegalArgumentException( + "Calendars with a BC era are not supported. The year " + + localDate.getYear() + " BC is invalid." + ); + } + + gregorianYear = localDate.getYear(); + gregorianMonth = localDate.getMonth().getValue(); // FIXME + 1;// 1 = January + gregorianDayOfMonth = localDate.getDayOfMonth(); + + // initialize absolute date + gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); + + // convert to Jewish date + absDateToJewishDate(); + + // day of week (same calculation as original) + dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; + } + + /** + * Sets the Gregorian Date, and updates the Jewish date accordingly. Like the Java Calendar A value of 0 is expected + * for January. + * + * @param year + * the Gregorian year + * @param month + * the Gregorian month. Like the Java Calendar, this class expects 0 for January + * @param dayOfMonth + * the Gregorian day of month. If this is > the number of days in the month/year, the last valid date of + * the month will be set + * @throws IllegalArgumentException + * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in + */ + public void setGregorianDate(int year, int month, int dayOfMonth) { + validateGregorianDate(year, month, dayOfMonth); + setInternalGregorianDate(year, month + 1, dayOfMonth); + } + + /** + * Sets the hidden internal representation of the Gregorian date , and updates the Jewish date accordingly. While + * public getters and setters have 0 based months matching the Java Calendar classes, This class internally + * represents the Gregorian month starting at 1. When this is called it will not adjust the month to match the Java + * Calendar classes. + * + * @param year the year + * @param month the month + * @param dayOfMonth the day of month + */ + private void setInternalGregorianDate(int year, int month, int dayOfMonth) { + // make sure date is a valid date for the given month, if not, set to last day of month + if (dayOfMonth > getLastDayOfGregorianMonth(month, year)) { + dayOfMonth = getLastDayOfGregorianMonth(month, year); + } + // init month, date, year + gregorianMonth = month; + gregorianDayOfMonth = dayOfMonth; + gregorianYear = year; + + gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); // init date + absDateToJewishDate(); + + dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // set day of week + } + + /** + * Sets the Jewish Date and updates the Gregorian date accordingly. + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for + * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar + * II) to avoid any confusion. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * @throws IllegalArgumentException + * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a + * leap year) or the day of month is < 1 or > 30 is passed in + */ + public void setJewishDate(int year, int month, int dayOfMonth) { + setJewishDate(year, month, dayOfMonth, 0, 0, 0); + } + + /** + * Sets the Jewish Date and updates the Gregorian date accordingly. + * + * @param year + * the Jewish year. The year can't be negative + * @param month + * the Jewish month starting with Nissan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for + * Adar II. Use the constants {@link #NISSAN} ... {@link #ADAR} (or {@link #ADAR_II} for a leap year Adar + * II) to avoid any confusion. + * @param dayOfMonth + * the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only + * has 29 days, the day will be set as 29. + * + * @param hours + * the hour of the day. Used for molad calculations + * @param minutes + * the minutes. Used for molad calculations + * @param chalakim + * the chalakim / parts. Used for molad calculations. The chalakim should not + * exceed 17. Minutes should be used for larger numbers. + * + * @throws IllegalArgumentException + * if a Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a leap year), the day + * of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > 17. For + * larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes (18 + * chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG). + */ + public void setJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { + validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); + + // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 + // days to a month that only has 29) set the date to 29th + if (dayOfMonth > getDaysInJewishMonth(month, year)) { + dayOfMonth = getDaysInJewishMonth(month, year); + } + + jewishMonth = month; + jewishDay = dayOfMonth; + jewishYear = year; + moladHours = hours; + moladMinutes = minutes; + moladChalakim = chalakim; + + gregorianAbsDate = jewishDateToAbsDate(jewishYear, jewishMonth, jewishDay); // reset Gregorian date + absDateToDate(gregorianAbsDate); + + dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // reset day of week + } + + /** + * Returns this object's date as a {@link java.util.Calendar} object. + * + * @return The {@link java.util.Calendar} + */ + public Calendar getGregorianCalendar() { + Calendar calendar = Calendar.getInstance(); + calendar.set(getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth()); + return calendar; + } + + /** + * Returns this object's date as a {@link java.time.LocalDate} object. + * + * @return The {@link java.time.LocalDate} + */ + public LocalDate getLocalDate() { + return LocalDate.of(getGregorianYear(), getGregorianMonth() + 1, getGregorianDayOfMonth()); + } + + /** + * Resets this date to the current system date. + */ + public void resetDate() { + LocalDate localDate = LocalDate.now(); + setGregorianDate(localDate); + } + + /** + * Returns a string containing the Jewish date in the form, "day Month, year" e.g. "21 Shevat, 5729". For more + * complex formatting, use the formatter classes. + * + * @return the Jewish date in the form "day Month, year" e.g. "21 Shevat, 5729" + * @see HebrewDateFormatter#format(JewishDate) + */ + public String toString() { + return new HebrewDateFormatter().format(this); + } + + /** + * Rolls the date, month or year forward by the amount passed in. It modifies both the Gregorian and Jewish dates accordingly. + * If manipulation beyond the fields supported here is required, use the {@link Calendar} class {@link Calendar#add(int, int)} + * or {@link Calendar#roll(int, int)} methods in the following manner. + * + *
+	 * 
+	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
+	 * 	cal.add(Calendar.MONTH, 3); // add 3 Gregorian months
+	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
+	 * 
+	 * 
+ * + * @param field the calendar field to be forwarded. The must be {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} + * @param amount the positive amount to move forward + * @throws IllegalArgumentException if the field is anything besides {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} + * or if the amount is less than 1 + * + * @see #back() + * @see Calendar#add(int, int) + * @see Calendar#roll(int, int) + */ + public void forward(int field, int amount) { //FIXME first param should be converted from the Calendar.DATE + if (field != Calendar.DATE && field != Calendar.MONTH && field != Calendar.YEAR) { + throw new IllegalArgumentException("Unsupported field was passed to Forward. Only Calendar.DATE, Calendar.MONTH or Calendar.YEAR are supported."); + } + if (amount < 1) { + throw new IllegalArgumentException("JewishDate.forward() does not support amounts less than 1. See JewishDate.back()"); + } + if (field == Calendar.DATE) { + // Change Gregorian date + for (int i = 0; i < amount; i++) { + if (gregorianDayOfMonth == getLastDayOfGregorianMonth(gregorianMonth, gregorianYear)) { + gregorianDayOfMonth = 1; + // if last day of year + if (gregorianMonth == 12) { + gregorianYear++; + gregorianMonth = 1; + } else { + gregorianMonth++; + } + } else { // if not last day of month + gregorianDayOfMonth++; + } + + // Change the Jewish Date + if (jewishDay == getDaysInJewishMonth()) { + // if it last day of elul (i.e. last day of Jewish year) + if (jewishMonth == ELUL) { + jewishYear++; + jewishMonth++; + jewishDay = 1; + } else if (jewishMonth == getLastMonthOfJewishYear(jewishYear)) { + // if it is the last day of Adar, or Adar II as case may be + jewishMonth = NISSAN; + jewishDay = 1; + } else { + jewishMonth++; + jewishDay = 1; + } + } else { // if not last date of month + jewishDay++; + } + + if (dayOfWeek == 7) { // if last day of week, loop back to Sunday + dayOfWeek = 1; + } else { + dayOfWeek++; + } + + gregorianAbsDate++; // increment the absolute date + } + } else if (field == Calendar.MONTH) { + forwardJewishMonth(amount); + } else { + setJewishYear(getJewishYear() + amount); + } + } + + /** + * Forward the Jewish date by the number of months passed in. + * FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt with similar to + * the way that the Java Calendar behaves (not that simple since there is a difference between add() or roll(). + * + * @throws IllegalArgumentException if the amount is less than 1 + * @param amount the number of months to roll the month forward + */ + private void forwardJewishMonth(int amount) { + if (amount < 1) { + throw new IllegalArgumentException("the amount of months to forward has to be greater than zero."); + } + for (int i = 0; i < amount; i++) { + if (getJewishMonth() == ELUL) { + setJewishMonth(TISHREI); + setJewishYear(getJewishYear() + 1); + } else if ((! isJewishLeapYear() && getJewishMonth() == ADAR) + || (isJewishLeapYear() && getJewishMonth() == ADAR_II)){ + setJewishMonth(NISSAN); + } else { + setJewishMonth(getJewishMonth() + 1); + } + } + } + + /** + * Rolls the date back by 1 day. It modifies both the Gregorian and Jewish dates accordingly. The API does not + * currently offer the ability to forward more than one day at a time, or to forward by month or year. If such + * manipulation is required use the {@link Calendar} class {@link Calendar#add(int, int)} or + * {@link Calendar#roll(int, int)} methods in the following manner. + * + *
+	 * 
+	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
+	 * 	cal.add(Calendar.MONTH, -3); // subtract 3 Gregorian months
+	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
+	 * 
+	 * 
+ * + * @see #back() + * @see Calendar#add(int, int) + * @see Calendar#roll(int, int) + */ + public void back() { + // Change Gregorian date + if (gregorianDayOfMonth == 1) { // if first day of month + if (gregorianMonth == 1) { // if first day of year + gregorianMonth = 12; + gregorianYear--; + } else { + gregorianMonth--; + } + // change to last day of previous month + gregorianDayOfMonth = getLastDayOfGregorianMonth(gregorianMonth, gregorianYear); + } else { + gregorianDayOfMonth--; + } + // change Jewish date + if (jewishDay == 1) { // if first day of the Jewish month + if (jewishMonth == NISSAN) { + jewishMonth = getLastMonthOfJewishYear(jewishYear); + } else if (jewishMonth == TISHREI) { // if Rosh Hashana + jewishYear--; + jewishMonth--; + } else { + jewishMonth--; + } + jewishDay = getDaysInJewishMonth(); + } else { + jewishDay--; + } + + if (dayOfWeek == 1) { // if first day of week, loop back to Saturday + dayOfWeek = 7; + } else { + dayOfWeek--; + } + gregorianAbsDate--; // change the absolute date + } + + /** + * Indicates whether some other object is "equal to" this one. + * @see Object#equals(Object) + */ + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof JewishDate)) { + return false; + } + JewishDate jewishDate = (JewishDate) object; + return gregorianAbsDate == jewishDate.getAbsDate(); + } + + /** + * Compares two dates as per the compareTo() method in the Comparable interface. Returns a value less than 0 if this + * date is "less than" (before) the date, greater than 0 if this date is "greater than" (after) the date, or 0 if + * they are equal. + */ + public int compareTo(JewishDate jewishDate) { + return Integer.compare(gregorianAbsDate, jewishDate.getAbsDate()); + } + + /** + * Returns the Gregorian month (between 0-11). + * + * @return the Gregorian month (between 0-11). Like the java.util.Calendar, months are 0 based. + */ + public int getGregorianMonth() { + return gregorianMonth - 1; //FIXME + } + + /** + * Returns the Gregorian day of the month. + * + * @return the Gregorian day of the mont + */ + public int getGregorianDayOfMonth() { + return gregorianDayOfMonth; + } + + /** + * Returns the Gregorian year. + * + * @return the Gregorian year + */ + public int getGregorianYear() { + return gregorianYear; + } + + /** + * Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nissan and goes to + * 13 for Adar II + * + * @return the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan and + * goes to 13 for Adar II + */ + public int getJewishMonth() { + return jewishMonth; + } + + /** + * Returns the Jewish day of month. + * + * @return the Jewish day of the month + */ + public int getJewishDayOfMonth() { + return jewishDay; + } + + /** + * Returns the Jewish year. + * + * @return the Jewish year + */ + public int getJewishYear() { + return jewishYear; + } + + /** + * Returns the day of the week as a number between 1-7. + * + * @return the day of the week as a number between 1-7. + */ + public int getDayOfWeek() { + return dayOfWeek; + } + + /** + * Sets the Gregorian month. + * + * @param month + * the Gregorian month + * + * @throws IllegalArgumentException + * if a month < 0 or > 11 is passed in + */ + public void setGregorianMonth(int month) { + validateGregorianMonth(month); + setInternalGregorianDate(gregorianYear, month + 1, gregorianDayOfMonth); //FIXME + } + + /** + * sets the Gregorian year. + * + * @param year + * the Gregorian year. + * @throws IllegalArgumentException + * if a year of < 1 is passed in + */ + public void setGregorianYear(int year) { + validateGregorianYear(year); + setInternalGregorianDate(year, gregorianMonth, gregorianDayOfMonth); + } + + /** + * sets the Gregorian Day of month. + * + * @param dayOfMonth + * the Gregorian Day of month. + * @throws IllegalArgumentException + * if the day of month of < 1 is passed in + */ + public void setGregorianDayOfMonth(int dayOfMonth) { + validateGregorianDayOfMonth(dayOfMonth); + setInternalGregorianDate(gregorianYear, gregorianMonth, dayOfMonth); + } + + /** + * sets the Jewish month. + * + * @param month + * the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan + * and goes to 13 for Adar II + * @throws IllegalArgumentException + * if a month < 1 or > 12 (or 13 on a leap year) is passed in + */ + public void setJewishMonth(int month) { + setJewishDate(jewishYear, month, jewishDay); + } + + /** + * sets the Jewish year. + * + * @param year + * the Jewish year + * @throws IllegalArgumentException + * if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day + * previously set are < 18 Teves (prior to Jan 1, 1 AD) + */ + public void setJewishYear(int year) { + setJewishDate(year, jewishMonth, jewishDay); + } + + /** + * sets the Jewish day of month. + * + * @param dayOfMonth + * the Jewish day of month + * @throws IllegalArgumentException + * if the day of month is < 1 or > 30 is passed in + */ + public void setJewishDayOfMonth(int dayOfMonth) { + setJewishDate(jewishYear, jewishMonth, dayOfMonth); + } + + /** + * A method that creates a deep copy of the object. + * + * @see Object#clone() + */ + public Object clone() { + JewishDate clone = null; + try { + clone = (JewishDate) super.clone(); + } catch (CloneNotSupportedException cnse) { + // Required by the compiler. Should never be reached since we implement clone() + } + if (clone != null) { + clone.setInternalGregorianDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); + } + return clone; + } + + /** + * Overrides {@link Object#hashCode()}. + * @see Object#hashCode() + */ + public int hashCode() { + int result = 17; + result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash + result += 37 * result + gregorianAbsDate; + return result; + } } From 3b79810230da6499fe57c755da2a7003bf248f99 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 18 Mar 2026 20:57:28 -0400 Subject: [PATCH 8/9] implement jewishdate --- .../com/kosherjava/zmanim/ZmanimCalendar.java | 3 +- .../zmanim/hebrewcalendar/JewishCalendar.java | 20 +- .../zmanim/hebrewcalendar/JewishDate.java | 587 +++--------------- 3 files changed, 101 insertions(+), 509 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java index 8cfd7274..2bb1f04a 100644 --- a/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ZmanimCalendar.java @@ -1086,8 +1086,7 @@ public void setCandleLightingOffset(double candleLightingOffset) { */ public boolean isAssurBemlacha(Instant currentTime, Instant tzais, boolean inIsrael) { JewishCalendar jewishCalendar = new JewishCalendar(); - jewishCalendar.setGregorianDate(getLocalDate().getYear(), getLocalDate().getMonthValue(), - getLocalDate().getDayOfMonth()); + jewishCalendar.setGregorianDate(getLocalDate()); jewishCalendar.setInIsrael(inIsrael); diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java index c2bd6960..6fef63cc 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java @@ -20,6 +20,7 @@ import java.time.Duration; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Calendar; @@ -506,12 +507,12 @@ public Parsha getUpcomingParshah() { JewishCalendar clone = (JewishCalendar) clone(); int daysToShabbos = (Calendar.SATURDAY - getDayOfWeek() + 7) % 7; if (getDayOfWeek() != Calendar.SATURDAY) { - clone.forward(Calendar.DATE, daysToShabbos); + clone.addDays(daysToShabbos); } else { - clone.forward(Calendar.DATE, 7); + clone.addDays( 7); } while(clone.getParshah() == Parsha.NONE) { //Yom Kippur / Sukkos or Pesach with 2 potential non-parsha Shabbosim in a row - clone.forward(Calendar.DATE, 7); + clone.addDays(7); } return clone.getParshah(); } @@ -1233,16 +1234,9 @@ public Instant getMoladAsInstant() { int seconds = (int) moladSeconds; int nanos = (int) ((moladSeconds - seconds) * 1_000_000_000); // convert remainder to nanos - ZonedDateTime moladZdt = ZonedDateTime.of( - molad.getGregorianYear(), - molad.getGregorianMonth() + 1, // 1-based FIXME - molad.getGregorianDayOfMonth(), - molad.getMoladHours(), - molad.getMoladMinutes(), - seconds, - nanos, - jerusalemStandardOffset - ); + LocalTime time = LocalTime.of(molad.getMoladHours(),molad.getMoladMinutes(),seconds,nanos); + + ZonedDateTime moladZdt = ZonedDateTime.of(molad.getLocalDate(),time,jerusalemStandardOffset); // Har Habayis at a longitude of 35.2354 offset vs longitude 35 in standard time, so we subtract the time difference // of 20.94 minutes (20 minutes and 56 seconds and 496 millis) to get to Standard time from local mean time diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java index db74d09d..1a3d30a9 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java @@ -17,9 +17,8 @@ package com.kosherjava.zmanim.hebrewcalendar; import java.time.LocalDate; +import java.time.YearMonth; import java.time.ZonedDateTime; -import java.util.Calendar; -import java.util.GregorianCalendar; /** * The JewishDate is the base calendar class, that supports maintenance of a {@link java.util.GregorianCalendar} @@ -290,51 +289,18 @@ public int getMoladChalakim() { return moladChalakim; } - /** - * Returns the last day in a gregorian month - * - * @param month - * the Gregorian month - * @return the last day of the Gregorian month - */ - int getLastDayOfGregorianMonth(int month) { - return getLastDayOfGregorianMonth(month, gregorianYear); - } - - /** - * Returns is the year passed in is a Gregorian leap year. - * @param year the Gregorian year - * @return if the year in question is a leap year. - */ - boolean isGregorianLeapYear(int year) { - return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); - } - - /** - * The month, where 1 == January, 2 == February, etc... Note that this is different than Java's Calendar class - * where January == 0. - */ - private int gregorianMonth; - - /** The day of the Gregorian month */ - private int gregorianDayOfMonth; - - /** The Gregorian year */ - private int gregorianYear; - /** 1 == Sunday, 2 == Monday, etc... */ private int dayOfWeek; /** Returns the absolute date (days since January 1, 0001 of the Gregorian calendar). * @see #getAbsDate() - * @see #absDateToJewishDate() + * @see #setJewishDateFromAbsDate() */ private int gregorianAbsDate; /** * Returns the number of days in a given month in a given month and year. - * + * * @param month * the month. As with other cases in this class, this is 1-based, not zero-based. * @param year @@ -342,28 +308,14 @@ boolean isGregorianLeapYear(int year) { * @return the number of days in the month in the given year */ private static int getLastDayOfGregorianMonth(int month, int year) { - switch (month) { - case 2: - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - return 29; - } else { - return 28; - } - case 4: - case 6: - case 9: - case 11: - return 30; - default: - return 31; - } + return YearMonth.of(year, month).lengthOfMonth(); } /** * Computes the Gregorian date from the absolute date. ND+ER * @param absDate the absolute date */ - private void absDateToDate(int absDate) { + private static LocalDate absDateToDate(int absDate) { int year = absDate / 366; // Search forward year by year from approximate year while (absDate >= gregorianDateToAbsDate(year + 1, 1, 1)) { year++; @@ -375,7 +327,7 @@ private void absDateToDate(int absDate) { } int dayOfMonth = absDate - gregorianDateToAbsDate(year, month, 1) + 1; - setInternalGregorianDate(year, month, dayOfMonth); + return LocalDate.of(year, month, dayOfMonth); } /** @@ -590,9 +542,11 @@ private static void validateJewishDate(int year, int month, int dayOfMonth, int throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " + month + " is invalid for the year " + year + "."); } - if (dayOfMonth < 1 || dayOfMonth > 30) { - throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth - + " is invalid."); + int monthLength = getDaysInJewishMonth(month, year); + if (dayOfMonth < 1 || dayOfMonth > monthLength) { + throw new IllegalArgumentException( + "The Jewish day of month can't be < 1 or > " + monthLength + + " for the month index " + monthLength + ". " + dayOfMonth + " is invalid."); } // reject dates prior to 18 Teves, 3761 (1/1/1 AD). This restriction can be relaxed if the date coding is // changed/corrected @@ -618,71 +572,6 @@ private static void validateJewishDate(int year, int month, int dayOfMonth, int } } - /** - * Validates the components of a Gregorian date for validity. It will throw an {@link IllegalArgumentException} if a - * year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in. - * - * @param year - * the Gregorian year to validate. It will reject any year < 1. - * @param month - * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a - * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. - * @param dayOfMonth - * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 - * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to - * see if there is any reason that the class needs days > the maximum. - * @throws IllegalArgumentException - * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in - * @see #validateGregorianYear(int) - * @see #validateGregorianMonth(int) - * @see #validateGregorianDayOfMonth(int) - */ - private static void validateGregorianDate(int year, int month, int dayOfMonth) { - validateGregorianMonth(month); - validateGregorianDayOfMonth(dayOfMonth); - validateGregorianYear(year); - } - - /** - * Validates a Gregorian month for validity. - * - * @param month - * the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a - * {@link GregorianCalendar}, where {@link Calendar#JANUARY} has a value of 0. - */ - private static void validateGregorianMonth(int month) { - if (month > 11 || month < 0) { - throw new IllegalArgumentException("The Gregorian month has to be between 0 - 11. " + month - + " is invalid."); - } - } - - /** - * Validates a Gregorian day of month for validity. - * - * @param dayOfMonth - * the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 - * since calling methods will simply set it to the maximum for that month. TODO: check calling methods to - * see if there is any reason that the class needs days > the maximum. - */ - private static void validateGregorianDayOfMonth(int dayOfMonth) { - if (dayOfMonth <= 0) { - throw new IllegalArgumentException("The day of month can't be less than 1. " + dayOfMonth + " is invalid."); - } - } - - /** - * Validates a Gregorian year for validity. - * - * @param year - * the Gregorian year to validate. It will reject any year < 1. - */ - private static void validateGregorianYear(int year) { - if (year < 1) { - throw new IllegalArgumentException("Years < 1 can't be calculated. " + year + " is invalid."); - } - } - /** * Returns the number of days for a given Jewish year. ND+ER * @@ -806,9 +695,13 @@ public int getDaysInJewishMonth() { } /** - * Computes the Jewish date from the absolute date. + * Computes and sets the Jewish date fields based on the provided absolute (Gregorian) date. */ - private void absDateToJewishDate() { + private 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; // Search forward for year from the approximation @@ -826,6 +719,14 @@ private void absDateToJewishDate() { } // 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; + + // Set the molad fields to 0 + moladHours = 0; + moladMinutes = 0; + moladChalakim = 0; } /** @@ -861,7 +762,7 @@ private static int jewishDateToAbsDate(int year, int month, int dayOfMonth) { public JewishDate getMolad() { JewishDate moladDate = new JewishDate(getChalakimSinceMoladTohu()); if (moladDate.getMoladHours() >= 6) { - moladDate.forward(Calendar.DATE, 1); + moladDate.addDays(1); } moladDate.setMoladHours((moladDate.getMoladHours() + 18) % 24); return moladDate; @@ -887,24 +788,13 @@ private static int moladToAbsDate(long chalakim) { * @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD */ public JewishDate(long molad) { - absDateToDate(moladToAbsDate(molad)); + setAbsDate(moladToAbsDate(molad)); int conjunctionDay = (int) (molad / (long) CHALAKIM_PER_DAY); - int conjunctionParts = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); - setMoladTime(conjunctionParts); - } - - /** - * Sets the molad time (hours minutes and chalakim) based on the number of chalakim since the start of the day. - * - * @param chalakim - * the number of chalakim since the start of the day. - */ - private void setMoladTime(int chalakim) { - int adjustedChalakim = chalakim; - setMoladHours(adjustedChalakim / CHALAKIM_PER_HOUR); - adjustedChalakim = adjustedChalakim - (getMoladHours() * CHALAKIM_PER_HOUR); - setMoladMinutes(adjustedChalakim / CHALAKIM_PER_MINUTE); - setMoladChalakim(adjustedChalakim - moladMinutes * CHALAKIM_PER_MINUTE); + int chalakim = (int) (molad - conjunctionDay * (long) CHALAKIM_PER_DAY); + setMoladHours(chalakim / CHALAKIM_PER_HOUR); + chalakim = chalakim - (getMoladHours() * CHALAKIM_PER_HOUR); + setMoladMinutes(chalakim / CHALAKIM_PER_MINUTE); + setMoladChalakim(chalakim - moladMinutes * CHALAKIM_PER_MINUTE); } /** @@ -1018,70 +908,10 @@ public void setGregorianDate(ZonedDateTime zonedDateTime) { * if the date would fall prior to the year 1 AD */ public void setGregorianDate(LocalDate localDate) { - if (localDate.getYear() <= 0) { - throw new IllegalArgumentException( - "Calendars with a BC era are not supported. The year " - + localDate.getYear() + " BC is invalid." - ); - } - - gregorianYear = localDate.getYear(); - gregorianMonth = localDate.getMonth().getValue(); // FIXME + 1;// 1 = January - gregorianDayOfMonth = localDate.getDayOfMonth(); - - // initialize absolute date - gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); + int absDate = gregorianDateToAbsDate(localDate.getYear(), localDate.getMonth().getValue(), localDate.getDayOfMonth()); // FIXME + 1;// 1 = January // convert to Jewish date - absDateToJewishDate(); - - // day of week (same calculation as original) - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; - } - - /** - * Sets the Gregorian Date, and updates the Jewish date accordingly. Like the Java Calendar A value of 0 is expected - * for January. - * - * @param year - * the Gregorian year - * @param month - * the Gregorian month. Like the Java Calendar, this class expects 0 for January - * @param dayOfMonth - * the Gregorian day of month. If this is > the number of days in the month/year, the last valid date of - * the month will be set - * @throws IllegalArgumentException - * if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in - */ - public void setGregorianDate(int year, int month, int dayOfMonth) { - validateGregorianDate(year, month, dayOfMonth); - setInternalGregorianDate(year, month + 1, dayOfMonth); - } - - /** - * Sets the hidden internal representation of the Gregorian date , and updates the Jewish date accordingly. While - * public getters and setters have 0 based months matching the Java Calendar classes, This class internally - * represents the Gregorian month starting at 1. When this is called it will not adjust the month to match the Java - * Calendar classes. - * - * @param year the year - * @param month the month - * @param dayOfMonth the day of month - */ - private void setInternalGregorianDate(int year, int month, int dayOfMonth) { - // make sure date is a valid date for the given month, if not, set to last day of month - if (dayOfMonth > getLastDayOfGregorianMonth(month, year)) { - dayOfMonth = getLastDayOfGregorianMonth(month, year); - } - // init month, date, year - gregorianMonth = month; - gregorianDayOfMonth = dayOfMonth; - gregorianYear = year; - - gregorianAbsDate = gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); // init date - absDateToJewishDate(); - - dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // set day of week + setAbsDate(absDate); } /** @@ -1132,13 +962,14 @@ public void setJewishDate(int year, int month, int dayOfMonth) { * chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG). */ public void setJewishDate(int year, int month, int dayOfMonth, int hours, int minutes, int chalakim) { - validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); - // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 - // days to a month that only has 29) set the date to 29th - if (dayOfMonth > getDaysInJewishMonth(month, year)) { - dayOfMonth = getDaysInJewishMonth(month, year); - } + // if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 + // days to a month that only has 29) set the date to 29th + if (dayOfMonth > getDaysInJewishMonth(month, year)) { + dayOfMonth = getDaysInJewishMonth(month, year); + } + + validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim); jewishMonth = month; jewishDay = dayOfMonth; @@ -1148,21 +979,10 @@ public void setJewishDate(int year, int month, int dayOfMonth, int hours, int mi moladChalakim = chalakim; gregorianAbsDate = jewishDateToAbsDate(jewishYear, jewishMonth, jewishDay); // reset Gregorian date - absDateToDate(gregorianAbsDate); dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1; // reset day of week } - /** - * Returns this object's date as a {@link java.util.Calendar} object. - * - * @return The {@link java.util.Calendar} - */ - public Calendar getGregorianCalendar() { - Calendar calendar = Calendar.getInstance(); - calendar.set(getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth()); - return calendar; - } /** * Returns this object's date as a {@link java.time.LocalDate} object. @@ -1170,7 +990,7 @@ public Calendar getGregorianCalendar() { * @return The {@link java.time.LocalDate} */ public LocalDate getLocalDate() { - return LocalDate.of(getGregorianYear(), getGregorianMonth() + 1, getGregorianDayOfMonth()); + return absDateToDate(getAbsDate()); } /** @@ -1181,6 +1001,52 @@ public void resetDate() { setGregorianDate(localDate); } + public void minusDays(int days){ + if (days < 1) { + throw new IllegalArgumentException("the amount of days to subtract has to be greater than zero."); + } + setAbsDate(getAbsDate() - days); + + } + public void addDays(int days){ + if (days < 1) { + throw new IllegalArgumentException("the amount of days to add has to be greater than zero."); + } + setAbsDate(getAbsDate() + days); + } + public void addMonths(int months){ + if (months < 1) { + throw new IllegalArgumentException("the amount of months to add has to be greater than zero."); + } + int year = getJewishYear(); + int month = getJewishMonth(); + for (int i = 0; i < months; i++) { + if (month == ELUL) { + month = TISHREI; + year++; + } else if ((! isJewishLeapYear(year) && month == ADAR) + || (isJewishLeapYear(year) && month == ADAR_II)){ + month = NISSAN; + } else { + month++; + } + } + int day = Math.min(getJewishDayOfMonth(), getDaysInJewishMonth(month,year)); + setJewishDate(year, month, day); + } + public void addYears(int years){ + if (years < 1) { + throw new IllegalArgumentException("the amount of years to add has to be greater than zero."); + } + int year = getJewishYear() + years; + // Clamp to ADAR + int month = Math.min(getJewishMonth(),getLastMonthOfJewishYear(year)); + // Clamp to final day of the month + int day = Math.min(getJewishDayOfMonth(), getDaysInJewishMonth(month,year)); + setJewishDate(year, month, day); + } + + /** * Returns a string containing the Jewish date in the form, "day Month, year" e.g. "21 Shevat, 5729". For more * complex formatting, use the formatter classes. @@ -1192,165 +1058,6 @@ public String toString() { return new HebrewDateFormatter().format(this); } - /** - * Rolls the date, month or year forward by the amount passed in. It modifies both the Gregorian and Jewish dates accordingly. - * If manipulation beyond the fields supported here is required, use the {@link Calendar} class {@link Calendar#add(int, int)} - * or {@link Calendar#roll(int, int)} methods in the following manner. - * - *
-	 * 
-	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
-	 * 	cal.add(Calendar.MONTH, 3); // add 3 Gregorian months
-	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
-	 * 
-	 * 
- * - * @param field the calendar field to be forwarded. The must be {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} - * @param amount the positive amount to move forward - * @throws IllegalArgumentException if the field is anything besides {@link Calendar#DATE}, {@link Calendar#MONTH} or {@link Calendar#YEAR} - * or if the amount is less than 1 - * - * @see #back() - * @see Calendar#add(int, int) - * @see Calendar#roll(int, int) - */ - public void forward(int field, int amount) { //FIXME first param should be converted from the Calendar.DATE - if (field != Calendar.DATE && field != Calendar.MONTH && field != Calendar.YEAR) { - throw new IllegalArgumentException("Unsupported field was passed to Forward. Only Calendar.DATE, Calendar.MONTH or Calendar.YEAR are supported."); - } - if (amount < 1) { - throw new IllegalArgumentException("JewishDate.forward() does not support amounts less than 1. See JewishDate.back()"); - } - if (field == Calendar.DATE) { - // Change Gregorian date - for (int i = 0; i < amount; i++) { - if (gregorianDayOfMonth == getLastDayOfGregorianMonth(gregorianMonth, gregorianYear)) { - gregorianDayOfMonth = 1; - // if last day of year - if (gregorianMonth == 12) { - gregorianYear++; - gregorianMonth = 1; - } else { - gregorianMonth++; - } - } else { // if not last day of month - gregorianDayOfMonth++; - } - - // Change the Jewish Date - if (jewishDay == getDaysInJewishMonth()) { - // if it last day of elul (i.e. last day of Jewish year) - if (jewishMonth == ELUL) { - jewishYear++; - jewishMonth++; - jewishDay = 1; - } else if (jewishMonth == getLastMonthOfJewishYear(jewishYear)) { - // if it is the last day of Adar, or Adar II as case may be - jewishMonth = NISSAN; - jewishDay = 1; - } else { - jewishMonth++; - jewishDay = 1; - } - } else { // if not last date of month - jewishDay++; - } - - if (dayOfWeek == 7) { // if last day of week, loop back to Sunday - dayOfWeek = 1; - } else { - dayOfWeek++; - } - - gregorianAbsDate++; // increment the absolute date - } - } else if (field == Calendar.MONTH) { - forwardJewishMonth(amount); - } else { - setJewishYear(getJewishYear() + amount); - } - } - - /** - * Forward the Jewish date by the number of months passed in. - * FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt with similar to - * the way that the Java Calendar behaves (not that simple since there is a difference between add() or roll(). - * - * @throws IllegalArgumentException if the amount is less than 1 - * @param amount the number of months to roll the month forward - */ - private void forwardJewishMonth(int amount) { - if (amount < 1) { - throw new IllegalArgumentException("the amount of months to forward has to be greater than zero."); - } - for (int i = 0; i < amount; i++) { - if (getJewishMonth() == ELUL) { - setJewishMonth(TISHREI); - setJewishYear(getJewishYear() + 1); - } else if ((! isJewishLeapYear() && getJewishMonth() == ADAR) - || (isJewishLeapYear() && getJewishMonth() == ADAR_II)){ - setJewishMonth(NISSAN); - } else { - setJewishMonth(getJewishMonth() + 1); - } - } - } - - /** - * Rolls the date back by 1 day. It modifies both the Gregorian and Jewish dates accordingly. The API does not - * currently offer the ability to forward more than one day at a time, or to forward by month or year. If such - * manipulation is required use the {@link Calendar} class {@link Calendar#add(int, int)} or - * {@link Calendar#roll(int, int)} methods in the following manner. - * - *
-	 * 
-	 * 	Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate
-	 * 	cal.add(Calendar.MONTH, -3); // subtract 3 Gregorian months
-	 * 	jewishDate.setDate(cal); // set the updated calendar back to this class
-	 * 
-	 * 
- * - * @see #back() - * @see Calendar#add(int, int) - * @see Calendar#roll(int, int) - */ - public void back() { - // Change Gregorian date - if (gregorianDayOfMonth == 1) { // if first day of month - if (gregorianMonth == 1) { // if first day of year - gregorianMonth = 12; - gregorianYear--; - } else { - gregorianMonth--; - } - // change to last day of previous month - gregorianDayOfMonth = getLastDayOfGregorianMonth(gregorianMonth, gregorianYear); - } else { - gregorianDayOfMonth--; - } - // change Jewish date - if (jewishDay == 1) { // if first day of the Jewish month - if (jewishMonth == NISSAN) { - jewishMonth = getLastMonthOfJewishYear(jewishYear); - } else if (jewishMonth == TISHREI) { // if Rosh Hashana - jewishYear--; - jewishMonth--; - } else { - jewishMonth--; - } - jewishDay = getDaysInJewishMonth(); - } else { - jewishDay--; - } - - if (dayOfWeek == 1) { // if first day of week, loop back to Saturday - dayOfWeek = 7; - } else { - dayOfWeek--; - } - gregorianAbsDate--; // change the absolute date - } - /** * Indicates whether some other object is "equal to" this one. * @see Object#equals(Object) @@ -1359,9 +1066,9 @@ public boolean equals(Object object) { if (this == object) { return true; } - if (!(object instanceof JewishDate)) { - return false; - } + if (object == null || getClass() != object.getClass()) { + return false; + } JewishDate jewishDate = (JewishDate) object; return gregorianAbsDate == jewishDate.getAbsDate(); } @@ -1375,33 +1082,6 @@ public int compareTo(JewishDate jewishDate) { return Integer.compare(gregorianAbsDate, jewishDate.getAbsDate()); } - /** - * Returns the Gregorian month (between 0-11). - * - * @return the Gregorian month (between 0-11). Like the java.util.Calendar, months are 0 based. - */ - public int getGregorianMonth() { - return gregorianMonth - 1; //FIXME - } - - /** - * Returns the Gregorian day of the month. - * - * @return the Gregorian day of the mont - */ - public int getGregorianDayOfMonth() { - return gregorianDayOfMonth; - } - - /** - * Returns the Gregorian year. - * - * @return the Gregorian year - */ - public int getGregorianYear() { - return gregorianYear; - } - /** * Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nissan and goes to * 13 for Adar II @@ -1440,84 +1120,6 @@ public int getDayOfWeek() { return dayOfWeek; } - /** - * Sets the Gregorian month. - * - * @param month - * the Gregorian month - * - * @throws IllegalArgumentException - * if a month < 0 or > 11 is passed in - */ - public void setGregorianMonth(int month) { - validateGregorianMonth(month); - setInternalGregorianDate(gregorianYear, month + 1, gregorianDayOfMonth); //FIXME - } - - /** - * sets the Gregorian year. - * - * @param year - * the Gregorian year. - * @throws IllegalArgumentException - * if a year of < 1 is passed in - */ - public void setGregorianYear(int year) { - validateGregorianYear(year); - setInternalGregorianDate(year, gregorianMonth, gregorianDayOfMonth); - } - - /** - * sets the Gregorian Day of month. - * - * @param dayOfMonth - * the Gregorian Day of month. - * @throws IllegalArgumentException - * if the day of month of < 1 is passed in - */ - public void setGregorianDayOfMonth(int dayOfMonth) { - validateGregorianDayOfMonth(dayOfMonth); - setInternalGregorianDate(gregorianYear, gregorianMonth, dayOfMonth); - } - - /** - * sets the Jewish month. - * - * @param month - * the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nissan - * and goes to 13 for Adar II - * @throws IllegalArgumentException - * if a month < 1 or > 12 (or 13 on a leap year) is passed in - */ - public void setJewishMonth(int month) { - setJewishDate(jewishYear, month, jewishDay); - } - - /** - * sets the Jewish year. - * - * @param year - * the Jewish year - * @throws IllegalArgumentException - * if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day - * previously set are < 18 Teves (prior to Jan 1, 1 AD) - */ - public void setJewishYear(int year) { - setJewishDate(year, jewishMonth, jewishDay); - } - - /** - * sets the Jewish day of month. - * - * @param dayOfMonth - * the Jewish day of month - * @throws IllegalArgumentException - * if the day of month is < 1 or > 30 is passed in - */ - public void setJewishDayOfMonth(int dayOfMonth) { - setJewishDate(jewishYear, jewishMonth, dayOfMonth); - } - /** * A method that creates a deep copy of the object. * @@ -1531,7 +1133,7 @@ public Object clone() { // Required by the compiler. Should never be reached since we implement clone() } if (clone != null) { - clone.setInternalGregorianDate(gregorianYear, gregorianMonth, gregorianDayOfMonth); + clone.setAbsDate(getAbsDate()); } return clone; } @@ -1541,9 +1143,6 @@ public Object clone() { * @see Object#hashCode() */ public int hashCode() { - int result = 17; - result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash - result += 37 * result + gregorianAbsDate; - return result; + return Integer.hashCode(gregorianAbsDate); } } From 8b95c6e29bf77f4908e27ff1a9435fc9d9964727 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 18 Mar 2026 22:18:57 -0400 Subject: [PATCH 9/9] some eq changes --- .../zmanim/hebrewcalendar/JewishCalendar.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java index 6fef63cc..9541b27e 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java @@ -23,7 +23,7 @@ import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Calendar; +import java.util.Calendar; // We still use the old Calendar.WEEKDAY constants /** * The JewishCalendar extends the JewishDate class and adds calendar methods. @@ -1396,9 +1396,9 @@ public boolean equals(Object object) { if (this == object) { return true; } - if (!(object instanceof JewishCalendar)) { - return false; - } + if (object == null || getClass() != object.getClass()) { + return false; + } JewishCalendar jewishCalendar = (JewishCalendar) object; return getAbsDate() == jewishCalendar.getAbsDate() && getInIsrael() == jewishCalendar.getInIsrael(); } @@ -1408,9 +1408,8 @@ public boolean equals(Object object) { * @see Object#hashCode() */ public int hashCode() { - int result = 17; - result = 37 * result + getClass().hashCode(); // needed or this and subclasses will return identical hash - result += 37 * result + getAbsDate() + (getInIsrael() ? 1 : 3); - return result; + int result = Integer.hashCode(getAbsDate()); + result = 31 * result + Boolean.hashCode(getInIsrael()); + return result; } }