Skip to content

Commit

Permalink
fix molad methods, add daf yomi calculators with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Elyahu41 committed Dec 21, 2023
1 parent 8de7ba1 commit 6ec77d7
Show file tree
Hide file tree
Showing 4 changed files with 387 additions and 69 deletions.
134 changes: 73 additions & 61 deletions KosherSwift/hebrewcalendar/JewishCalendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1363,59 +1363,6 @@ public class JewishCalendar {
return false;
}

//TODO add Parasha methods

func getSpecialParasha() -> String {
let dayOfWeek = getDayOfWeek()
let jewishMonth = getJewishMonth()
let jewishDayOfMonth = getJewishDayOfMonth()
let isLeapYear = isJewishLeapYear()

if dayOfWeek == 7 {
if (jewishMonth == JewishCalendar.SHEVAT && !isLeapYear) || (jewishMonth == JewishCalendar.ADAR && isLeapYear) {
if [25, 27, 29].contains(jewishDayOfMonth) {
return "שקלים"
}
}
if (jewishMonth == JewishCalendar.ADAR && !isLeapYear) || jewishMonth == JewishCalendar.ADAR_II {
if jewishDayOfMonth == 1 {
return "שקלים"
}
if [8, 9, 11, 13].contains(jewishDayOfMonth) {
return "זכור"
}
if [18, 20, 22, 23].contains(jewishDayOfMonth) {
return "פרה"
}
if [25, 27, 29].contains(jewishDayOfMonth) {
return "החדש"
}
}
if jewishMonth == JewishCalendar.NISSAN {
if jewishDayOfMonth == 1 || (jewishDayOfMonth >= 8 && jewishDayOfMonth <= 14) {
return "הגדול"
}
}
if jewishMonth == JewishCalendar.AV {
if jewishDayOfMonth >= 4 && jewishDayOfMonth <= 9 {
return "חזון"
}
if jewishDayOfMonth >= 10 && jewishDayOfMonth <= 16 {
return "נחמו"
}
}
if jewishMonth == JewishCalendar.TISHREI {
if jewishDayOfMonth >= 3 && jewishDayOfMonth <= 8 {
return "שובה"
}
}
// if getParashah() == "בשלח" {
// return "שירה"
// }
}
return ""
}

/**
* Returns an index of the Jewish holiday or fast day for the current day, or a -1 if there is no holiday for this day.
* There are constants in this class representing each <em>Yom Tov</em>. Formatting of the <em>Yomim tovim</em> is done
Expand Down Expand Up @@ -2081,6 +2028,72 @@ public class JewishCalendar {
return holidayIndex == JewishCalendar.TISHA_BEAV;
}

/**
This method does not return anything, however, it does set the variables moladHours, moladMiutes, and moladChalakim with the proper
values for the molad of this month. Using these variable, you can create a string like so: "The molad is at 20 hours, 1 minutes and 3 Chalakim".
There is also a method to recieve a string like this called getMoladAsString
*/
public func calculateMolad() {
let chalakim = getChalakimSinceMoladTohu(year: getJewishYear(), month: getJewishMonth())
let moladToAbsDate = (chalakim / JewishCalendar.CHALAKIM_PER_DAY) + (JewishCalendar.JEWISH_EPOCH)
var year = moladToAbsDate / 366
while (moladToAbsDate >= gregorianDateToAbsDate(year: year+1,month: 1,dayOfMonth: 1)) {
year+=1
}
var month = 1
while (moladToAbsDate > gregorianDateToAbsDate(year: year, month: month, dayOfMonth: getLastDayOfGregorianMonth(month: month, year: year))) {
month+=1
}
var dayOfMonth = moladToAbsDate - gregorianDateToAbsDate(year: year, month: month, dayOfMonth: 1) + 1
if dayOfMonth > getLastDayOfGregorianMonth(month: month, year: year) {
dayOfMonth = getLastDayOfGregorianMonth(month: month, year: year)
}
let conjunctionDay = chalakim / JewishCalendar.CHALAKIM_PER_DAY
let conjunctionParts = chalakim - conjunctionDay * JewishCalendar.CHALAKIM_PER_DAY

moladHours = conjunctionParts / 1080
let moladRemainingChalakim = conjunctionParts - moladHours * 1080
moladMinutes = moladRemainingChalakim / 18
moladChalakim = moladRemainingChalakim - moladMinutes * 18
moladHours = (moladHours + 18) % 24
}

