I managed to convert the Java classes in the above link to JavaScript and use them in openHAB.
For the benefit of others (until Jewish Calendar is supported in Ephemeris…) I’m pasting my jewishCal.js file, which can be placed in one of the directories and loaded the same way TimeUtils.js is loaded.
(function(context) {
'use strict';
var log = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.time_utils");
var LocalDateTime = (context.LocalDateTime === undefined) ? Java.type("java.time.LocalDateTime") : context.LocalDateTime;
//------------------------------------------------
context.getWeekday = function(absDate) { return (absDate % 7); }
var month_list = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
context.getLastDayOfGregorianMonth = function(month, year) {
if ((month == 2) &&
((year % 4) == 0) &&
((year % 400) != 100) &&
((year % 400) != 200) &&
((year % 400) != 300))
return 29;
else
return month_list[month-1];
}
context.absoluteFromGregorianDate = function(locDate) {
var value, m;
if(!(locDate instanceof LocalDateTime)) {
log.error("argument to absoluteFromGregorianDate not in LocalDateTime format");
return null;
}
/* Days so far this month */
value = locDate.getDayOfMonth();
/* Days in prior months this year */
for (m = 1; m < locDate.getMonthValue(); m++) {
value += getLastDayOfGregorianMonth(m, locDate.getYear());
}
/* Days in prior years */
value += (365 * (locDate.getYear()-1));
/* Julian leap days in prior years ... */
value += parseInt((locDate.getYear()-1) / 4);
/* ... minus prior century years ... */
value -= parseInt((locDate.getYear()-1) / 100);
/* ... plus prior years divisible by 400 */
value += parseInt((locDate.getYear()-1) / 400);
return (value);
}
context.gregorianDateFromAbsolute = function(absDate) {
var approx, y, m, day, month, year, temp;
/* Approximation */
approx = parseInt(absDate/366);
/* Search forward from the approximation */
y = approx;
for (;;) {
temp = absoluteFromGregorianDate(LocalDateTime.of(y+1, 1, 1, 0, 0));
if (absDate < temp) break;
y++;
}
year = y;
/* Search forward from January */
m = 1;
for (;;) {
temp = absoluteFromGregorianDate(LocalDateTime.of(year, m, getLastDayOfGregorianMonth(m, year), 0, 0));
if (absDate <= temp) break;
m++;
}
month = m;
/* Calculate the day by subtraction */
temp = absoluteFromGregorianDate(LocalDateTime.of(year, month, 1, 0, 0));
day = absDate-temp+1;
return LocalDateTime.of(year, month, day, 0, 0);
}
context.hebrewLeapYear = function(year) {
if ((((year*7)+1) % 19) < 7)
return true;
else
return false;
}
context.getLastMonthOfJewishYear = function(year) {
if (hebrewLeapYear(year))
return 13;
else
return 12;
}
context.getLastDayOfJewishMonth = function(month, year) {
if ((month == 2) ||
(month == 4) ||
(month == 6) ||
(month == 10) ||
(month == 13))
return 29;
if ((month == 12) && (!hebrewLeapYear(year)))
return 29;
if ((month == 8) && (!longHeshvan(year)))
return 29;
if ((month == 9) && (shortKislev(year)))
return 29;
return 30;
}
context.hebrewCalendarElapsedDays = function(year) {
var value, monthsElapsed, partsElapsed, hoursElapsed;
var day, parts, alternativeDay;
/* Months in complete cycles so far */
value = 235 * parseInt((year-1) / 19);
monthsElapsed = value;
/* Regular months in this cycle */
value = 12 * ((year-1) % 19);
monthsElapsed += value;
/* Leap months this cycle */
value = parseInt(((((year-1) % 19) * 7) + 1) / 19);
monthsElapsed += value;
partsElapsed = (((monthsElapsed % 1080) * 793) + 204);
hoursElapsed = (5 +
(monthsElapsed * 12) +
(parseInt(monthsElapsed / 1080) * 793) +
parseInt(partsElapsed / 1080));
/* Conjunction day */
day = 1 + (29 * monthsElapsed) + parseInt(hoursElapsed/24);
/* Conjunction parts */
parts = ((hoursElapsed % 24) * 1080) +
(partsElapsed % 1080);
/* If new moon is at or after midday, */
if ((parts >= 19440) ||
/* ...or is on a Tuesday... */
(((day % 7) == 2) &&
/* at 9 hours, 204 parts or later */
(parts >= 9924) &&
/* of a common year */
(!hebrewLeapYear(year))) ||
/* ...or is on a Monday at... */
(((day % 7) == 1) &&
/* 15 hours, 589 parts or later... */
(parts >= 16789) &&
/* at the end of a leap year */
(hebrewLeapYear(year-1))))
/* Then postpone Rosh HaShanah one day */
alternativeDay = day+1;
else
alternativeDay = day;
/* If Rosh HaShanah would occur on Sunday, Wednesday, */
/* or Friday */
if (((alternativeDay % 7) == 0) ||
((alternativeDay % 7) == 3) ||
((alternativeDay % 7) == 5))
/* Then postpone it one (more) day and return */
alternativeDay++;
return (alternativeDay);
}
context.daysInHebrewYear = function(year) {
return (hebrewCalendarElapsedDays(year+1) -
hebrewCalendarElapsedDays(year));
}
context.longHeshvan = function(year) {
if ((daysInHebrewYear(year) % 10) == 5)
return true;
else
return false;
}
context.shortKislev = function(year) {
if ((daysInHebrewYear(year) % 10) == 3)
return true;
else
return false;
}
context.absoluteFromJewishDate = function(year, month, day) {
var value, returnValue, m;
/* Days so far this month */
value = day;
returnValue = value;
/* If before Tishri */
if (month < 7) {
/* Then add days in prior months this year before and */
/* after Nisan. */
for (m = 7; m <= getLastMonthOfJewishYear(year); m++) {
value = getLastDayOfJewishMonth(m, year);
returnValue += value;
}
for (m = 1; m < month; m++) {
value = getLastDayOfJewishMonth(m, year);
returnValue += value;
}
} else {
for (m = 7; m < month; m++) {
value = getLastDayOfJewishMonth(m, year);
returnValue += value;
}
}
/* Days in prior years */
value = hebrewCalendarElapsedDays(year);
returnValue += value;
/* Days elapsed before absolute date 1 */
value = 1373429;
returnValue -= value;
return (returnValue);
}
context.jewishDateFromAbsolute = function(absDate) {
var approx, y, m, year, month, day, temp, start;
log.info("In jewishDateFromAbsolute absDate = " + absDate);
/* Approximation */
approx = parseInt((absDate+1373429) / 366);
/* Search forward from the approximation */
y = approx;
for (;;) {
temp = absoluteFromJewishDate(y+1, 7, 1);
if (absDate < temp) break;
y++;
}
year = y;
/* Starting month for search for month */
temp = absoluteFromJewishDate(year, 1, 1);
if (absDate < temp)
start = 7;
else
start = 1;
/* Search forward from either Tishri or Nisan */
m = start;
for (;;) {
temp = absoluteFromJewishDate(year, m, getLastDayOfJewishMonth(m, year));
if (absDate <= temp)
break;
m++;
}
month = m;
/* Calculate the day by subtraction */
temp = absoluteFromJewishDate(year, month, 1);
day = absDate-temp+1;
return [year, month, day];
}
//------------------------------------------------
context.getJewishHolidayForDate = function(gdate, diaspora) {
var absDate = absoluteFromGregorianDate(gdate);
var jdate = jewishDateFromAbsolute(absDate);
var hebYear = jdate[0];
var hebMonth = jdate[1];
var hebDay = jdate[2];
// Holidays in Nisan
if (hebDay == 15 && hebMonth == 1)
return "PESACH";
if (hebDay == 16 && hebMonth == 1 && diaspora)
return "PESACH";
if (hebDay == 21 && hebMonth == 1)
return "PESACH_VII";
if (hebDay == 22 && hebMonth == 1 && diaspora)
return "PESACH_VII";
// Holidays in Sivan
if (hebDay == 6 && hebMonth == 3)
return "SHAVUOT";
if (hebDay == 7 && hebMonth == 3 && diaspora)
return "SHAVUOT";
// Holidays in Tishri
if (hebDay == 1 && hebMonth == 7)
return "ROSH_HASHANA";
if (hebDay == 2 && hebMonth == 7)
return "ROSH_HASHANA";
if (hebDay == 10 && hebMonth == 7)
return "YOM_KIPPUR";
if (hebDay == 15 && hebMonth == 7)
return "SUKKOT";
if (hebDay == 16 && hebMonth == 7 && diaspora)
return "SUKKOT";
if (hebDay == 22 && hebMonth == 7)
return "SIMCHAT_TORAH";
if (hebDay == 23 && hebMonth == 7 && diaspora)
return "SIMCHAT_TORAH";
return 0;
}
context.isJewishHoliday = function(gdate, diaspora) {
return (getJewishHolidayForDate(gdate, diaspora) != 0);
}
})(this);
And the rule example that provides the relevant information:
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.TimeOfDay");
scriptExtension.importPreset("default");
this.Ephemeris = (this.Ephemeris === undefined) ? Java.type("org.openhab.core.model.script.actions.Ephemeris") : this.Ephemeris;
this.ZonedDateTime = (this.ZonedDateTime === undefined) ? Java.type("java.time.ZonedDateTime") : this.ZonedDateTime;
this.LocalDateTime = (this.LocalDateTime === undefined) ? Java.type("java.time.LocalDateTime") : this.LocalDateTime;
this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv("OPENHAB_CONF") : this.OPENHAB_CONF;
load(OPENHAB_CONF+'/automation/lib/javascript/community/jewishCal.js');
logger.info("Is today a holiday? " + isJewishHoliday(LocalDateTime.now().minusDays(7), false));
logger.info("Today is? " + getJewishHolidayForDate(LocalDateTime.of(2021,9,7,0,0), false));
logger.info("Is 5782 a leap year? " + hebrewLeapYear(5782));
This is the first time I’ve written anything in JS, so comments are welcome if improvements are needed.
ToDo:
Open a bug to Ephemeris
Finally update the TimeOfDay DP to support Jewish holidays