Skip to content

Commit

Permalink
Correct Tanakh Yomi calculations by running through entire year
Browse files Browse the repository at this point in the history
  • Loading branch information
mjradwin committed Jan 28, 2024
1 parent 3996e4c commit 059379c
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 34 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hebcal/learning",
"version": "5.0.3",
"version": "5.0.4",
"description": "Daily learning schedules: Daf Yomi, Mishna Yomi, etc",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
Expand Down
20 changes: 20 additions & 0 deletions src/masoretic.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
{
"split":{
"Joshua":{
"4.1":"6.27-7.26",
"4.2":"8.1-32"
},
"Jeremiah":{
"9.1":"17:7-25",
"9.2":"17:26-18:18"
},
"Song of Songs":{
"1.1":"1:1-5:1",
"1.2":"5:2-8:14"
},
"Ruth":{
"1.1":"1:1-2:11",
"1.2":"2:12-4:22"
}
},
"regular":{
"Joshua":[
"1:1-3:6",
"3:7-4:23",
Expand Down Expand Up @@ -324,4 +343,5 @@
"II Chronicles 34:2-35:5",
"II Chronicles 35:6-36:23"
]
}
}
147 changes: 114 additions & 33 deletions src/tanakhYomi.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import masoretic from './masoretic.json.js';
const startDate = new Date(1948, 9, 26);
export const tanakhYomiStart = greg.greg2abs(startDate);

const JOSHUA = 'Joshua';
const JEREMIAH = 'Jeremiah';
const RUTH = 'Ruth';
const SHIR_HASHIRIM = 'Song of Songs';