/**
This method sets the variables moladHours, moladMinutes, and moladChalakim within this class and returns a string like so: "The molad is at 20 hours, 1 minutes and 3 Chalakim"
*/
public func getMoladAsString() -> String {
let chalakim = getChalakimSinceMoladTohu(year: getJewishYear(), month: getJewishMonth())
let moladToAbsDate = (chalakim / JewishCalendar.CHALAKIM_PER_DAY) + (JewishCalendar.JEWISH_EPOCH)
var year = moladToAbsDate / 366
while (moladToAbsDate >= gregorianDateToAbsDate(year: year+1,month: 1,dayOfMonth: 1)) {
year+=1
}
var month = 1
while (moladToAbsDate > gregorianDateToAbsDate(year: year, month: month, dayOfMonth: getLastDayOfGregorianMonth(month: month, year: year))) {
month+=1
}
var dayOfMonth = moladToAbsDate - gregorianDateToAbsDate(year: year, month: month, dayOfMonth: 1) + 1
if dayOfMonth > getLastDayOfGregorianMonth(month: month, year: year) {
dayOfMonth = getLastDayOfGregorianMonth(month: month, year: year)
}
let conjunctionDay = chalakim / JewishCalendar.CHALAKIM_PER_DAY
let conjunctionParts = chalakim - conjunctionDay * JewishCalendar.CHALAKIM_PER_DAY

moladHours = conjunctionParts / 1080
let moladRemainingChalakim = conjunctionParts - moladHours * 1080
moladMinutes = moladRemainingChalakim / 18
moladChalakim = moladRemainingChalakim - moladMinutes * 18
moladHours = (moladHours + 18) % 24

return "The molad is at "
.appending(String(moladHours))
.appending(" hours, ")
.appending(String(moladMinutes))
.appending(" minutes and ")
.appending(String(moladChalakim))
.appending(" Chalakim")
}

