Skip to content

Commit

Permalink
Make options.hour12 work as advertised for Candles/Havdalah #378
Browse files Browse the repository at this point in the history
  • Loading branch information
mjradwin committed Dec 27, 2023
1 parent 3a4a1a1 commit 3d08275
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 107 deletions.
6 changes: 3 additions & 3 deletions hebcal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ declare module '@hebcal/core' {
* @param desc - Description (not translated)
*/
export class TimedEvent extends Event {
constructor(date: HDate, desc: string, mask: number, eventTime: Date, location: Location, linkedEvent?: Event);
constructor(date: HDate, desc: string, mask: number, eventTime: Date, location: Location, linkedEvent?: Event, options?: CalOptions);
render(locale?: string): string;
renderBrief(locale?: string): string;
getCategories(): string[];
Expand All @@ -999,11 +999,11 @@ declare module '@hebcal/core' {
readonly linkedEvent?: Event;
}
export class CandleLightingEvent extends TimedEvent {
constructor(date: HDate, mask: number, eventTime: Date, location: Location, linkedEvent?: Event);
constructor(date: HDate, mask: number, eventTime: Date, location: Location, linkedEvent?: Event, options?: CalOptions);
getEmoji(): string;
}
export class HavdalahEvent extends TimedEvent {
constructor(date: HDate, mask: number, eventTime: Date, location: Location, havdalahMins?: number, linkedEvent?: Event);
constructor(date: HDate, mask: number, eventTime: Date, location: Location, havdalahMins?: number, linkedEvent?: Event, options?: CalOptions);
render(locale?: string): string;
renderBrief(locale?: string): string;
getEmoji(): string;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hebcal/core",
"version": "5.0.6",
"version": "5.0.7",
"author": "Michael J. Radwin (https://github.com/mjradwin)",
"contributors": [
"Eyal Schachter (https://github.com/Scimonster)",
Expand Down
45 changes: 26 additions & 19 deletions src/candles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {months} from '@hebcal/hdate';
import {Event, flags} from './event.js';
import {Locale} from './locale.js';
import {reformatTimeStr} from './reformatTimeStr.js';
import {Zmanim} from './zmanim.js';

const FRI = 5;
Expand All @@ -12,11 +13,10 @@ const SAT = 6;
* @param {Event} e
* @param {HDate} hd
* @param {number} dow
* @param {Location} location
* @param {CalOptions} options
* @return {Event}
*/
export function makeCandleEvent(e, hd, dow, location, options) {
export function makeCandleEvent(e, hd, dow, options) {
let havdalahTitle = false;
let useHavdalahOffset = dow === SAT;
let mask = e ? e.getFlags() : flags.LIGHT_CANDLES;
Expand All @@ -36,15 +36,16 @@ export function makeCandleEvent(e, hd, dow, location, options) {
}
// if offset is 0 or undefined, we'll use tzeit time
const offset = useHavdalahOffset ? options.havdalahMins : options.candleLightingMins;
const location = options.location;
const zmanim = new Zmanim(location, hd, options.useElevation);
const time = offset ? zmanim.sunsetOffset(offset, true) : zmanim.tzeit(options.havdalahDeg);
if (isNaN(time.getTime())) {
return null; // no sunset
}
if (havdalahTitle) {
return new HavdalahEvent(hd, mask, time, location, options.havdalahMins, e);
return new HavdalahEvent(hd, mask, time, location, options.havdalahMins, e, options);
} else {
return new CandleLightingEvent(hd, mask, time, location, e);
return new CandleLightingEvent(hd, mask, time, location, e, options);
}
}

Expand All @@ -57,13 +58,16 @@ export class TimedEvent extends Event {
* @param {Date} eventTime
* @param {Location} location
* @param {Event} linkedEvent
* @param {CalOptions} options
*/
constructor(date, desc, mask, eventTime, location, linkedEvent) {
constructor(date, desc, mask, eventTime, location, linkedEvent, options) {
super(date, desc, mask);
this.eventTime = Zmanim.roundTime(eventTime);
this.location = location;
const timeFormat = location.getTimeFormatter();
this.eventTimeStr = Zmanim.formatTime(this.eventTime, timeFormat);
const opts = Object.assign({location}, options);
this.fmtTime = reformatTimeStr(this.eventTimeStr, 'pm', opts);
if (typeof linkedEvent !== 'undefined') {
this.linkedEvent = linkedEvent;
}
Expand All @@ -73,7 +77,7 @@ export class TimedEvent extends Event {
* @return {string}
*/
render(locale) {
return Locale.gettext(this.getDesc(), locale) + ': ' + this.eventTimeStr;
return Locale.gettext(this.getDesc(), locale) + ': ' + this.fmtTime;
}
/**
* Returns translation of "Candle lighting" without the time.
Expand Down Expand Up @@ -110,9 +114,10 @@ export class HavdalahEvent extends TimedEvent {
* @param {Location} location
* @param {number} havdalahMins
* @param {Event} linkedEvent
* @param {CalOptions} options
*/
constructor(date, mask, eventTime, location, havdalahMins, linkedEvent) {
super(date, 'Havdalah', mask, eventTime, location, linkedEvent);
constructor(date, mask, eventTime, location, havdalahMins, linkedEvent, options) {
super(date, 'Havdalah', mask, eventTime, location, linkedEvent, options);
if (havdalahMins) {
this.havdalahMins = havdalahMins;
}
Expand All @@ -122,7 +127,7 @@ export class HavdalahEvent extends TimedEvent {
* @return {string}
*/
render(locale) {
return this.renderBrief(locale) + ': ' + this.eventTimeStr;
return this.renderBrief(locale) + ': ' + this.fmtTime;
}
/**
* Returns translation of "Havdalah" without the time.
Expand Down Expand Up @@ -151,9 +156,10 @@ export class CandleLightingEvent extends TimedEvent {
* @param {Date} eventTime
* @param {Location} location
* @param {Event} linkedEvent
* @param {CalOptions} options
*/
constructor(date, mask, eventTime, location, linkedEvent) {
super(date, 'Candle lighting', mask, eventTime, location, linkedEvent);
constructor(date, mask, eventTime, location, linkedEvent, options) {
super(date, 'Candle lighting', mask, eventTime, location, linkedEvent, options);
}
/** @return {string} */
getEmoji() {
Expand Down Expand Up @@ -181,14 +187,14 @@ export function makeFastStartEnd(ev, options) {
const zmanim = new Zmanim(location, dt, options.useElevation);
if (desc === 'Erev Tish\'a B\'Av') {
const sunset = zmanim.sunset();
ev.startEvent = makeTimedEvent(hd, sunset, 'Fast begins', ev, location);
ev.startEvent = makeTimedEvent(hd, sunset, 'Fast begins', ev, options);
} else if (desc.startsWith('Tish\'a B\'Av')) {
ev.endEvent = makeTimedEvent(hd, zmanim.tzeit(fastEndDeg), 'Fast ends', ev, location);
ev.endEvent = makeTimedEvent(hd, zmanim.tzeit(fastEndDeg), 'Fast ends', ev, options);
} else {
const dawn = zmanim.alotHaShachar();
ev.startEvent = makeTimedEvent(hd, dawn, 'Fast begins', ev, location);
ev.startEvent = makeTimedEvent(hd, dawn, 'Fast begins', ev, options);
if (dt.getDay() !== 5 && !(hd.getDate() === 14 && hd.getMonth() === months.NISAN)) {
ev.endEvent = makeTimedEvent(hd, zmanim.tzeit(fastEndDeg), 'Fast ends', ev, location);
ev.endEvent = makeTimedEvent(hd, zmanim.tzeit(fastEndDeg), 'Fast ends', ev, options);
}
}
return ev;
Expand All @@ -200,14 +206,15 @@ export function makeFastStartEnd(ev, options) {
* @param {Date} time
* @param {string} desc
* @param {Event} ev
* @param {Location} location
* @param {CalOptions} options
* @return {TimedEvent}
*/
function makeTimedEvent(hd, time, desc, ev, location) {
function makeTimedEvent(hd, time, desc, ev, options) {
if (isNaN(time.getTime())) {
return null;
}
return new TimedEvent(hd, desc, ev.getFlags(), time, location, ev);
const location = options.location;
return new TimedEvent(hd, desc, ev.getFlags(), time, location, ev, options);
}


Expand All @@ -224,5 +231,5 @@ export function makeWeekdayChanukahCandleLighting(ev, hd, options) {
const zmanim = new Zmanim(location, hd.greg(), options.useElevation);
const candleLightingTime = zmanim.dusk();
// const candleLightingTime = zmanim.tzeit(4.6667);
return makeTimedEvent(hd, candleLightingTime, ev.getDesc(), ev, location);
return makeTimedEvent(hd, candleLightingTime, ev.getDesc(), ev, options);
}
76 changes: 56 additions & 20 deletions src/candles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ function eventDateDesc(ev) {

test('makeCandleEvent-nosunset', (t) => {
const location = Location.lookup('Helsinki');
const options = {
location,
candleLightingMins: -18,
useElevation: true,
};

const dates = [
[2020, 4, 15],
Expand All @@ -37,10 +42,7 @@ test('makeCandleEvent-nosunset', (t) => {
const events = [];
for (const dt of dates) {
const hd = new HDate(new Date(dt[0], dt[1], dt[2]));
const ev = makeCandleEvent(undefined, hd, hd.getDay(), location, {
candleLightingMins: -18,
useElevation: true,
});
const ev = makeCandleEvent(undefined, hd, hd.getDay(), options);
events.push(ev);
}
const result = events.map(eventDateDesc);
Expand All @@ -56,14 +58,11 @@ test('makeCandleEvent-nosunset', (t) => {
];
t.deepEqual(result, expected);

options.havdalahMins = 72;
const events2 = [];
for (const dt of dates) {
const hd = new HDate(new Date(dt[0], dt[1], dt[2]));
const ev = makeCandleEvent(undefined, hd, hd.getDay(), location, {
candleLightingMins: -18,
havdalahMins: 72,
useElevation: true,
});
const ev = makeCandleEvent(undefined, hd, hd.getDay(), options);
events2.push(ev);
}
const result2 = events2.map(eventDateDesc);
Expand All @@ -88,6 +87,7 @@ test('candles-only-diaspora', (t) => {
location: Location.lookup('Chicago'),
candlelighting: true,
useElevation: true,
hour12: 0,
};
const events = HebrewCalendar.calendar(options);
t.is(events.length, 132);
Expand Down Expand Up @@ -148,7 +148,7 @@ test('havdalah-mins', (t) => {
.filter((ev) => ev.getDesc().startsWith('Havdalah'));
const ev = events[0];
t.is(ev.getFlags(), flags.LIGHT_CANDLES_TZEIS);
t.is(ev.render('en'), 'Havdalah (47 min): 20:02');
t.is(ev.render('en'), 'Havdalah (47 min): 8:02pm');
t.is(ev.getDesc(), 'Havdalah');
t.is(ev.eventTimeStr, '20:02');
const actual = events.slice(1, 5).map(eventTitleDateTime);
Expand Down Expand Up @@ -478,7 +478,7 @@ test('no-chanukah-candles-when-noHolidays', (t) => {
test('renderBrief', (t) => {
const dt = new Date('2020-12-28T20:12:14.987Z');
const hd = new HDate(dt);
const location = new Location(0, 0, false, 'UTC');
const location = new Location(0, 0, false, 'UTC', undefined, 'GB');
const timed = new TimedEvent(hd, 'Foo Bar', 0, dt, location);
const candleLighting = new CandleLightingEvent(hd, flags.LIGHT_CANDLES, dt, location);
const havdalah = new HavdalahEvent(hd, flags.LIGHT_CANDLES_TZEIS, dt, location, 42);
Expand Down Expand Up @@ -508,16 +508,16 @@ test('renderBrief', (t) => {
test('havdalahDeg', (t) => {
const hd = new HDate(new Date(2020, 4, 16));
const dow = hd.getDay();
const location = new Location(0, 0, false, 'UTC');
const location = new Location(0, 0, false, 'UTC', undefined, 'GB');
const events = [
makeCandleEvent(undefined, hd, dow, location, {}),
makeCandleEvent(undefined, hd, dow, location, {havdalahDeg: 6.5}),
makeCandleEvent(undefined, hd, dow, location, {havdalahDeg: 7.0833}),
makeCandleEvent(undefined, hd, dow, location, {havdalahDeg: 7.5}),
makeCandleEvent(undefined, hd, dow, location, {havdalahDeg: 8.5}),
makeCandleEvent(undefined, hd, dow, location, {havdalahMins: 42}),
makeCandleEvent(undefined, hd, dow, location, {havdalahMins: 50}),
makeCandleEvent(undefined, hd, dow, location, {havdalahMins: 72}),
makeCandleEvent(undefined, hd, dow, {location}),
makeCandleEvent(undefined, hd, dow, {location, havdalahDeg: 6.5}),
makeCandleEvent(undefined, hd, dow, {location, havdalahDeg: 7.0833}),
makeCandleEvent(undefined, hd, dow, {location, havdalahDeg: 7.5}),
makeCandleEvent(undefined, hd, dow, {location, havdalahDeg: 8.5}),
makeCandleEvent(undefined, hd, dow, {location, havdalahMins: 42}),
makeCandleEvent(undefined, hd, dow, {location, havdalahMins: 50}),
makeCandleEvent(undefined, hd, dow, {location, havdalahMins: 72}),
];
const results = events.map(eventDateDesc);
const expected = [
Expand Down Expand Up @@ -642,3 +642,39 @@ test('makeFastStartEnd', (t) => {
t.is(startEvent.eventTime.toISOString(), '2023-07-27T00:10:00.000Z');
t.is(startEvent.eventTimeStr, '20:10');
});

test('candles-hour12-true', (t) => {
const events = HebrewCalendar.calendar({
start: new Date(2022, 9, 4),
end: new Date(2022, 9, 5),
noHolidays: true,
location: Location.lookup('Boston'),
candlelighting: true,
useElevation: true,
hour12: true,
});
const actual = events.map(eventDateDesc);
const expected = [
{date: '2022-10-04', desc: 'Candle lighting: 6:03pm'},
{date: '2022-10-05', desc: 'Havdalah: 7:00pm'},
];
t.deepEqual(actual, expected);
});

test('candles-hour12-false', (t) => {
const events = HebrewCalendar.calendar({
start: new Date(2022, 9, 4),
end: new Date(2022, 9, 5),
noHolidays: true,
location: Location.lookup('Boston'),
candlelighting: true,
useElevation: true,
hour12: false,
});
const actual = events.map(eventDateDesc);
const expected = [
{date: '2022-10-04', desc: 'Candle lighting: 18:03'},
{date: '2022-10-05', desc: 'Havdalah: 19:00'},
];
t.deepEqual(actual, expected);
});
31 changes: 4 additions & 27 deletions src/hebcal.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {Locale} from './locale.js';
import {Location} from './location.js';
import {MoladEvent} from './molad.js';
import {OmerEvent} from './omer.js';
import {reformatTimeStr} from './reformatTimeStr.js';
import {tachanun_} from './tachanun.js';
import {Zmanim} from './zmanim.js';

Expand Down Expand Up @@ -429,10 +430,6 @@ const MASK_LIGHT_CANDLES =

const defaultLocation = new Location(0, 0, false, 'UTC');

const hour12cc = {
US: 1, CA: 1, BR: 1, AU: 1, NZ: 1, DO: 1, PR: 1, GR: 1, IN: 1, KR: 1, NP: 1, ZA: 1,
};

/**
* @private
* @param {CalOptions} options
Expand Down Expand Up @@ -689,7 +686,7 @@ export class HebrewCalendar {
evts.push(new MoladEvent(hd, hyear, monNext));
}
if (!candlesEv && options.candlelighting && (dow == FRI || dow == SAT)) {
candlesEv = makeCandleEvent(undefined, hd, dow, location, options);
candlesEv = makeCandleEvent(undefined, hd, dow, options);
if (dow === FRI && candlesEv && sedra) {
candlesEv.memo = sedra.getString(abs);
}
Expand Down Expand Up @@ -857,27 +854,7 @@ export class HebrewCalendar {
* @return {string}
*/
static reformatTimeStr(timeStr, suffix, options) {
if (typeof timeStr !== 'string') throw new TypeError(`Bad timeStr: ${timeStr}`);
const cc = options.location?.cc || (options.il ? 'IL' : 'US');
if (typeof options.hour12 !== 'undefined' && !options.hour12) {
return timeStr;
}
if (!options.hour12 && typeof hour12cc[cc] === 'undefined') {
return timeStr;
}
const hm = timeStr.split(':');
let hour = parseInt(hm[0], 10);
if (hour < 12 && suffix) {
suffix = suffix.replace('p', 'a').replace('P', 'A');
if (hour === 0) {
hour = 12;
}
} else if (hour > 12) {
hour = hour % 12;
} else if (hour === 0) {
hour = '00';
}
return `${hour}:${hm[1]}${suffix}`;
return reformatTimeStr(timeStr, suffix, options);
}

/** @return {string} */
Expand Down Expand Up @@ -977,7 +954,7 @@ function appendHolidayAndRelated(events, ev, options, candlesEv, dow) {
if ((eFlags & options.mask) || (!eFlags && !options.userMask)) {
if (options.candlelighting && eFlags & MASK_LIGHT_CANDLES) {
const hd = ev.getDate();
candlesEv = makeCandleEvent(ev, hd, dow, location, options);
candlesEv = makeCandleEvent(ev, hd, dow, options);
if (eFlags & CHANUKAH_CANDLES && candlesEv && !options.noHolidays) {
const chanukahEv = (dow === FRI || dow === SAT) ? candlesEv :
makeWeekdayChanukahCandleLighting(ev, hd, options);
Expand Down
Loading

0 comments on commit 3d08275

Please sign in to comment.