const books = [
['Joshua', 14],
[JOSHUA, 14],
['Judges', 14],
['Samuel', 34],
['Kings', 35],
Expand Down Expand Up @@ -63,51 +64,48 @@ export function tanakhYomi(date) {
const hyear = hd.getFullYear();
const rh = HDate.hebrew2abs(hyear, months.TISHREI, 1);
const startAbs = rh + 22;
const table = books.slice();
const longCheshvan = HDate.longCheshvan(hyear);
let longRuth = false;
if (HDate.isLeapYear(hyear) || longCheshvan) {
table[12] = {name: RUTH, blatt: 2}; // Ruth gets 2 days
longRuth = true;
}
let longShirHaShirim = false;
if (longCheshvan) {
table[11] = {name: SHIR_HASHIRIM, blatt: 2}; // Shir HaShirim gets 2 days
longShirHaShirim = true;
}
let longJeremiah = false;
if (longCheshvan && !HDate.shortKislev(hyear)) {
table[5] = {name: JEREMIAH, blatt: 32}; // Jeremiah 9 gets split across two days
longJeremiah = true;
}

if (cday < startAbs) {
let blatt = rh % 7 === 6 ? 10 : 12;
const rhDow = rh % 7;
let blatt = rhDow === 4 ? 11 : rhDow === 6 ? 10 : 12;
for (let i = rh + 2; i < cday; i++) {
const hdate = new HDate(i);
if (!skipDay(hdate)) {
blatt++;
}
}
if (blatt === 26) {
throw new Error(`${hd.toString()} Chronicles ${blatt}`);
}
return new TanakhYomi('Chronicles', blatt);
}

let total = 0;
for (let i = startAbs; i < cday; i++) {
const hdate = new HDate(i);
if (!skipDay(hdate)) {
total++;
}
}
const readingTable = makeReadingTable(hyear);
const table = readingTable.table;

for (let j = 0; j < table.length; j++) {
if (total < table[j].blatt) {
const blatt = total + 1;
const name = table[j].name;
if ((longShirHaShirim && name === SHIR_HASHIRIM) ||
(longRuth && name === RUTH)) {
if ((readingTable.longShirHaShirim && name === SHIR_HASHIRIM) ||
(readingTable.longRuth && name === RUTH)) {
return new TanakhYomi(name, '1.' + blatt);
}
if (longJeremiah && name === JEREMIAH && blatt >= 9) {
if (readingTable.longJoshua && name === JOSHUA && blatt >= 4) {
if (blatt === 4) {
return new TanakhYomi(name, '4.1');
} else if (blatt === 5) {
return new TanakhYomi(name, '4.2');
} else {
return new TanakhYomi(name, blatt - 1);
}
}
if (readingTable.longJeremiah && name === JEREMIAH && blatt >= 9) {
if (blatt === 9) {
return new TanakhYomi(name, '9.1');
} else if (blatt === 10) {
Expand Down Expand Up @@ -146,11 +144,84 @@ function skipDay(hd) {
return false;
}

const splitSeder = {
'Jeremiah': {'9.1': '17:7-25', '9.2': '17:26-18:18'},
'Song of Songs': {'1.1': '1:1-5:1', '1.2': '5:2-8:14'},
'Ruth': {'1.1': '1:1-2:11', '1.2': '2:12-4:22'},
};
/**
* @private
* @param {number} year
* @return {number}
*/
function calculateNumDaysToRead(year) {
const startAbs = HDate.hebrew2abs(year, months.TISHREI, 23);
const endAbs = HDate.hebrew2abs(year + 1, months.TISHREI, 22);
let included = 0;
for (let abs = startAbs; abs <= endAbs; abs++) {
const hdate = new HDate(abs);
if (!skipDay(hdate)) {
included++;
}
}
return included;
}

/**
* A common year can have a length of 353, 354 or 355 days
* A leap year can have a length of 383, 384 or 385 days
*
* Common years can have
* 293 chapters - no extra chapters (45%)
* 294 chapters - 1 extra chapter (5%)
* 295 chapters - 2 extra chapters (31%)
* 296 chapters - 3 extra chapters (19%)
* Leap years can have
* 318 chapters - no extra chapters (10%)
* 319 chapters - 1 extra chapter (30%)
* 320 chapters - 2 extra chapters (47%)
* 222 chapters - 4 extra chapters (12%)
*
* @private
* @param {number} year
* @return {any}
*/
function makeReadingTable(year) {
const numDays = calculateNumDaysToRead(year);
const count = HDate.isLeapYear(year) ? numDays - 25 : numDays;
const extra = count - 293;
const table = books.slice();
const result = {
numDays,
table,
longRuth: false,
longShirHaShirim: false,
longJeremiah: false,
longJoshua: false,
};
switch (extra) {
case 0:
return result;
case 4:
// Joshua 4 gets split across two days
table[0] = {name: JOSHUA, blatt: 15};
result.longJoshua = true;
/* FALLTHROUGH */
case 3:
// Jeremiah 9 gets split across two days
table[5] = {name: JEREMIAH, blatt: 32};
result.longJeremiah = true;
/* FALLTHROUGH */
case 2:
// Shir HaShirim gets 2 days
table[11] = {name: SHIR_HASHIRIM, blatt: 2};
result.longShirHaShirim = true;
/* FALLTHROUGH */
case 1:
// Ruth gets 2 days
table[12] = {name: RUTH, blatt: 2};
result.longRuth = true;
break;
default:
throw new Error(`${year} => ${numDays} ${count} ${extra}`);
}
return result;
}

/**
* Returns the Daf Yomi for given date
Expand All @@ -163,9 +234,12 @@ export class TanakhYomi extends DafPage {
*/
constructor(name, blatt) {
super(name, blatt);
const seders = masoretic[name];
const seders = masoretic.regular[name];
const verses = typeof blatt === 'number' ?
seders[blatt - 1] : splitSeder[name][blatt];
seders[blatt - 1] : masoretic.split[name][blatt];
if (!verses) {
throw new Error(`${name} ${blatt}`);
}
const firstChar = verses.charCodeAt(0);
this.verses = firstChar >= 48 && firstChar <= 57 ?
`${name} ${verses}` : verses;
Expand All @@ -182,10 +256,17 @@ export class TanakhYomi extends DafPage {
locale = locale.toLowerCase();
}
const name = Locale.gettext(this.name, locale);
const blatt = this.blatt;
if (locale === 'he' || locale === 'he-x-nonikud') {
return name + ' ס׳ ' + gematriya(this.blatt);
const prefix = name + ' ס׳ ';
if (typeof blatt === 'string') {
const major = blatt[0];
const minor = blatt[2];
return prefix + gematriya(+major) + minor;
}
return prefix + gematriya(blatt);
}
return name + ' Seder ' + this.blatt;
return name + ' Seder ' + blatt;
}
}

Expand Down
Loading

0 comments on commit 059379c

Please sign in to comment.