Skip to content

Commit

Permalink
Get correct readings for Shabbat Chanukah maftir
Browse files Browse the repository at this point in the history
  • Loading branch information
mjradwin committed Aug 24, 2022
1 parent 6be7ea2 commit e3b4f79
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 72 deletions.
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hebcal/leyning",
"version": "6.0.0",
"version": "6.0.1",
"author": "Michael J. Radwin (https://github.com/mjradwin)",
"keywords": [
"hebcal",
Expand Down Expand Up @@ -60,7 +60,7 @@
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"ava": "^4.3.1",
"core-js": "^3.24.1",
"core-js": "^3.25.0",
"eslint": "^8.22.0",
"eslint-config-google": "^0.14.0",
"jsdoc": "^3.6.11",
Expand Down
60 changes: 33 additions & 27 deletions src/getLeyningForParshaHaShavua.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ function formatAliyah(aliyot, num) {
return formatAliyahWithBook(aliyot.fullkriyah[num]);
}

test('getLeyningForParshaHaShavua', (t) => {
/** @type {HebrewCalendar.Options} */
test('2026', (t) => {
const options = {year: 2026, isHebrewYear: false, sedrot: true, noHolidays: true};
const events = HebrewCalendar.calendar(options);
for (const ev of events) {
Expand Down Expand Up @@ -71,8 +70,7 @@ test('getLeyningForParshaHaShavua', (t) => {
t.is(a.haftara, 'I Samuel 20:18-42');
break;
case 'Parashat Vayeshev':
t.is(a.reason.haftara, 'Shabbat Chanukah');
t.is(a.reason.M, 'Chanukah Day 1');
t.is(a.reason.haftara, 'Chanukah Day 1 (on Shabbat)');
t.is(a.haftara, 'Zechariah 2:14-4:7');
t.is(formatAliyah(a, 'M'), 'Numbers 7:1-7:17');
break;
Expand Down Expand Up @@ -100,14 +98,14 @@ test('getLeyningForParshaHaShavua', (t) => {
'6': {k: 'Genesis', b: '43:16', e: '43:29', v: 14},
'7': {k: 'Genesis', b: '43:30', e: '44:17', v: 22},
'M': {p: 35, k: 'Numbers', b: '7:54', e: '8:4', v: 40,
reason: 'Chanukah Day 8'},
reason: 'Chanukah Day 8 (on Shabbat)'},
},
haft: {
k: 'I Kings',
b: '7:40',
e: '7:50',
v: 11,
reason: 'Shabbat Chanukah II',
reason: 'Chanukah Day 8 (on Shabbat)',
},
haftara: 'I Kings 7:40-50',
haftaraNumV: 11,
Expand All @@ -117,29 +115,37 @@ test('getLeyningForParshaHaShavua', (t) => {
'3': {k: 'Genesis', b: '41:8', e: '41:14', v: 7},
},
reason: {
haftara: 'Shabbat Chanukah II',
M: 'Chanukah Day 8',
haftara: 'Chanukah Day 8 (on Shabbat)',
M: 'Chanukah Day 8 (on Shabbat)',
},
};
t.deepEqual(a, expected, 'Shabbat Chanukah II');
t.deepEqual(a, expected, 'Chanukah Day 8 (on Shabbat)');
break;
}
}
});

options.year = 2020;
options.month = 12;
let events2 = HebrewCalendar.calendar(options);
const vayeshev = events2.find((e) => e.getDesc() == 'Parashat Vayeshev');
let a = getLeyningForParshaHaShavua(vayeshev);
t.is(a.reason.haftara, 'Shabbat Chanukah');
t.is(a.reason['M'], 'Chanukah Day 2');
test('2020-12', (t) => {
const events = HebrewCalendar.calendar({year: 2020, month: 12, sedrot: true, noHolidays: true});
const vayeshev = events.find((e) => e.getDesc() == 'Parashat Vayeshev');
const a = getLeyningForParshaHaShavua(vayeshev);
t.is(a.reason.haftara, 'Chanukah Day 2 (on Shabbat)');
t.is(a.reason['M'], 'Chanukah Day 2 (on Shabbat)');
t.is(a.haftara, 'Zechariah 2:14-4:7');
t.is(formatAliyah(a, 'M'), 'Numbers 7:18-7:29');
const expected = {
p: 35,
k: 'Numbers',
b: '7:18',
e: '7:23',
v: 6,
reason: 'Chanukah Day 2 (on Shabbat)',
};
t.deepEqual(a.fullkriyah.M, expected);
});

options.year = 2021;
options.month = 12;
events2 = HebrewCalendar.calendar(options);
const miketz = events2.find((e) => e.getDesc() == 'Parashat Miketz');
test('2021-12', (t) => {
const events = HebrewCalendar.calendar({year: 2021, month: 12, sedrot: true, noHolidays: true});
const miketz = events.find((e) => e.getDesc() == 'Parashat Miketz');
const expected = {
name: {
en: 'Miketz',
Expand Down Expand Up @@ -187,14 +193,14 @@ test('getLeyningForParshaHaShavua', (t) => {
'M': 'Shabbat Rosh Chodesh Chanukah',
},
};
a = getLeyningForParshaHaShavua(miketz);
const a = getLeyningForParshaHaShavua(miketz);
t.deepEqual(a, expected, 'Shabbat Rosh Chodesh Chanukah');
});

options.year = 2019;
options.month = 4;
events2 = HebrewCalendar.calendar(options);
const tazria = events2.find((e) => e.getDesc() == 'Parashat Tazria');
a = getLeyningForParshaHaShavua(tazria);
test('2019-04', (t) => {
const events = HebrewCalendar.calendar({year: 2019, month: 4, sedrot: true, noHolidays: true});
const tazria = events.find((e) => e.getDesc() == 'Parashat Tazria');
const a = getLeyningForParshaHaShavua(tazria);
t.is(a.reason.haftara, 'Shabbat HaChodesh (on Rosh Chodesh)');
t.is(a.reason['7'], 'Shabbat HaChodesh (on Rosh Chodesh)');
t.is(a.reason['M'], 'Shabbat HaChodesh (on Rosh Chodesh)');
Expand Down
2 changes: 2 additions & 0 deletions src/getLeyningKeyForEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export function getLeyningKeyForEvent(ev, il = false) {
return 'Shabbat Rosh Chodesh Chanukah';
} else if (isRoshChodesh && ev.chanukahDay == 7) {
return `Chanukah Day 7 (on Rosh Chodesh)`;
} else if (isShabbat) {
return `Chanukah Day ${ev.chanukahDay} (on Shabbat)`;
} else {
return `Chanukah Day ${ev.chanukahDay}`;
}
Expand Down
2 changes: 1 addition & 1 deletion src/getLeyningKeyForEvent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ test('getLeyningKeyForEvent', (t) => {
'Rosh Chodesh Kislev': 'Rosh Chodesh Kislev',
'Chanukah: 1 Candle': undefined,
'Chanukah: 2 Candles': 'Chanukah Day 1',
'Chanukah: 3 Candles': 'Chanukah Day 2',
'Chanukah: 3 Candles': 'Chanukah Day 2 (on Shabbat)',
'Chanukah: 4 Candles': 'Chanukah Day 3',
'Chanukah: 5 Candles': 'Chanukah Day 4',
'Chanukah: 6 Candles': 'Chanukah Day 5',
Expand Down
25 changes: 21 additions & 4 deletions src/holiday-readings.json
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,6 @@
"6":{"p":54,"k":5,"b":"33:27","e":"34:12"},
"7":{"p":1,"k":1,"b":"1:1","e":"2:3"},
"M":{"p":41,"k":4,"b":"29:35","e":"30:1"}}},
"Shabbat Chanukah":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"}},
"Shabbat Chanukah II":{
"haft":{"k":"I Kings","b":"7:40","e":"7:50"}},
"Shabbat Rosh Chodesh Chanukah":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":
Expand All @@ -371,6 +367,9 @@
"alt":{"1":{"p":35,"k":4,"b":"7:1","e":"7:3"},
"2":{"p":35,"k":4,"b":"7:4","e":"7:11"},
"3":{"p":35,"k":4,"b":"7:12","e":"7:17"}}},
"Chanukah Day 1 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:1","e":"7:17"}}},
"Chanukah Day 2":{
"fullkriyah":
{"1":{"p":35,"k":4,"b":"7:18","e":"7:20"},
Expand All @@ -379,6 +378,9 @@
"alt":{"1":{"p":35,"k":4,"b":"7:18","e":"7:20"},
"2":{"p":35,"k":4,"b":"7:21","e":"7:23"},
"3":{"p":35,"k":4,"b":"7:18","e":"7:23"}}},
"Chanukah Day 2 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:18","e":"7:23"}}},
"Chanukah Day 3":{
"fullkriyah":
{"1":{"p":35,"k":4,"b":"7:24","e":"7:26"},
Expand All @@ -387,6 +389,9 @@
"alt":{"1":{"p":35,"k":4,"b":"7:24","e":"7:26"},
"2":{"p":35,"k":4,"b":"7:27","e":"7:29"},
"3":{"p":35,"k":4,"b":"7:24","e":"7:29"}}},
"Chanukah Day 3 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:24","e":"7:29"}}},
"Chanukah Day 4":{
"fullkriyah":
{"1":{"p":35,"k":4,"b":"7:30","e":"7:32"},
Expand All @@ -395,6 +400,9 @@
"alt":{"1":{"p":35,"k":4,"b":"7:30","e":"7:32"},
"2":{"p":35,"k":4,"b":"7:33","e":"7:35"},
"3":{"p":35,"k":4,"b":"7:30","e":"7:35"}}},
"Chanukah Day 4 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:30","e":"7:35"}}},
"Chanukah Day 5":{
"fullkriyah":
{"1":{"p":35,"k":4,"b":"7:36","e":"7:38"},
Expand All @@ -403,6 +411,9 @@
"alt":{"1":{"p":35,"k":4,"b":"7:36","e":"7:38"},
"2":{"p":35,"k":4,"b":"7:39","e":"7:41"},
"3":{"p":35,"k":4,"b":"7:36","e":"7:41"}}},
"Chanukah Day 5 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:36","e":"7:41"}}},
"Chanukah Day 6":{
"fullkriyah":
{"1":{"p":41,"k":4,"b":"28:1","e":"28:5"},
Expand All @@ -423,11 +434,17 @@
"2":{"p":41,"k":4,"b":"28:6","e":"28:10"},
"3":{"p":41,"k":4,"b":"28:11","e":"28:15"},
"4":{"p":35,"k":4,"b":"7:48","e":"7:53"}}},
"Chanukah Day 7 (on Shabbat)":{
"haft":{"k":"Zechariah","b":"2:14","e":"4:7"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:48","e":"7:53"}}},
"Chanukah Day 8":{
"fullkriyah":
{"1":{"p":35,"k":4,"b":"7:54","e":"7:56"},
"2":{"p":35,"k":4,"b":"7:57","e":"7:59"},
"3":{"p":35,"k":4,"b":"7:60","e":"8:4"}}},
"Chanukah Day 8 (on Shabbat)":{
"haft":{"k":"I Kings","b":"7:40","e":"7:50"},
"fullkriyah":{"M":{"p":35,"k":4,"b":"7:54","e":"8:4"}}},
"Purim":{
"fullkriyah":
{"1":{"p":16,"k":2,"b":"17:8","e":"17:10"},
Expand Down
17 changes: 15 additions & 2 deletions src/holiday.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ test('getLeyningForHoliday', (t) => {
t.is(getLeyningForHoliday(sukkot2).fullkriyah['4'].p, 41);
const shminiAtzeret = events.find((e) => e.getDesc() == 'Shmini Atzeret');
t.is(getLeyningForHoliday(shminiAtzeret).fullkriyah['7'].p, 47);
const chanukah3 = events.find((e) => e.getDesc() == 'Chanukah: 3 Candles');
t.is(getLeyningForHoliday(chanukah3).fullkriyah['3'].e, '7:29');
const tevet17 = events.find((e) => e.getDesc() == 'Asara B\'Tevet');
t.is(getLeyningForHoliday(tevet17).fullkriyah['3'].e, '34:10');
const pesach5 = events.find((e) => e.getDesc() == 'Pesach V (CH\'\'M)');
Expand All @@ -125,6 +123,21 @@ test('getLeyningForHoliday', (t) => {
t.is(getLeyningForHoliday(av9).haftara, 'Jeremiah 8:13-9:23');
});

test('getLeyningForHoliday-Chanukah', (t) => {
const options = {year: 5757, isHebrewYear: true, il: true};
const events = HebrewCalendar.calendar(options);
const chanukah3 = events.find((e) => e.getDesc() == 'Chanukah: 3 Candles');
const reading = getLeyningForHoliday(chanukah3);
const expected = {
name: {en: 'Chanukah Day 2 (on Shabbat)', he: undefined},
fullkriyah: {M: {p: 35, k: 'Numbers', b: '7:18', e: '7:23', v: 6}},
haft: {k: 'Zechariah', b: '2:14', e: '4:7', v: 21},
haftara: 'Zechariah 2:14-4:7',
haftaraNumV: 21,
};
t.deepEqual(reading, expected);
});

test('getLeyningForHoliday-RoshChodesh', (t) => {
const ev = new Event(new HDate(1, months.SIVAN, 5782),
'Rosh Chodesh Sivan', flags.ROSH_CHODESH);
Expand Down
16 changes: 5 additions & 11 deletions src/leyning.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,12 @@ function getHaftaraKey(parsha) {

/**
* @private
* @param {Event} ev the Hebcal event associated with this leyning
* @return {any}
* @param {string[]} parsha
* @param {HDate} hd
* @return {Object|boolean}
*/
function getSpecialHaftara(ev) {
if (!ev instanceof Event) {
throw new TypeError(`Bad event argument: ${ev}`);
} else if (ev.getFlags() != flags.PARSHA_HASHAVUA) {
throw new TypeError(`Event must be parsha hashavua: ${ev.getDesc()}`);
}
const parsha = ev.parsha;
function getSpecialHaftara(parsha, hd) {
const name = parshaToString(parsha); // untranslated
const hd = ev.getDate();
const day = hd.getDate();
if (name === 'Pinchas') {
const month = hd.getMonth();
Expand Down Expand Up @@ -260,7 +254,7 @@ export function getLeyningForParshaHaShavua(ev, il=false) {
result.summary = makeSummaryFromParts(parts);
result.summaryParts = parts;
}
const specialHaftara2 = getSpecialHaftara(ev);
const specialHaftara2 = getSpecialHaftara(parsha, hd);
if (specialHaftara2) {
result.haft = cloneHaftara(specialHaftara2.haft);
updateHaftaraSummary = true;
Expand Down
17 changes: 1 addition & 16 deletions src/specialReadings.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,7 @@ export function specialReadings(hd, il, aliyot, reason) {
// console.log(hd.greg().toLocaleDateString(), name, ev.getDesc(), key);
const special = lookupFestival(key);
if (special) {
const shabbatChanukah = getChanukahShabbatKey(ev, key);
if (shabbatChanukah) {
// only for Shabbat Chanukah I or Shabbat Chanukah II. Note
// this section doesn't apply to Shabbat Rosh Chodesh Chanukah; that
// case gets handled below with the mergeAliyotWithSpecial() logic
haft = cloneHaftara(lookupFestival(shabbatChanukah).haft);
specialHaft = true;
reason.haftara = shabbatChanukah;
// Aliyot 1-3 from regular daily reading becomes Maftir
const maftir = aliyot['M'] = clone(special.fullkriyah['1']);
maftir.e = special.fullkriyah['3'].e;
calculateNumVerses(maftir);
reason.M = key;
} else {
handleSpecial(special, key);
}
handleSpecial(special, key);
}
});
if (!haft) {
Expand Down

0 comments on commit e3b4f79

Please sign in to comment.