From ba52cc156110fd5287bfda6c9304d7900da1e322 Mon Sep 17 00:00:00 2001 From: Elyahu Date: Fri, 6 Sep 2024 01:46:27 -0400 Subject: [PATCH] added support for safek mukaf choma, updated code with KosherJava changes --- KosherSwiftNew.podspec | 2 +- .../KosherSwift/ComplexZmanimCalendar.swift | 8 +- .../hebrewcalendar/JewishCalendar.swift | 47 ++++++- Sources/KosherSwift/util/NOAACalculator.swift | 117 +++++------------- 4 files changed, 80 insertions(+), 94 deletions(-) diff --git a/KosherSwiftNew.podspec b/KosherSwiftNew.podspec index fb6b719..97ee226 100644 --- a/KosherSwiftNew.podspec +++ b/KosherSwiftNew.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = "KosherSwiftNew" - spec.version = "1.0.7" + spec.version = "1.0.9" spec.summary = "KosherJava Zmanim API / Library ported to Swift." spec.description = "This Zmanim library is an API for a specialized calendar that can calculate different astronomical times including sunrise and sunset and Jewish zmanim or religious times for prayers and other Jewish religious duties. diff --git a/Sources/KosherSwift/ComplexZmanimCalendar.swift b/Sources/KosherSwift/ComplexZmanimCalendar.swift index f1f0d88..6663781 100644 --- a/Sources/KosherSwift/ComplexZmanimCalendar.swift +++ b/Sources/KosherSwift/ComplexZmanimCalendar.swift @@ -4033,7 +4033,7 @@ public class ComplexZmanimCalendar : ZmanimCalendar { * @see getHalfDayBasedZman(Date, Date, double) */ public func getSofZmanShmaGRASunriseToFixedLocalChatzos() -> Date? { - return getHalfDayBasedZman(startOfHalfDay: getSunrise(), endOfHalfDay: getFixedLocalChatzos(), hours: 3); + return getHalfDayBasedZman(startOfHalfDay: getElevationAdjustedSunrise(), endOfHalfDay: getFixedLocalChatzos(), hours: 3); } /** @@ -4053,7 +4053,7 @@ public class ComplexZmanimCalendar : ZmanimCalendar { * @see getHalfDayBasedZman(Date, Date, double) */ public func getSofZmanTfilaGRASunriseToFixedLocalChatzos() -> Date? { - return getHalfDayBasedZman(startOfHalfDay: getSunrise(), endOfHalfDay: getFixedLocalChatzos(), hours: 4); + return getHalfDayBasedZman(startOfHalfDay: getElevationAdjustedSunrise(), endOfHalfDay: getFixedLocalChatzos(), hours: 4); } /** @@ -4093,7 +4093,7 @@ public class ComplexZmanimCalendar : ZmanimCalendar { * @see ZmanimCalendar#getHalfDayBasedZman(Date, Date, double) */ public func getMinchaKetanaGRAFixedLocalChatzosToSunset() -> Date? { - return getHalfDayBasedZman(startOfHalfDay: getFixedLocalChatzos(), endOfHalfDay: getSunset(), hours: 3.5); + return getHalfDayBasedZman(startOfHalfDay: getFixedLocalChatzos(), endOfHalfDay: getElevationAdjustedSunset(), hours: 3.5); } /** @@ -4114,7 +4114,7 @@ public class ComplexZmanimCalendar : ZmanimCalendar { * @see ZmanimCalendar#getHalfDayBasedZman(Date, Date, double) */ public func getPlagHaminchaGRAFixedLocalChatzosToSunset() -> Date? { - return getHalfDayBasedZman(startOfHalfDay: getFixedLocalChatzos(), endOfHalfDay: getSunset(), hours: 4.75); + return getHalfDayBasedZman(startOfHalfDay: getFixedLocalChatzos(), endOfHalfDay: getElevationAdjustedSunset(), hours: 4.75); } /** diff --git a/Sources/KosherSwift/hebrewcalendar/JewishCalendar.swift b/Sources/KosherSwift/hebrewcalendar/JewishCalendar.swift index 355922b..1daf959 100644 --- a/Sources/KosherSwift/hebrewcalendar/JewishCalendar.swift +++ b/Sources/KosherSwift/hebrewcalendar/JewishCalendar.swift @@ -302,6 +302,13 @@ public class JewishCalendar { */ public var isMukafChoma: Bool = false + /** + * Is the calendar set to have a safek (doubtful) Purim demukafim, where Purim is celebrated (mainly) on the 14th and the 15th of Adar. + * @see #getIsSafekMukafChoma() + * @see #setIsSafekMukafChoma(boolean) + */ + public var isSafekMukafChoma = false + /** * List of parshiyos or special Shabasos. ``NONE`` indicates a week without a parsha, while the enum for * the parsha of ``VZOS_HABERACHA`` exists for consistency, but is not currently used. The special Shabasos of @@ -678,6 +685,29 @@ public class JewishCalendar { self.isMukafChoma = isMukafChoma; } + /** + * Returns if the city is set as a city that may have been surrounded by a wall from the time of Yehoshua, and + * Shushan Purim should be celebrated as well as regular Purim. + * @return if the city is set as a city that may have been surrounded by a wall from the time of Yehoshua, and + * Shushan Purim should be celebrated as well as regular Purim. + * @see #setIsSafekMukafChoma(boolean) + */ + public func getIsSafekMukafChoma() -> Bool { + return isSafekMukafChoma + } + + /** + * Sets if the location may have been surrounded by a wall from the time of Yehoshua, and Shushan Purim should be + * celebrated as well as regular Purim. This can be set for Baghdad Iraq, or other cities around the world that + * have the status of a "Safek Mukaf Choma". There are multiple cities in Israel that have this status. + * The exception being Yerushalayim. + * @param isSafekMukafChoma if the city may have been surrounded by a wall from the time of Yehoshua. + * @see #getIsSafekMukafChoma() + */ + public func setIsSafekMukafChoma(isSafekMukafChoma: Bool) { + self.isSafekMukafChoma = isSafekMukafChoma + } + /** * The internal date object that all the calculations are dependant on. Change this date to effect all the other methods of the class. By default the date is set to the system's current time with the system's current timezone. */ @@ -1544,7 +1574,7 @@ public class JewishCalendar { if (day == 15 || (day == 16 && !inIsrael)) { return JewishCalendar.SUCCOS; } - if (day >= 17 && day <= 20 || (day == 16 && inIsrael)) { + if (day >= 16 && day <= 20) { return JewishCalendar.CHOL_HAMOED_SUCCOS; } if (day == 21) { @@ -1641,7 +1671,7 @@ public class JewishCalendar { */ public func isYomTov() -> Bool { let holidayIndex = getYomTovIndex(); - if ((isErevYomTov() && (holidayIndex != JewishCalendar.HOSHANA_RABBA || (holidayIndex == JewishCalendar.CHOL_HAMOED_PESACH && getJewishDayOfMonth() != 20))) + if ((isErevYomTov() && !( holidayIndex == JewishCalendar.HOSHANA_RABBA || holidayIndex == JewishCalendar.CHOL_HAMOED_PESACH)) || (isTaanis() && holidayIndex != JewishCalendar.YOM_KIPPUR) || holidayIndex == JewishCalendar.ISRU_CHAG) { return false; } @@ -2018,17 +2048,22 @@ public class JewishCalendar { /** * Returns if the day is Purim (Shushan Purim - * in a mukaf choma and regular Purim in a non-mukaf choma). - * @return if the day is Purim (Shushan Purim in a mukaf choma and regular Purim in a non-mukaf choma) + * in a mukaf choma and regular Purim in a non-mukaf choma. On both days if {@link #setIsSafekMukafChoma(boolean)} is true). + * @return if the day is Purim (Shushan Purim in a mukaf choma and regular Purim in a non-mukaf choma or on both days + * if {@link #setIsSafekMukafChoma(boolean)} is true) * * @see #getIsMukafChoma() * @see #setIsMukafChoma(boolean) + * @see #getIsSafekMukafChoma() + * @see #setIsSafekMukafChoma(boolean) */ public func isPurim() -> Bool { if (isMukafChoma) { - return getYomTovIndex() == JewishCalendar.SHUSHAN_PURIM; + return getYomTovIndex() == JewishCalendar.SHUSHAN_PURIM + } else if (isSafekMukafChoma) { + return getYomTovIndex() == JewishCalendar.PURIM || getYomTovIndex() == JewishCalendar.SHUSHAN_PURIM } else { - return getYomTovIndex() == JewishCalendar.PURIM; + return getYomTovIndex() == JewishCalendar.PURIM } } diff --git a/Sources/KosherSwift/util/NOAACalculator.swift b/Sources/KosherSwift/util/NOAACalculator.swift index 06e7662..82222a5 100644 --- a/Sources/KosherSwift/util/NOAACalculator.swift +++ b/Sources/KosherSwift/util/NOAACalculator.swift @@ -50,13 +50,23 @@ public class NOAACalculator : AstronomicalCalculator { public init(geoLocation: GeoLocation) { NOAACalculator.geoLocation = geoLocation } + + /** + * An enum to indicate what type of solar event is being calculated. + */ + enum SolarEvent { + /**SUNRISE A solar event related to sunrise*/case SUNRISE + /**SUNSET A solar event related to sunset*/case SUNSET + // possibly add the following in the future, if added, an IllegalArgumentException should be thrown in getSunHourAngle + // /**NOON A solar event related to noon*/NOON, /**MIDNIGHT A solar event related to midnight*/MIDNIGHT + } /** * Returns the name of the algorithm. * @return the descriptive name of the algorithm. */ public func getCalculatorName() -> String { - return "US National Oceanic and Atmospheric Administration Algorithm"; + return "US National Oceanic and Atmospheric Administration Algorithm"; // Implementation of the Jean Meeus algorithm } /** @@ -72,8 +82,7 @@ public class NOAACalculator : AstronomicalCalculator { public override func getUTCSunrise(date: Date, geoLocation: GeoLocation, zenith: Double, adjustForElevation: Bool) -> Double { let elevation = adjustForElevation ? geoLocation.getElevation() : 0; let adjustedZenith = adjustZenith(zenith: zenith, elevation: elevation); - - var sunrise = NOAACalculator.getSunriseUTC(julianDay: NOAACalculator.getJulianDay(date: date), latitude: geoLocation.getLatitude(), longitude: -geoLocation.getLongitude(), zenith: adjustedZenith); + var sunrise = NOAACalculator.getSunRiseSetUTC(julianDay: NOAACalculator.getJulianDay(date: date), latitude: geoLocation.getLatitude(), longitude: -geoLocation.getLongitude(), zenith: adjustedZenith, solarEvent: SolarEvent.SUNRISE); sunrise = sunrise / 60; // ensure that the time is >= 0 and < 24 @@ -111,7 +120,7 @@ public class NOAACalculator : AstronomicalCalculator { let elevation = adjustForElevation ? geoLocation.getElevation() : 0; let adjustedZenith = adjustZenith(zenith: zenith, elevation: elevation); - var sunset = NOAACalculator.getSunsetUTC(julianDay: NOAACalculator.getJulianDay(date: date), latitude: geoLocation.getLatitude(), longitude: -geoLocation.getLongitude(),zenith: adjustedZenith); + var sunset = NOAACalculator.getSunRiseSetUTC(julianDay: NOAACalculator.getJulianDay(date: date), latitude: geoLocation.getLatitude(), longitude: -geoLocation.getLongitude(),zenith: adjustedZenith, solarEvent: SolarEvent.SUNSET); sunset = sunset / 60; // ensure that the time is >= 0 and < 24 @@ -360,28 +369,9 @@ public class NOAACalculator : AstronomicalCalculator { return toDegrees(radians: equationOfTime) * 4.0; // in minutes of time } - /** - * Return the hour angle of the sun in - * radians at sunrise for the latitude. - * - * @param lat - * the latitude of observer in degrees - * @param solarDec - * the declination angle of sun in degrees - * @param zenith - * the zenith - * @return hour angle of sunrise in radians - */ - private static func getSunHourAngleAtSunrise(lat: Double, solarDec: Double, zenith: Double) -> Double { - let latRad = toRadians(degrees: lat); - let sdRad = toRadians(degrees: solarDec); - - return acos(cos(toRadians(degrees: zenith)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad)); // in radians - } - /** * Returns the hour angle of the sun in radiansat sunset for the latitude. + * "https://en.wikipedia.org/wiki/Radian">radiansat for the latitude. * @todo use - {@link #getSunHourAngleAtSunrise(double, double, double)} implementation to avoid duplication of code. * * @param lat @@ -390,14 +380,19 @@ public class NOAACalculator : AstronomicalCalculator { * the declination angle of sun in degrees * @param zenith * the zenith + * @param solarEvent + * If the hour angle is for sunrise or sunset * @return the hour angle of sunset in radians */ - private static func getSunHourAngleAtSunset(lat: Double, solarDec: Double, zenith: Double) -> Double { - let latRad = toRadians(degrees: lat); - let sdRad = toRadians(degrees: solarDec); - - let hourAngle = (acos(cos(toRadians(degrees: zenith)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad))); - return -hourAngle; // in radians + private static func getSunHourAngle(latitude: Double, solarDeclination: Double, zenith: Double, solarEvent: SolarEvent) -> Double { + let latRad = toRadians(degrees: latitude); + let sdRad = toRadians(degrees: solarDeclination); + + var hourAngle = (acos(cos(toRadians(degrees: zenith)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad))); + if (solarEvent == SolarEvent.SUNSET) { + hourAngle = -hourAngle; + } + return hourAngle; } /** @@ -474,52 +469,6 @@ public class NOAACalculator : AstronomicalCalculator { return toDegrees(radians: atan(sin(hourAngle_rad) / ((cos(hourAngle_rad) * sin(lat_rad)) - (tan(dec_rad) * cos(lat_rad)))))+180.0; } - - /** - * Return the Universal Coordinated Time (UTC) - * of sunrise for the given day at the given location on earth. - * - * @param julianDay - * the Julian day - * @param latitude - * the latitude of observer in degrees - * @param longitude - * the longitude of observer in degrees - * @param zenith - * the zenith - * @return the time in minutes from zero UTC - */ - private static func getSunriseUTC(julianDay:Double, latitude:Double, longitude:Double, zenith:Double) -> Double { - let julianCenturies = getJulianCenturiesFromJulianDay(julianDay: julianDay); - - // Find the time of solar noon at the location, and use that declination. This is better than start of the - // Julian day - - let noonmin = getSolarNoonUTC(julianCenturies: julianCenturies, longitude: longitude); - let tnoon = getJulianCenturiesFromJulianDay(julianDay: julianDay + noonmin / 1440.0); - - // First pass to approximate sunrise (using solar noon) - - var eqTime = getEquationOfTime(julianCenturies: tnoon); - var solarDec = getSunDeclination(julianCenturies: tnoon); - var hourAngle = getSunHourAngleAtSunrise(lat: latitude, solarDec: solarDec, zenith: zenith); - - var delta = longitude - toDegrees(radians: hourAngle); - var timeDiff = 4 * delta; // in minutes of time - var timeUTC = 720 + timeDiff - eqTime; // in minutes - - // Second pass includes fractional Julian Day in gamma calc - - let newt = getJulianCenturiesFromJulianDay(julianDay: getJulianDayFromJulianCenturies(julianCenturies: julianCenturies) + timeUTC - / 1440.0); - eqTime = getEquationOfTime(julianCenturies: newt); - solarDec = getSunDeclination(julianCenturies: newt); - hourAngle = getSunHourAngleAtSunrise(lat: latitude, solarDec: solarDec, zenith: zenith); - delta = longitude - toDegrees(radians: hourAngle); - timeDiff = 4 * delta; - timeUTC = 720 + timeDiff - eqTime; // in minutes - return timeUTC; - } /** * Return the Universal Coordinated Time (UTC) @@ -572,7 +521,7 @@ public class NOAACalculator : AstronomicalCalculator { * @see #getUTCNoon(Calendar, GeoLocation) */ private static func getSolarNoonUTC(julianCenturies: Double, longitude: Double) -> Double { - // First pass uses approximate solar noon to calculate equation of time + // Only 1 pass for approximate solar noon to calculate equation of time let tnoon = getJulianCenturiesFromJulianDay(julianDay: getJulianDayFromJulianCenturies(julianCenturies: julianCenturies) + longitude / 360.0); var eqTime = getEquationOfTime(julianCenturies: tnoon); let solNoonUTC = 720 + (longitude * 4) - eqTime; // min @@ -586,7 +535,7 @@ public class NOAACalculator : AstronomicalCalculator { /** * Return the Universal Coordinated Time (UTC) - * of sunset for the given day at the given location on earth. + * of sunrise or sunset for the given day at the given location on earth. * * @param julianDay * the Julian day @@ -596,13 +545,15 @@ public class NOAACalculator : AstronomicalCalculator { * longitude of observer in degrees * @param zenith * zenith + * @param solarEvent + * Is the calculation for sunrise or sunset * @return the time in minutes from zero Universal Coordinated Time (UTC) */ - private static func getSunsetUTC(julianDay:Double, latitude:Double, longitude: Double, zenith:Double) -> Double { + private static func getSunRiseSetUTC(julianDay: Double, latitude: Double, longitude: Double, zenith: Double, solarEvent: SolarEvent) -> Double { let julianCenturies = getJulianCenturiesFromJulianDay(julianDay: julianDay); - // Find the time of solar noon at the location, and use that declination. This is better than start of the - // Julian day + // Find the time of solar noon at the location, and use that declination. + // This is better than start of the Julian day let noonmin = getSolarNoonUTC(julianCenturies: julianCenturies, longitude: longitude); let tnoon = getJulianCenturiesFromJulianDay(julianDay: julianDay + noonmin / 1440.0); @@ -611,7 +562,7 @@ public class NOAACalculator : AstronomicalCalculator { var eqTime = getEquationOfTime(julianCenturies: tnoon); var solarDec = getSunDeclination(julianCenturies: tnoon); - var hourAngle = getSunHourAngleAtSunset(lat: latitude, solarDec: solarDec, zenith: zenith); + var hourAngle = getSunHourAngle(latitude: latitude, solarDeclination: solarDec, zenith: zenith, solarEvent: solarEvent); var delta = longitude - toDegrees(radians: hourAngle); var timeDiff = 4 * delta; @@ -623,7 +574,7 @@ public class NOAACalculator : AstronomicalCalculator { / 1440.0); eqTime = getEquationOfTime(julianCenturies: newt); solarDec = getSunDeclination(julianCenturies: newt); - hourAngle = getSunHourAngleAtSunset(lat: latitude, solarDec: solarDec, zenith: zenith); + hourAngle = getSunHourAngle(latitude: latitude, solarDeclination: solarDec, zenith: zenith, solarEvent: solarEvent); delta = longitude - toDegrees(radians: hourAngle); timeDiff = 4 * delta;