From b62ceff1c60dcc9aa29c5ea982eb341a2509346f Mon Sep 17 00:00:00 2001 From: "Michael J. Radwin" Date: Mon, 20 Nov 2023 17:21:53 -0800 Subject: [PATCH] Make more zmanim concur with KosherJava --- package-lock.json | 8 ++-- package.json | 2 +- src/candles.spec.js | 70 ++++++++++++++-------------- src/zmanim.js | 108 +++++++++++++++++++++++++++++++------------- src/zmanim.spec.js | 100 ++++++++++++++++++++-------------------- 5 files changed, 166 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94bf3f1..a6fcfad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@babel/core": "^7.23.3", "@babel/preset-env": "^7.23.3", "@babel/register": "^7.22.15", - "@hebcal/noaa": "^0.8.1", + "@hebcal/noaa": "^0.8.2", "@hebcal/solar-calc": "^1.1.2", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", @@ -2140,9 +2140,9 @@ } }, "node_modules/@hebcal/noaa": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@hebcal/noaa/-/noaa-0.8.1.tgz", - "integrity": "sha512-7YBuIWluBS3iq6k0uVjfh8L4KaTSDu6SF0cdMA5Cn/hRwCuheFYv21ivsCf8arSB8lTUIwsVFR0DnjDVXo/r0Q==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@hebcal/noaa/-/noaa-0.8.2.tgz", + "integrity": "sha512-vq4m0am9DPUqMVhYhs7kcb6yJVw1uv6eXSAy2B51wuUCnM7EgMI5FsgONZ0HTdKNoFyYbw1VyrSlxUMlYmkZiA==", "dev": true, "dependencies": { "temporal-polyfill": "^0.1.1" diff --git a/package.json b/package.json index aac6805..c20526f 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@babel/core": "^7.23.3", "@babel/preset-env": "^7.23.3", "@babel/register": "^7.22.15", - "@hebcal/noaa": "^0.8.1", + "@hebcal/noaa": "^0.8.2", "@hebcal/solar-calc": "^1.1.2", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", diff --git a/src/candles.spec.js b/src/candles.spec.js index a9facd9..a9f829e 100644 --- a/src/candles.spec.js +++ b/src/candles.spec.js @@ -38,13 +38,13 @@ test('makeCandleEvent-nosunset', (t) => { } const result = events.map(eventDateDesc); const expected = [ - {date: '2020-05-15', desc: 'Candle lighting: 21:38'}, + {date: '2020-05-15', desc: 'Candle lighting: 21:36'}, {date: '2020-05-16', desc: 'Havdalah: 23:49'}, - {date: '2020-05-22', desc: 'Candle lighting: 21:54'}, + {date: '2020-05-22', desc: 'Candle lighting: 21:52'}, {date: '2020-05-23', desc: 'Havdalah: 00:31'}, - {date: '2020-05-29', desc: 'Candle lighting: 22:08'}, + {date: '2020-05-29', desc: 'Candle lighting: 22:06'}, null, // no tzeit - {date: '2020-06-05', desc: 'Candle lighting: 22:20'}, + {date: '2020-06-05', desc: 'Candle lighting: 22:18'}, null, // no tzeit ]; t.deepEqual(result, expected); @@ -60,14 +60,14 @@ test('makeCandleEvent-nosunset', (t) => { } const result2 = events2.map(eventDateDesc); const expected2 = [ - {date: '2020-05-15', desc: 'Candle lighting: 21:38'}, - {date: '2020-05-16', desc: 'Havdalah (72 min): 23:11'}, - {date: '2020-05-22', desc: 'Candle lighting: 21:54'}, - {date: '2020-05-23', desc: 'Havdalah (72 min): 23:26'}, - {date: '2020-05-29', desc: 'Candle lighting: 22:08'}, - {date: '2020-05-30', desc: 'Havdalah (72 min): 23:41'}, - {date: '2020-06-05', desc: 'Candle lighting: 22:20'}, - {date: '2020-06-06', desc: 'Havdalah (72 min): 23:52'}, + {date: '2020-05-15', desc: 'Candle lighting: 21:36'}, + {date: '2020-05-16', desc: 'Havdalah (72 min): 23:09'}, + {date: '2020-05-22', desc: 'Candle lighting: 21:52'}, + {date: '2020-05-23', desc: 'Havdalah (72 min): 23:25'}, + {date: '2020-05-29', desc: 'Candle lighting: 22:06'}, + {date: '2020-05-30', desc: 'Havdalah (72 min): 23:39'}, + {date: '2020-06-05', desc: 'Candle lighting: 22:18'}, + {date: '2020-06-06', desc: 'Havdalah (72 min): 23:50'}, ]; t.deepEqual(result2, expected2); }); @@ -84,9 +84,9 @@ test('candles-only-diaspora', (t) => { t.is(events.length, 132); const ev0 = events[0]; t.is(Boolean(ev0.getFlags() & flags.LIGHT_CANDLES), true); - t.is(ev0.render('en'), 'Candle lighting: 16:16'); + t.is(ev0.render('en'), 'Candle lighting: 16:13'); t.is(ev0.getDesc(), 'Candle lighting'); - t.is(ev0.eventTimeStr, '16:16'); + t.is(ev0.eventTimeStr, '16:13'); const ev1 = events[1]; t.is(Boolean(ev1.getFlags() & flags.LIGHT_CANDLES_TZEIS), true); t.is(ev1.render('en'), 'Havdalah: 17:18'); @@ -230,23 +230,23 @@ test('candleLightingMins', (t) => { }; const events30 = HebrewCalendar.calendar(options).map(eventTitleDateTime); const expected30 = [ - {dt: '2020-05-01T18:52:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-08T18:57:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-15T19:02:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-22T19:07:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-01T18:51:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-08T18:56:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-15T19:01:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-22T19:06:00+03:00', desc: 'Candle lighting'}, {dt: '2020-05-28T19:10:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-29T19:11:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-29T19:10:00+03:00', desc: 'Candle lighting'}, ]; t.deepEqual(events30, expected30); delete options.candleLightingMins; const events18 = HebrewCalendar.calendar(options).map(eventTitleDateTime); const expected18 = [ - {dt: '2020-05-01T19:04:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-08T19:09:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-15T19:14:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-22T19:19:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-01T19:03:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-08T19:08:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-15T19:13:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-22T19:18:00+03:00', desc: 'Candle lighting'}, {dt: '2020-05-28T19:22:00+03:00', desc: 'Candle lighting'}, - {dt: '2020-05-29T19:23:00+03:00', desc: 'Candle lighting'}, + {dt: '2020-05-29T19:22:00+03:00', desc: 'Candle lighting'}, ]; t.deepEqual(events18, expected18); }); @@ -267,16 +267,16 @@ test('jerusalem40', (t) => { const items = candleEvents.map(eventTitleDateTime); const expected = [ {dt: '2020-01-03T16:07:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-01-10T16:13:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-01-10T16:12:00+02:00', desc: 'Candle lighting'}, {dt: '2020-01-17T16:19:00+02:00', desc: 'Candle lighting'}, {dt: '2020-01-24T16:25:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-01-31T16:32:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-01-31T16:31:00+02:00', desc: 'Candle lighting'}, {dt: '2020-02-07T16:38:00+02:00', desc: 'Candle lighting'}, {dt: '2020-02-14T16:44:00+02:00', desc: 'Candle lighting'}, {dt: '2020-02-21T16:50:00+02:00', desc: 'Candle lighting'}, {dt: '2020-02-28T16:55:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-03-06T17:01:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-03-13T17:06:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-06T17:00:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-13T17:05:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-20T17:10:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-27T18:15:00+03:00', desc: 'Candle lighting'}, ]; @@ -295,8 +295,8 @@ test('jerusalem31', (t) => { const candleEvents = events.filter((ev) => ev.getDesc() === 'Candle lighting'); const items = candleEvents.map(eventTitleDateTime); const expected = [ - {dt: '2020-03-06T17:10:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-03-13T17:15:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-06T17:09:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-13T17:14:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-20T17:19:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-27T18:24:00+03:00', desc: 'Candle lighting'}, ]; @@ -315,8 +315,8 @@ test('jerusalem18-forced-to-40', (t) => { const candleEvents = events.filter((ev) => ev.getDesc() === 'Candle lighting'); const items = candleEvents.map(eventTitleDateTime); const expected = [ - {dt: '2020-03-06T17:01:00+02:00', desc: 'Candle lighting'}, - {dt: '2020-03-13T17:06:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-06T17:00:00+02:00', desc: 'Candle lighting'}, + {dt: '2020-03-13T17:05:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-20T17:10:00+02:00', desc: 'Candle lighting'}, {dt: '2020-03-27T18:15:00+03:00', desc: 'Candle lighting'}, ]; @@ -455,9 +455,9 @@ test('no-chanukah-candles-when-noHolidays', (t) => { }; const events = HebrewCalendar.calendar(options); const expected = [ - {dt: '2020-12-11T16:01:00-08:00', desc: 'Candle lighting'}, + {dt: '2020-12-11T15:59:00-08:00', desc: 'Candle lighting'}, {dt: '2020-12-12T17:10:00-08:00', desc: 'Havdalah'}, - {dt: '2020-12-18T16:02:00-08:00', desc: 'Candle lighting'}, + {dt: '2020-12-18T16:01:00-08:00', desc: 'Candle lighting'}, {dt: '2020-12-19T17:12:00-08:00', desc: 'Havdalah'}, ]; const actual = events.map(eventTitleDateTime); @@ -585,7 +585,7 @@ test('yk-candles-only', (t) => { }); const actual = events.map(eventTitleDateTime); const expected = [ - {dt: '2022-10-04T18:03:00-04:00', desc: 'Candle lighting'}, + {dt: '2022-10-04T18:02:00-04:00', desc: 'Candle lighting'}, {dt: '2022-10-05T19:00:00-04:00', desc: 'Havdalah'}, ]; t.deepEqual(actual, expected); diff --git a/src/zmanim.js b/src/zmanim.js index 9058b95..e145435 100644 --- a/src/zmanim.js +++ b/src/zmanim.js @@ -18,6 +18,20 @@ function pad2(number) { return String(number); } +/** + * @private + * @param {Temporal.ZonedDateTime} zdt + * @return {Date} + */ +function zdtToDate(zdt) { + if (zdt === null) { + return new Date(NaN); + } + const res = new Date(zdt.epochMilliseconds); + res.setMilliseconds(0); + return res; +} + /** * @typedef {Object} ZmanimTimesResult * @property {Date} dawn @@ -140,12 +154,7 @@ export class Zmanim { const offsetZenith = 90 + angle; const zdt = rising ? this.noaa.getSunriseOffsetByDegrees(offsetZenith) : this.noaa.getSunsetOffsetByDegrees(offsetZenith); - if (zdt === null) { - return new Date(NaN); - } - const res = new Date(zdt.epochMilliseconds); - res.setMilliseconds(0); - return res; + return zdtToDate(zdt); } return this.sun.timeAtAngle(angle, rising); } @@ -156,12 +165,18 @@ export class Zmanim { sunrise() { if (this.noaa) { const zdt = this.noaa.getSunrise(); - if (zdt === null) { - return new Date(NaN); - } - const res = new Date(zdt.epochMilliseconds); - res.setMilliseconds(0); - return res; + return zdtToDate(zdt); + } + return this.sun.timeAtAngle(0.833333, true); + } + /** + * Upper edge of the Sun appears over the eastern horizon in the morning (0.833° above horizon) + * @return {Date} + */ + seaLevelSunrise() { + if (this.noaa) { + const zdt = this.noaa.getSeaLevelSunrise(); + return zdtToDate(zdt); } return this.sun.timeAtAngle(0.833333, true); } @@ -172,12 +187,18 @@ export class Zmanim { sunset() { if (this.noaa) { const zdt = this.noaa.getSunset(); - if (zdt === null) { - return new Date(NaN); - } - const res = new Date(zdt.epochMilliseconds); - res.setMilliseconds(0); - return res; + return zdtToDate(zdt); + } + return this.sun.timeAtAngle(0.833333, false); + } + /** + * When the upper edge of the Sun disappears below the horizon (0.833° below horizon) + * @return {Date} + */ + seaLevelSunset() { + if (this.noaa) { + const zdt = this.noaa.getSeaLevelSunset(); + return zdtToDate(zdt); } return this.sun.timeAtAngle(0.833333, false); } @@ -188,12 +209,7 @@ export class Zmanim { dawn() { if (this.noaa) { const zdt = this.noaa.getBeginCivilTwilight(); - if (zdt === null) { - return new Date(NaN); - } - const res = new Date(zdt.epochMilliseconds); - res.setMilliseconds(0); - return res; + return zdtToDate(zdt); } return this.sun.timeAtAngle(6, true); } @@ -204,12 +220,7 @@ export class Zmanim { dusk() { if (this.noaa) { const zdt = this.noaa.getEndCivilTwilight(); - if (zdt === null) { - return new Date(NaN); - } - const res = new Date(zdt.epochMilliseconds); - res.setMilliseconds(0); - return res; + return zdtToDate(zdt); } return this.sun.timeAtAngle(6, false); } @@ -250,6 +261,10 @@ export class Zmanim { * @return {Date} */ chatzot() { + if (this.noaa) { + const zdt = this.noaa.getSunTransit(); + return zdtToDate(zdt); + } return this.hourOffset(6); } /** @@ -280,11 +295,28 @@ export class Zmanim { misheyakirMachmir() { return this.timeAtAngle(10.2, true); } + /** + * Utility method for using elevation-aware sunrise/sunset + * @private + * @param {number} hours + * @return {Date} + */ + getShaahZmanisBasedZman(hours) { + const startOfDay = this.noaa.getSunrise(); + const endOfDay = this.noaa.getSunset(); + const temporalHour = this.noaa.getTemporalHour(startOfDay, endOfDay); + const offset = Math.round(temporalHour * hours); + const zdt = NOAACalculator.getTimeOffset(startOfDay, offset); + return zdtToDate(zdt); + } /** * Latest Shema (Gra); Sunrise plus 3 halachic hours, according to the Gra * @return {Date} */ sofZmanShma() { // Gra + if (this.noaa) { + return this.getShaahZmanisBasedZman(3); + } return this.hourOffset(3); } /** @@ -292,6 +324,9 @@ export class Zmanim { * @return {Date} */ sofZmanTfilla() { // Gra + if (this.noaa) { + return this.getShaahZmanisBasedZman(4); + } return this.hourOffset(4); } /** @@ -319,6 +354,9 @@ export class Zmanim { * @return {Date} */ minchaGedola() { + if (this.noaa) { + return this.getShaahZmanisBasedZman(6.5); + } return this.hourOffset(6.5); } /** @@ -326,6 +364,9 @@ export class Zmanim { * @return {Date} */ minchaKetana() { + if (this.noaa) { + return this.getShaahZmanisBasedZman(9.5); + } return this.hourOffset(9.5); } /** @@ -333,6 +374,9 @@ export class Zmanim { * @return {Date} */ plagHaMincha() { + if (this.noaa) { + return this.getShaahZmanisBasedZman(10.75); + } return this.hourOffset(10.75); } /** @@ -428,7 +472,7 @@ export class Zmanim { * @return {Date} */ sunriseOffset(offset, roundMinute=true) { - const sunrise = this.sunrise(); + const sunrise = this.seaLevelSunrise(); if (isNaN(sunrise.getTime())) { return sunrise; } @@ -449,7 +493,7 @@ export class Zmanim { * @return {Date} */ sunsetOffset(offset, roundMinute=true) { - const sunset = this.sunset(); + const sunset = this.seaLevelSunset(); if (isNaN(sunset.getTime())) { return sunset; } diff --git a/src/zmanim.spec.js b/src/zmanim.spec.js index f772111..7ca10fd 100644 --- a/src/zmanim.spec.js +++ b/src/zmanim.spec.js @@ -108,51 +108,6 @@ test('zmanim-tlv', (t) => { t.deepEqual(actual, expected); }); -/* -{ - "metadata": { - "date": "2020-06-02", - "type": "com.kosherjava.zmanim.ZmanimCalendar", - "algorithm": "US National Oceanic and Atmospheric Administration Algorithm", - "location": "Denver", - "latitude": "39.73915", - "longitude": "-104.9847", - "elevation": "1636.0", - "timeZoneName": "Mountain Daylight Time", - "timeZoneID": "America/Denver", - "timeZoneOffset": "-6.0" - }, - "BasicZmanim": { - "BeginAstronomicalTwilight": "2020-06-02T03:34:54-06:00", - "AlosHashachar": "2020-06-02T03:50:25-06:00", - "BeginNauticalTwilight": "2020-06-02T04:21:01-06:00", - "Alos72": "2020-06-02T04:21:22-06:00", - "BeginCivilTwilight": "2020-06-02T05:01:19-06:00", - "Sunrise": "2020-06-02T05:25:29-06:00", - "SeaLevelSunrise": "2020-06-02T05:33:22-06:00", - "SofZmanShmaMGA": "2020-06-02T08:39:48-06:00", - "SofZmanShmaGRA": "2020-06-02T09:15:48-06:00", - "SofZmanTfilaMGA": "2020-06-02T10:05:56-06:00", - "SofZmanTfilaGRA": "2020-06-02T10:29:56-06:00", - "Chatzos": "2020-06-02T12:58:13-06:00", - "SunTransit": "2020-06-02T12:58:13-06:00", - "MinchaGedola": "2020-06-02T13:35:18-06:00", - "MinchaKetana": "2020-06-02T17:17:44-06:00", - "PlagHamincha": "2020-06-02T18:50:25-06:00", - "CandleLighting": "2020-06-02T20:05:05-06:00", - "SeaLevelSunset": "2020-06-02T20:23:05-06:00", - "Sunset": "2020-06-02T20:30:59-06:00", - "EndCivilTwilight": "2020-06-02T20:55:12-06:00", - "Tzais": "2020-06-02T21:11:32-06:00", - "Tzais72": "2020-06-02T21:35:05-06:00", - "EndNauticalTwilight": "2020-06-02T21:35:36-06:00", - "EndAstronomicalTwilight": "2020-06-02T22:21:56-06:00", - "ShaahZmanisGra": "PT1H14M8.636S", - "TemporalHour": "PT1H14M8.636S", - "ShaahZmanisMGA": "PT1H26M8.636S" - } -} -*/ test('zmanim-denver', (t) => { const zman = makeZmanWithElevation(); const tzid = 'America/Denver'; @@ -170,23 +125,40 @@ test('zmanim-denver', (t) => { const expected = { gregEve: '06/04/2020, 20:32:19', chatzotNight: '06/05/2020, 24:58:24', + // "AlosHashachar": "2020-06-05T03:48:37-06:00", alotHaShachar: '06/05/2020, 03:48:37', misheyakir: '06/05/2020, 04:23:08', misheyakirMachmir: '06/05/2020, 04:32:14', + // "BeginCivilTwilight": "2020-06-05T05:00:12-06:00", dawn: '06/05/2020, 05:00:12', + // "Sunrise": "2020-06-05T05:24:30-06:00", sunrise: '06/05/2020, 05:24:30', + // "SeaLevelSunrise": "2020-06-05T05:32:26-06:00", + seaLevelSunrise: '06/05/2020, 05:32:26', neitzHaChama: '06/05/2020, 05:24:30', - sofZmanShma: '06/05/2020, 09:11:36', - sofZmanShmaMGA: '06/05/2020, 08:35:36', + // @TODO "SofZmanShmaGRA": "2020-06-05T09:15:34-06:00", + sofZmanShma: '06/05/2020, 09:11:37', + // "SofZmanShmaMGA": "2020-06-05T08:39:34-06:00", + sofZmanShmaMGA: '06/05/2020, 08:39:34', + // @TODO "SofZmanTfilaGRA": "2020-06-05T10:29:57-06:00", sofZmanTfilla: '06/05/2020, 10:27:19', - sofZmanTfillaMGA: '06/05/2020, 10:03:19', + // "SofZmanTfilaMGA": "2020-06-05T10:05:57-06:00", + sofZmanTfillaMGA: '06/05/2020, 10:05:57', + // "Chatzos": "2020-06-05T12:58:43-06:00", chatzot: '06/05/2020, 12:58:43', - minchaGedola: '06/05/2020, 13:36:34', - minchaKetana: '06/05/2020, 17:23:41', + // @TODO "MinchaGedola": "2020-06-05T13:35:55-06:00", + minchaGedola: '06/05/2020, 13:36:35', + // @TODO "MinchaKetana": "2020-06-05T17:19:04-06:00", + minchaKetana: '06/05/2020, 17:23:42', + // @TODO "PlagHamincha": "2020-06-05T18:52:02-06:00", plagHaMincha: '06/05/2020, 18:58:19', + seaLevelSunset: '06/05/2020, 20:25:01', + // "Sunset": "2020-06-05T20:32:57-06:00", sunset: '06/05/2020, 20:32:57', shkiah: '06/05/2020, 20:32:57', + // "EndCivilTwilight": "2020-06-05T20:57:18-06:00" dusk: '06/05/2020, 20:57:18', + // "Tzais": "2020-06-05T21:13:45-06:00" tzeit: '06/05/2020, 21:13:45', }; @@ -199,6 +171,9 @@ test('zmanim-denver', (t) => { t.is(Math.round(zman.hourMins()), 76); t.is(Math.round(zman.nightHourMins()), 44); + + // "Tzais72": "2020-06-05T21:37:01-06:00" + t.is(f.format(zman.sunsetOffset(72, false)), '06/05/2020, 21:37:01'); }); test('suntime', (t) => { @@ -471,3 +446,28 @@ test('timeAtAngle', (t) => { t.is(f.format(zman.timeAtAngle(3.7, false)), '20:40:59'); t.is(f.format(zman.timeAtAngle(11.5, false)), '21:35:53'); }); + +test('jlem-sunset', (t) => { + const latitude = 31.76904; + const longitude = 35.21633; + const elevtion = 786; + const tzid = 'Asia/Jerusalem'; + const f = new Intl.DateTimeFormat('en-US', { + timeZone: tzid, + hour: 'numeric', + minute: 'numeric', + hour12: false, + }); + const toTest = [ + [new Date(2022, 8, 26), '18:35'], // Rosh Hashannah 9/26/2022 + [new Date(2022, 11, 19), '16:43'], // Hannukah 1 12/19/2022 + [new Date(2023, 3, 6), '19:06'], // Pesach 4/6/2023 + [new Date(2023, 5, 26), '19:53'], // Tisha B'Av 6/26/2023 + ]; + for (const [dt, expected] of toTest) { + const zman = new Zmanim(dt, latitude, longitude, elevtion, tzid); + const sunset = zman.sunset(); + const actual = f.format(sunset); + t.is(actual, expected, dt.toDateString()); + } +});