🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

kenat

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kenat - npm Package Compare versions

Comparing version
2.2.0
to
2.3.0
+156
-45
examples/vanilla/fasting.html

@@ -9,3 +9,8 @@ <!DOCTYPE html>

<style>
html,
body {
height: 100%;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

@@ -26,2 +31,3 @@ background-color: #f9f9f9;

overflow: hidden;
height: calc(100vh - 2rem);
}

@@ -31,4 +37,7 @@

flex: 1 1 0%;
padding: 2.5rem 2rem 2rem 2rem;
padding: 2rem 2rem;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}

@@ -44,2 +53,16 @@

#calendar-container .header-controls {
position: sticky;
top: 0;
background: #fff;
padding: 0.5rem 0;
z-index: 2;
}
#calendar {
flex: 1;
overflow: auto;
padding-bottom: 1rem;
}
#fasting-sidebar {

@@ -54,2 +77,3 @@ width: 300px;

min-height: 100%;
overflow-y: auto;
}

@@ -81,2 +105,4 @@

width: 100%;
overflow-y: auto;
max-height: calc(100% - 120px);
}

@@ -96,2 +122,14 @@

#fasting-list li .fast-title {
font-weight: 600;
display: block;
margin-bottom: 4px;
}
#fasting-list li .fast-desc {
font-size: 0.9rem;
color: #3e4b5c;
line-height: 1.3;
}
#fasting-list li:hover {

@@ -110,3 +148,4 @@ background: #e3eeff;

background-color: rgba(76, 175, 80, 0.13) !important;
box-shadow: 0 0 0 2px #4a2d7b33;
box-shadow: inset 0 0 0 2px #4a2d7b33;
border-radius: 12px;
}

@@ -121,4 +160,4 @@

overflow: hidden;
width: 95%;
max-width: 600px;
width: 100%;
max-width: 720px;
}

@@ -128,6 +167,6 @@