/**
* Returns the <em>molad</em> in Standard Time in Yerushalayim as a Date. The traditional calculation uses local time.
* This method subtracts 20.94 minutes (20 minutes and 56.496 seconds) from the local time (of <em>Har Habayis</em>
Expand Down Expand Up @@ -2108,7 +2121,7 @@ public class JewishCalendar {
let conjunctionDay = chalakim / JewishCalendar.CHALAKIM_PER_DAY
let conjunctionParts = chalakim - conjunctionDay * JewishCalendar.CHALAKIM_PER_DAY

var moladHours = conjunctionParts / 1080
let moladHours = conjunctionParts / 1080
let moladRemainingChalakim = conjunctionParts - moladHours * 1080
var moladMinutes = moladRemainingChalakim / 18
let moladChalakim = moladRemainingChalakim - moladMinutes * 18
Expand All @@ -2124,16 +2137,15 @@ public class JewishCalendar {
// The raw molad Date (point in time) must be generated using standard time. Using "Asia/Jerusalem" timezone will result in the time
// being incorrectly off by an hour in the summer due to DST. Proper adjustment for the actual time in DST will be done by the date
// formatter class used to display the Date.
var moladDay = DateComponents(calendar: calendar, timeZone: TimeZone(identifier: "GMT+2")!, year: year, month: month, day: dayOfMonth, hour: moladHours, minute: moladMinutes, second: Int(moladSeconds))
var moladDay = DateComponents(calendar: calendar, timeZone: TimeZone(identifier: "GMT+2")!, year: year, month: month, day: dayOfMonth, hour: moladHours, minute: moladMinutes, second: Int(moladSeconds)-1)

if moladHours > 6 {
moladHours = (moladHours + 18) % 24
moladDay.day! += 1
moladDay.setValue(moladHours, for: .hour)
return calendar.date(from: moladDay)!
} else {
return calendar.date(from: moladDay)!
}
moladDay.setValue((moladHours + 18) % 24, for: .hour)

return calendar.date(from: moladDay)!
}

/**
Expand Down Expand Up @@ -2224,7 +2236,7 @@ public class JewishCalendar {
*
* @return the daf as a {@link Daf}
*/
public func getDafYomiBavli() -> Daf {
public func getDafYomiBavli() -> Daf? {
return YomiCalculator.getDafYomiBavli(jewishCalendar: self);
}
/**
Expand All @@ -2234,7 +2246,7 @@ public class JewishCalendar {
*
* @return the daf as a {@link Daf}
*/
public func getDafYomiYerushalmi() -> Daf {
public func getDafYomiYerushalmi() -> Daf? {
return YerushalmiYomiCalculator.getDafYomiYerushalmi(jewishCalendar: self);
}

Expand Down
170 changes: 167 additions & 3 deletions KosherSwift/hebrewcalendar/YerushalmiYomiCalculator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,173 @@

import Foundation

class YerushalmiYomiCalculator {
/**
* This class calculates the <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud">Talmud Yerusalmi</a> <a href=
* "https://en.wikipedia.org/wiki/Daf_Yomi">Daf Yomi</a> page ({@link Daf}) for the a given date.
*
* @author &copy; elihaidv
* @author &copy; Eliyahu Hershfeld 2017 - 2023
*/
public class YerushalmiYomiCalculator {

public static func getDafYomiYerushalmi(jewishCalendar:JewishCalendar) -> Daf {
return Daf(masechtaNumber: 0, daf: 0)
/**
* The start date of the first Daf Yomi Yerushalmi cycle of February 2, 1980 / 15 Shevat, 5740.
*/
private static let DAF_YOMI_START_DAY = gregorianDate(forYear: 1980, month: 2, andDay: 2)
/** The number of milliseconds in a day. */
private static let DAY_MILIS = 1000 * 60 * 60 * 24
/** The number of pages in the Talmud Yerushalmi.*/
private static let WHOLE_SHAS_DAFS = 1554
/** The number of pages per <em>masechta</em> (tractate).*/
private static let BLATT_PER_MASECHTA = [
68, 37, 34, 44, 31, 59, 26, 33, 28, 20, 13, 92, 65, 71, 22, 22, 42, 26, 26, 33, 34, 22,
19, 85, 72, 47, 40, 47, 54, 48, 44, 37, 34, 44, 9, 57, 37, 19, 13]

/**
* Returns the <a href="https://en.wikipedia.org/wiki/Daf_Yomi">Daf Yomi</a>
* <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud">Yerusalmi</a> page ({@link Daf}) for a given date.
* The first Daf Yomi cycle started on 15 Shevat (Tu Bishvat), 5740 (February, 2, 1980) and calculations
* prior to this date will result in an nil. A nil will be returned on Tisha B'Av or
* Yom Kippur.
*
* @param calendar
* the calendar date for calculation
* @return the {@link Daf} or null if the date is on Tisha B'Av or Yom Kippur.
*
* @throws IllegalArgumentException
* if the date is prior to the February 2, 1980, the start of the first Daf Yomi Yerushalmi cycle
*/
public static func getDafYomiYerushalmi(jewishCalendar:JewishCalendar) -> Daf? {
let dateCreator = Calendar(identifier: .gregorian)
var nextCycle = DateComponents()
var prevCycle = DateComponents()
var masechta = 0
var dafYomi: Daf?

// There isn't Daf Yomi on Yom Kippur or Tisha B'Av.
if jewishCalendar.getYomTovIndex() == JewishCalendar.YOM_KIPPUR || jewishCalendar.getYomTovIndex() == JewishCalendar.TISHA_BEAV {
return nil
}

if jewishCalendar.workingDate.compare(DAF_YOMI_START_DAY!) == .orderedAscending {
return nil
}

nextCycle.year = 1980
nextCycle.month = 2
nextCycle.day = 2

// let n = dateCreator.date(from: nextCycle)
// let p = dateCreator.date(from: prevCycle)

// Go cycle by cycle, until we get the next cycle
while jewishCalendar.workingDate.compare(dateCreator.date(from: nextCycle)!) == .orderedDescending {
prevCycle = nextCycle

nextCycle.day! += WHOLE_SHAS_DAFS
nextCycle.day! += getNumOfSpecialDays(startDate: dateCreator.date(from: prevCycle)!, endDate: dateCreator.date(from: nextCycle)!)
}

// Get the number of days from cycle start until request.
let dafNo = getDiffBetweenDays(start: dateCreator.date(from: prevCycle)!, end: jewishCalendar.workingDate.addingTimeInterval(-86400))// this should be a temporary solution. Not sure why the dates are one day off

// Get the number of special day to subtract
let specialDays = getNumOfSpecialDays(startDate: dateCreator.date(from: prevCycle)!, endDate: jewishCalendar.workingDate)
var total = dafNo - specialDays

// Finally find the daf.
for j in 0..<BLATT_PER_MASECHTA.count {
if total < BLATT_PER_MASECHTA[j] {
dafYomi = Daf(masechtaNumber: masechta, daf: total + 1)
break
}
total -= BLATT_PER_MASECHTA[j]
masechta += 1
}

return dafYomi
}

/**
* Return the number of special days (Yom Kippur and Tisha Beav) That there is no Daf in this days.
* From the last given number of days until given date
*
* @param start start date to calculate
* @param end end date to calculate
* @return the number of special days
*/
private static func getNumOfSpecialDays(startDate: Date, endDate: Date) -> Int {
let startCalendar = JewishCalendar()
startCalendar.workingDate = startDate
let endCalendar = JewishCalendar()
endCalendar.workingDate = endDate

var startYear = startCalendar.getJewishYear()
let endYear = endCalendar.getJewishYear()

var specialDays = 0

let dateCreator = Calendar(identifier: .hebrew)

//create a hebrew calendar set to the date 7/10/5770
var yomKippurComponents = DateComponents()
yomKippurComponents.year = 5770
yomKippurComponents.month = 1
yomKippurComponents.day = 10

var tishaBeavComponents = DateComponents()
tishaBeavComponents.year = 5770
tishaBeavComponents.month = 5
tishaBeavComponents.day = 9

while startYear <= endYear {
yomKippurComponents.year = startYear
tishaBeavComponents.year = startYear

if isBetween(start: startDate, date: dateCreator.date(from: yomKippurComponents)!, end: endDate) {
specialDays += 1
}

if isBetween(start: startDate, date: dateCreator.date(from: tishaBeavComponents)!, end: endDate) {
specialDays += 1
}

startYear += 1
}

return specialDays
}

/**
* Return if the date is between two dates
*
* @param start the start date
* @param date the date being compared
* @param end the end date
* @return if the date is between the start and end dates
*/
private static func isBetween(start: Date, date: Date, end: Date) -> Bool {
return (start.compare(date) == .orderedAscending) && (end.compare(date) == .orderedDescending)
}

/**
* Return the number of days between the dates passed in
* @param start the start date
* @param end the end date
* @return the number of days between the start and end dates
*/
private static func getDiffBetweenDays(start: Date, end: Date) -> Int {
let DAY_MILIS: Double = 24 * 60 * 60
let s = Int(end.timeIntervalSince1970 - start.timeIntervalSince1970)
return s / Int(DAY_MILIS)
}

private static func gregorianDate(forYear year: Int, month: Int, andDay day: Int) -> Date? {
var components = DateComponents()
components.year = year
components.month = month
components.day = day
components.calendar = Calendar(identifier: .gregorian)
return components.date
}
}
Loading

0 comments on commit 6ec77d7

Please sign in to comment.