td {
border: 1px solid #e0e0e0;
padding: 0.25rem;
width: 48px;
height: 48px;
border: 1px solid #eef0f3;
padding: 0.35rem;
width: 52px;
height: 56px;
vertical-align: top;

@@ -139,13 +178,34 @@ position: relative;

th {
background-color: #f5f5f5;
font-weight: 600;
background: linear-gradient(180deg, #f6f7fb 0%, #eef1f7 100%);
font-weight: 700;
border-radius: 12px 12px 0 0;
position: sticky;
top: 0;
z-index: 1;
}
.today {
background-color: #fffde7;
border: 2px solid #ffc107;
font-weight: bold;
background-color: #fffbe6;
border: 2px solid #ffd54f;
font-weight: 700;
}
td strong {
font-weight: 700;
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border-radius: 50%;
}
.today strong {
background-color: #ffecb3;
}
td:hover {
background-color: #f7faff;
}
td.event-day {

@@ -156,11 +216,11 @@ cursor: pointer;

.official-holiday {
background-color: #e3f2fd;
background-color: #e8f3ff;
}
.christian-holiday {
background-color: #ebf5ff;
background-color: #eef7ff;
}
.jummah-day {
background-color: #e8f5e9;
background-color: #e9f7ef;
}

@@ -239,4 +299,11 @@

background-color: #fff;
transition: all 0.15s ease-in-out;
}
button:hover,
select:hover {
border-color: #9fb3ff;
box-shadow: 0 0 0 3px #9fb3ff22;
}
.modal {

@@ -316,4 +383,4 @@ display: none;

import Kenat, { MonthGrid, HolidayTags, toArabic } from '../../src/index.js';
import { monthNames } from '../../src/constants.js';
import { getFastingPeriod } from '../../src/fasting.js';
import { monthNames, FastingKeys } from '../../src/constants.js';
import { getFastingPeriod, getFastingInfo, getFastingDays } from '../../src/fasting.js';

@@ -329,2 +396,3 @@ // State variables

let activeFastingRange = null;
let activeFastingKey = null;

@@ -402,2 +470,9 @@ const initialDate = new Kenat().getEthiopian();

}
if (!activeFastingRange && activeFastingKey === FastingKeys.TSOME_DIHENET) {
const fdays = getFastingDays(FastingKeys.TSOME_DIHENET, yearForComparison, month);
if (fdays.includes(item.ethiopian.day)) {
dayClasses.push('fasting-day-marker');
inlineStyle = `style="background-color: #ffe0b2"`;
}
}
const holidaysText = hasEvents ? holidaysForDay(item) : '';

@@ -435,18 +510,44 @@ const eventAttr = hasEvents ? `data-day-index="${i + j}"` : '';

const fastingPeriods = [
{ key: 'ABIY_TSOME', name: 'The Great Lent (Hudade)', color: '#fff59d' },
{ key: 'TSOME_NEBIYAT', name: 'Fast of the Prophets', color: '#b3e5fc' },
{ key: 'TSOME_HAWARYAT', name: 'Fast of the Apostles', color: '#c8e6c9' },
{ key: 'NINEVEH', name: 'Fast of Nineveh', color: '#ffccbc' },
{ key: 'RAMADAN', name: 'Ramadan', color: '#d1c4e9' }
];
// Color palette per fasting key
const FAST_COLORS = {
[FastingKeys.ABIY_TSOME]: '#fff59d',
[FastingKeys.TSOME_NEBIYAT]: '#b3e5fc',
[FastingKeys.TSOME_HAWARYAT]: '#c8e6c9',
[FastingKeys.NINEVEH]: '#ffccbc',
[FastingKeys.RAMADAN]: '#d1c4e9',
[FastingKeys.FILSETA]: '#f8bbd0',
[FastingKeys.TSOME_DIHENET]: '#ffe0b2',
};
function buildFastingList() {
const keys = [
FastingKeys.ABIY_TSOME,
FastingKeys.TSOME_NEBIYAT,
FastingKeys.TSOME_HAWARYAT,
FastingKeys.NINEVEH,
FastingKeys.RAMADAN,
FastingKeys.FILSETA,
FastingKeys.TSOME_DIHENET,
];
return keys.map(key => {
const info = getFastingInfo(key, currentYear, { lang: weekdayLang });
return {
key,
name: info?.name || key,
description: info?.description || '',
color: FAST_COLORS[key] || '#e0e0e0',
};
});
}
function renderFastingSidebar() {
const list = document.getElementById('fasting-list');
list.innerHTML = '';
const fastingPeriods = buildFastingList();
fastingPeriods.forEach(fast => {
const li = document.createElement('li');
li.textContent = fast.name;
const shortDesc = fast.description || '';
li.innerHTML = `<span class="fast-title">${fast.name}</span>${shortDesc ? `<div class="fast-desc">${shortDesc}</div>` : ''}`;
li.style.borderLeftColor = fast.color;
if (activeFastingRange && activeFastingRange.key === fast.key) {
if ((activeFastingRange && activeFastingRange.key === fast.key) || (activeFastingKey === fast.key)) {
li.classList.add('active');

@@ -456,20 +557,27 @@ }

const yearToSearch = currentYear;
const period = getFastingPeriod(fast.key, yearToSearch);
if (period) {
if (activeFastingRange && activeFastingRange.key === fast.key) {
if (fast.key === FastingKeys.TSOME_DIHENET) {
activeFastingKey = (activeFastingKey === FastingKeys.TSOME_DIHENET) ? null : FastingKeys.TSOME_DIHENET;
activeFastingRange = null;
} else {
const period = getFastingPeriod(fast.key, yearToSearch);
if (period) {
if (activeFastingRange && activeFastingRange.key === fast.key) {
activeFastingRange = null;
activeFastingKey = null;
} else {
activeFastingRange = {
key: fast.key,
start: new Kenat(period.start),
end: new Kenat(period.end),
color: fast.color
};
activeFastingKey = null;
currentYear = activeFastingRange.start.getEthiopian().year;
currentMonth = activeFastingRange.start.getEthiopian().month;
}
} else {
alert(`Fasting period for ${fast.name} not found in ${yearToSearch} E.C.`);
activeFastingRange = null;
} else {
activeFastingRange = {
key: fast.key,
start: new Kenat(period.start),
end: new Kenat(period.end),
color: fast.color
};
currentYear = activeFastingRange.start.getEthiopian().year;
currentMonth = activeFastingRange.start.getEthiopian().month;
activeFastingKey = null;
}
} else {
alert(`Fasting period for ${fast.name} not found in ${yearToSearch} E.C.`);
activeFastingRange = null;
}

@@ -499,3 +607,5 @@ rerender();

const days = Kenat.generateDateRange(activeFastingRange.start, activeFastingRange.end).length;
const fastName = fastingPeriods.find(f => f.key === activeFastingRange.key).name;
const selectedInfo = getFastingInfo(activeFastingRange.key, start.year, { lang: weekdayLang });
const fastName = selectedInfo?.name || activeFastingRange.key;
const fastDesc = selectedInfo?.description || '';
// Use a colored dot for the fast color, but keep text readable

@@ -505,3 +615,4 @@ infoBox.innerHTML = `<strong>Selected Fast:</strong> <span style="display:inline-flex;align-items:center;"><span style="display:inline-block;width:14px;height:14px;border-radius:50%;background:${activeFastingRange.color};margin-right:7px;border:1px solid #ccc;"></span><span style="color:#2d3a4a;">${fastName}</span></span><br>

<strong>End:</strong> ${end.month}/${end.day}/${end.year}<br>
<strong>Days:</strong> ${days}`;
<strong>Days:</strong> ${days}
${fastDesc ? `<hr><div>${fastDesc}</div>` : ''}`;
infoBox.style.display = 'block';

@@ -508,0 +619,0 @@ } else {

{
"name": "kenat",
"version": "2.2.0",
"version": "2.3.0",
"description": "A JavaScript library for the Ethiopian calendar with date and time support.",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -335,1 +335,72 @@ export const monthNames = {

};
// Canonical fasting keys and list to avoid hardcoded strings
export const FastingKeys = {
ABIY_TSOME: 'ABIY_TSOME',
TSOME_HAWARYAT: 'TSOME_HAWARYAT',
TSOME_NEBIYAT: 'TSOME_NEBIYAT',
NINEVEH: 'NINEVEH',
FILSETA: 'FILSETA',
RAMADAN: 'RAMADAN',
TSOME_DIHENET: 'TSOME_DIHENET',
};
// Fasting metadata similar to holidayInfo, with i18n-friendly names and descriptions
export const fastingInfo = {
ABIY_TSOME: {
name: { amharic: "ዐቢይ ጾም (ሁዳዴ)", english: "Great Lent (Abiy Tsome)" },
description: {
amharic: "ከፋሲካ በፊት የሚጾም 55 ቀናት የጾም ወቅት ነው።",
english: "A 55-day fasting season observed before Easter in the Ethiopian Orthodox Church.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
NINEVEH: {
name: { amharic: "ጾመ ነነዌ", english: "Fast of Nineveh" },
description: {
amharic: "የነነዌ ሰዎች ለሰሩት ሐጢያት የእግዚአብሔርን ምህረትና ይቅርታ ለመጠየቅ የፆሙትን ለማስታወስ ነው።",
english: "A three-day fast commemorating the repentance of the people of Nineveh.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
TSOME_HAWARYAT: {
name: { amharic: "ጾመ ሐዋርያት", english: "Apostles' Fast (Tsome Hawaryat)" },
description: {
amharic: "ክርስቶስ ከሙታን ተለይቶ ከተነሳ በሃላ ቅዱሳን ሀዋርያት ሰማያዊ ተልእኮ ስለተሰማቸው ከ50ቀን በሃላ በበአለ ጰራቅሊጦስ ማግስት መፆም ጀመሩ።",
english: "Begins after Pentecost and lasts until Hamle 4 in the Ethiopian calendar.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
TSOME_NEBIYAT: {
name: { amharic: "የገና-ፆም (ጾመ ነቢያት)", english: "Fast of the Prophets (Tsome Nebiyat)" },
description: {
amharic: "ከህዳር 15 እስከ ታሕሳስ 28 ድረስ የሚጠብቅ የቋሚ ጾም ነው።",
english: "A fixed fast observed from Hidar 15 to Tahsas 28.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
FILSETA: {
name: { amharic: "ፍልሰታ", english: "Filseta" },
description: {
amharic: "ፆመ ፍልሰታ /ከነሐሴ 1 እስከ ነሐሴ 14 ቀን/ የእመቤታችን ዕረፍት፣ ትንሣኤና ዕርገቷ የሚታሰብበት ወቅት ነው፡፡",
english: "A fixed 14-day fast (Nehase 1–14) commemorating the Dormition, Resurrection, and Assumption of the Virgin Mary; a season of prayer and intercession.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
RAMADAN: {
name: { amharic: "ረመዳን", english: "Ramadan" },
description: {
amharic: "ረመዳን በሂጅራ አቆጣጠር ዘጠነኛው ወር እና በሙስሊሞች ዘንድ የጾም፣ ጸሎት ወር ነው",
english: "The Islamic month of fasting from dawn to sunset; a time of prayer, reflection, and charity.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.MUSLIM],
},
TSOME_DIHENET: {
name: { amharic: "ጾመ ድኅነት (ረቡእ·ዓርብ)", english: "Fast of Salvation (Wed & Fri)" },
description: {
amharic: "ከበአለ ሀምሳ ውጭ በሳምንት ረቡእ እና አርብ የምንፆመው ነው።",
english: "A fast observed on Wednesdays and Fridays outside of the 50 days after Easter.",
},
tags: [HolidayTags.RELIGIOUS, HolidayTags.CHRISTIAN],
},
};
import { getBahireHasab } from './bahireHasab.js';
import { findHijriMonthRanges } from './holidays.js';
import { addDays } from './dayArithmetic.js';
import { addDays, diffInDays } from './dayArithmetic.js';
import { fastingInfo, FastingKeys } from './constants.js';
import { getWeekday, getEthiopianDaysInMonth, validateNumericInputs, validateEthiopianDateObject } from './utils.js';

@@ -15,3 +17,3 @@ /**

switch (fastKey) {
case 'ABIY_TSOME': {
case FastingKeys.ABIY_TSOME: {
const start = bh.movableFeasts.abiyTsome?.ethiopian;

@@ -25,3 +27,3 @@ const end = bh.movableFeasts.siklet?.ethiopian;

case 'TSOME_HAWARYAT': {
case FastingKeys.TSOME_HAWARYAT: {
const start = bh.movableFeasts.tsomeHawaryat?.ethiopian;

@@ -35,3 +37,3 @@ const end = { year: ethiopianYear, month: 11, day: 4 };

case 'NINEVEH': {
case FastingKeys.NINEVEH: {
const start = bh.movableFeasts.nineveh?.ethiopian;

@@ -45,3 +47,3 @@ if (start) {

case 'TSOME_NEBIYAT': {
case FastingKeys.TSOME_NEBIYAT: {
const start = { year: ethiopianYear, month: 3, day: 15 };

@@ -52,3 +54,10 @@ const end = { year: ethiopianYear, month: 4, day: 28 };

case 'RAMADAN': {
case FastingKeys.FILSETA: {
// Nehase 1 to Nehase 14
const start = { year: ethiopianYear, month: 12, day: 1 };
const end = { year: ethiopianYear, month: 12, day: 14 };
return { start, end };
}
case FastingKeys.RAMADAN: {
const ranges = findHijriMonthRanges(ethiopianYear, 9);

@@ -62,1 +71,114 @@ return ranges.length > 0 ? ranges[0] : null;

}
/**
* Returns fasting information (names, descriptions, period) for a given fast and year.
* @param {'ABIY_TSOME'|'TSOME_HAWARYAT'|'TSOME_NEBIYAT'|'NINEVEH'|'RAMADAN'} fastKey
* @param {number} ethiopianYear
* @param {{lang?: 'amharic'|'english'}} options
* @returns {{ key: string, name: string, description: string, period: { start: object, end: object } } | null}
*/
export function getFastingInfo(fastKey, ethiopianYear, options = {}) {
validateNumericInputs('getFastingInfo', { ethiopianYear });
const { lang = 'amharic' } = options;
const info = fastingInfo[fastKey];
if (!info) return null;
const name = info?.name?.[lang] || info?.name?.english;
const description = info?.description?.[lang] || info?.description?.english;
// TSOME_DIHENET is a weekly fast (Wed/Fri) with an exception; it doesn't have a single contiguous period.
if (fastKey === FastingKeys.TSOME_DIHENET) {
return {
key: fastKey,
name,
description,
tags: info.tags,
period: null,
};
}
const period = getFastingPeriod(fastKey, ethiopianYear);
if (!period) return null;
return {
key: fastKey,
name,
description,
tags: info.tags,
period,
};
}
/**
* Checks if a given Ethiopian date is an Orthodox weekly fasting day (Tsome Dihnet).
* Rules:
* - Fasting occurs every Wednesday and Friday.
* - Exception: for the 50 days after Easter (Fasika) up to and including Pentecost (Paraclete),
* Wednesdays and Fridays are NOT considered fasting days.
*
* @param {{year:number, month:number, day:number}} etDate - Ethiopian date object.
* @returns {boolean} true if it's a fasting day, false otherwise.
*/
function isTsomeDihnetFastDay(etDate) {
validateEthiopianDateObject(etDate, 'isTsomeDihnetFastDay', 'etDate');
const weekday = getWeekday(etDate); // 0=Sun ... 6=Sat
const isWedOrFri = (weekday === 3 || weekday === 5);
if (!isWedOrFri) return false;
// Get Easter (Fasika) for the year and apply 50-day exception window
const bh = getBahireHasab(etDate.year);
const fasika = bh?.movableFeasts?.fasika?.ethiopian;
const paraclete = bh?.movableFeasts?.paraclete?.ethiopian;
if (!fasika || !paraclete) {
// If for some reason we cannot compute the window, default to standard Wed/Fri fasting.
return true;
}
const daysFromEaster = diffInDays(etDate, fasika);
const inPentecostSeason = daysFromEaster >= 1 && diffInDays(etDate, paraclete) <= 0;
return !inPentecostSeason;
}
// (no export) private helper only
/**
* Return an array of day numbers in the given Ethiopian month that belong to a fasting period.
* For TSOME_DIHENET, it returns all Wednesdays and Fridays excluding the 50-day period after Easter (through Pentecost).
* For fixed/range fasts, it returns the days intersecting the fast period.
*
* @param {string} fastKey - One of FastingKeys
* @param {number} year - Ethiopian year
* @param {number} month - Ethiopian month (1-13)
* @returns {number[]}
*/
export function getFastingDays(fastKey, year, month) {
validateNumericInputs('getFastingDays', { year, month });
const daysInMonth = getEthiopianDaysInMonth(year, month);
if (fastKey === FastingKeys.TSOME_DIHENET) {
const out = [];
for (let d = 1; d <= daysInMonth; d++) {
if (isTsomeDihnetFastDay({ year, month, day: d })) out.push(d);
}
return out;
}
// For other fasts: compute period and intersect with the month
const period = getFastingPeriod(fastKey, year);
if (!period) return [];
const startYearMonth = { y: period.start.year, m: period.start.month };
const endYearMonth = { y: period.end.year, m: period.end.month };
// If the month is completely outside, return []
const before = (year < startYearMonth.y) || (year === startYearMonth.y && month < startYearMonth.m);
const after = (year > endYearMonth.y) || (year === endYearMonth.y && month > endYearMonth.m);
if (before || after) return [];
const startDay = (year === period.start.year && month === period.start.month) ? period.start.day : 1;
const endDay = (year === period.end.year && month === period.end.month) ? period.end.day : daysInMonth;
const result = [];
for (let d = startDay; d <= endDay; d++) result.push(d);
return result;
}

@@ -10,3 +10,3 @@ import { Kenat } from './Kenat.js';

import { monthNames } from './constants.js';
import { getFastingPeriod } from './fasting.js';
import { getFastingPeriod, getFastingInfo, getFastingDays } from './fasting.js';

@@ -26,2 +26,4 @@ // Default export is the Kenat class directly

getFastingPeriod,
getFastingInfo,
getFastingDays,
MonthGrid,

@@ -28,0 +30,0 @@ Time,

@@ -1,2 +0,6 @@

import { getFastingPeriod } from '../src/fasting.js';
import { getFastingPeriod, getFastingInfo, getFastingDays } from '../src/fasting.js';
import { FastingKeys } from '../src/constants.js';
import { getBahireHasab } from '../src/bahireHasab.js';
import { addDays } from '../src/dayArithmetic.js';
import { getWeekday, getEthiopianDaysInMonth } from '../src/utils.js';

@@ -12,3 +16,3 @@ // Mock dependencies if they are not available in the test environment

test('should return the correct start and end dates for The Great Lent (Abiy Tsome)', () => {
const period = getFastingPeriod('ABIY_TSOME', year);
const period = getFastingPeriod(FastingKeys.ABIY_TSOME, year);
// VERIFIED: In 2016, Abiy Tsome starts on Megabit 2 and ends on Siklet (Miazia 25)

@@ -20,3 +24,3 @@ expect(period.start).toEqual({ year: 2016, month: 7, day: 2 });

test('should return the correct start and end dates for Fast of the Apostles (Tsome Hawaryat)', () => {
const period = getFastingPeriod('TSOME_HAWARYAT', year);
const period = getFastingPeriod(FastingKeys.TSOME_HAWARYAT, year);
// VERIFIED: In 2016, Paraclete is Sene 17, so Tsome Hawaryat starts Sene 18. It ends on Hamle 4.

@@ -27,3 +31,3 @@ expect(period.end).toEqual({ year: 2016, month: 11, day: 4 });

test('should return the correct start and end dates for Fast of Nineveh', () => {
const period = getFastingPeriod('NINEVEH', year);
const period = getFastingPeriod(FastingKeys.NINEVEH, year);
// VERIFIED: In 2016, Nineveh starts on Yekatit 18 and lasts 3 days.

@@ -35,3 +39,3 @@ expect(period.start).toEqual({ year: 2016, month: 6, day: 18 });

test('should return the correct start and end dates for Fast of the Prophets (Tsome Nebiyat)', () => {
const period = getFastingPeriod('TSOME_NEBIYAT', year);
const period = getFastingPeriod(FastingKeys.TSOME_NEBIYAT, year);
// This is a fixed fast from Hidar 15 to Tahsas 28. This test was already correct.

@@ -47,3 +51,3 @@ expect(period.start).toEqual({ year: 2016, month: 3, day: 15 });

test('should return the correct start and end dates for Ramadan', () => {
const period = getFastingPeriod('RAMADAN', year);
const period = getFastingPeriod(FastingKeys.RAMADAN, year);
// VERIFIED: For 2016 E.C., Ramadan 1445 A.H. runs from Megabit 2 to Miazia 1.

@@ -62,3 +66,3 @@ expect(period.start).toEqual({ year: 2016, month: 7, day: 2 });

// This confirms the calculation logic doesn't crash on different inputs.
const period = getFastingPeriod('ABIY_TSOME', 2020);
const period = getFastingPeriod(FastingKeys.ABIY_TSOME, 2020);
expect(period).toBeDefined();

@@ -70,1 +74,104 @@ expect(period.start).toBeDefined();

});
describe('getFastingInfo', () => {
const year = 2016;
test('returns multilingual info and period for Abiy Tsome', () => {
const infoAm = getFastingInfo(FastingKeys.ABIY_TSOME, year, { lang: 'amharic' });
expect(infoAm).toBeTruthy();
expect(infoAm.key).toBe(FastingKeys.ABIY_TSOME);
expect(infoAm.name).toBe('ዐቢይ ጾም (ሁዳዴ)');
expect(infoAm.description).toBeDefined();
expect(infoAm.period.start).toEqual({ year: 2016, month: 7, day: 2 });
const infoEn = getFastingInfo(FastingKeys.ABIY_TSOME, year, { lang: 'english' });
expect(infoEn.name).toMatch(/Great Lent/i);
expect(infoEn.period.end).toEqual({ year: 2016, month: 8, day: 25 });
});
test('returns info for Nineveh with 3-day period', () => {
const info = getFastingInfo(FastingKeys.NINEVEH, year);
expect(info.name).toBe('ጾመ ነነዌ');
expect(info.period.start).toEqual({ year: 2016, month: 6, day: 18 });
expect(info.period.end).toEqual({ year: 2016, month: 6, day: 20 });
});
test('returns info for Ramadan including tags', () => {
const info = getFastingInfo(FastingKeys.RAMADAN, year, { lang: 'english' });
expect(info.name).toBe('Ramadan');
expect(Array.isArray(info.tags)).toBe(true);
expect(info.period.start).toEqual({ year: 2016, month: 7, day: 2 });
});
test('returns info for Filseta (fixed Nehase 1-14)', () => {
const info = getFastingInfo(FastingKeys.FILSETA, year, { lang: 'amharic' });
expect(info).toBeTruthy();
expect(info.key).toBe(FastingKeys.FILSETA);
expect(info.name).toBe('ፍልሰታ');
expect(info.period.start).toEqual({ year: 2016, month: 12, day: 1 });
expect(info.period.end).toEqual({ year: 2016, month: 12, day: 14 });
});
});
describe('Orthodox Weekly Fasting (Tsome Dihnet)', () => {
test('Wednesdays and Fridays are fasting days outside the 50 days after Easter', () => {
const year = 2016;
// Choose a month well before Easter season: Hidar (month 3)
const month = 3;
const daysInMonth = getEthiopianDaysInMonth(year, month);
let foundWed = false;
let foundFri = false;
const days = getFastingDays('TSOME_DIHENET', year, month);
for (let day = 1; day <= daysInMonth; day++) {
const date = { year, month, day };
const wd = getWeekday(date);
if (wd === 3) { // Wednesday
foundWed = true;
expect(days.includes(day)).toBe(true);
}
if (wd === 5) { // Friday
foundFri = true;
expect(days.includes(day)).toBe(true);
}
}
expect(foundWed || foundFri).toBe(true);
});
test('No fasting on Wednesdays/Fridays during the 50 days after Easter (until Pentecost)', () => {
const year = 2016;
const bh = getBahireHasab(year);
const easter = bh.movableFeasts.fasika.ethiopian;
const pentecost = bh.movableFeasts.paraclete.ethiopian;
// Scan the full window starting the day after Easter through Pentecost inclusive
let d = addDays(easter, 1);
while (true) {
const wd = getWeekday(d);
if (wd === 3 || wd === 5) {
const list = getFastingDays('TSOME_DIHENET', d.year, d.month);
expect(list.includes(d.day)).toBe(false);
}
if (d.year === pentecost.year && d.month === pentecost.month && d.day === pentecost.day) break;
d = addDays(d, 1);
}
});
test('Fasting resumes on Wed/Fri after Pentecost in the same year', () => {
const year = 2016;
const bh = getBahireHasab(year);
const pentecost = bh.movableFeasts.paraclete.ethiopian;
// Search within a few weeks after Pentecost for a Wed or Fri marked as fasting
let found = false;
for (let i = 1; i <= 21; i++) {
const d = addDays(pentecost, i);
const wd = getWeekday(d);
if (wd === 3 || wd === 5) {
const list = getFastingDays('TSOME_DIHENET', d.year, d.month);
expect(list.includes(d.day)).toBe(true);
found = true;
break;
}
}
expect(found).toBe(true);
});
});