Releases: MelakuDemeke/kenat
v3.2.0
Kenat v3.2.0 β Distance API and Holiday Helpers
Highlights
- Human-friendly Ethiopian date distance calculations
- Low-level diff utilities for advanced use cases
- Convenience helpers to compute distances to holidays
Whatβs New
- Distance API on
Kenat
instances:distanceTo(target, { units, output })
- Units:
years
,months
,days
(any combination) - Output:
string
(human-friendly) orobject
(structured)
- Units:
- Low-level utility export:
diffBreakdown(ethiopianDateA, ethiopianDateB)
- Holiday helpers:
Kenat.distanceToHoliday(holidayName, { direction, units, output })
HolidayNames
enum for discoverable holiday keys
Why it matters
Answer questions like βHow long until Meskel?β or βHow many days since 2016/1/1?β with concise, accurate, and Ethiopian-calendarβaware distance results. APIs are composable and suitable for both UI strings and logical calculations.
Install
npm install kenat@^3.2.0
Usage Examples
import Kenat, { diffBreakdown, HolidayNames } from 'kenat';
// Today in Ethiopian calendar
const today = new Kenat();
console.log('Today (ET):', today.format({ lang: 'english' }));
// 1) Difference to a specific Ethiopian date β only days
console.log('Days since 2016/1/1:', today.distanceTo('2016/1/1', { units: ['days'], output: 'string' }));
// 2) Difference to a future date β months and days
const futureDate = { year: today.getEthiopian().year, month: 13, day: 5 };
console.log('Until 13/5 (this year):', today.distanceTo(futureDate, { units: ['months', 'days'], output: 'string' }));
// 3) Full breakdown (years, months, days) as object
const other = new Kenat('2015/5/10');
console.log('Breakdown (object):', today.distanceTo(other, { units: ['years', 'months', 'days'], output: 'object' }));
// 4) Using low-level diffBreakdown directly on Ethiopian date objects
console.log('diffBreakdown low-level:', diffBreakdown(today.getEthiopian(), other.getEthiopian()));
// 5) Holidays β days until next Ethiopian New Year (Enkutatash)
console.log('Days until next Enkutatash:', Kenat.distanceToHoliday(HolidayNames.enkutatash, { direction: 'future', units: ['days'], output: 'string' }));
// 6) Holidays β how long ago was Meskel (months and days)
console.log('Since last Meskel:', Kenat.distanceToHoliday(HolidayNames.meskel, { direction: 'past', units: ['months', 'days'], output: 'string' }));
// 7) Holidays β closest Meskel (auto chooses nearest past or future) full breakdown
console.log('Nearest Meskel (full):', Kenat.distanceToHoliday(HolidayNames.meskel, { direction: 'auto', units: ['years', 'months', 'days'], output: 'string' }));
Compatibility
- No breaking changes expected to existing public APIs
- New exports:
diffBreakdown
,HolidayNames
Upgrade Notes
- No code changes required for existing consumers
- For TypeScript users, ensure your types pick up the new exports if you rely on re-export patterns
Testing
npm test --silent | cat
Method Chaining
Changelog
All notable changes to this project will be documented in this file.
3.1.0 - 2025-09-15
Added
- Method chaining and immutability support in
Kenat
:add(amount, 'days'|'months'|'years')
subtract(amount, 'days'|'months'|'years')
startOf('day'|'month'|'year')
endOf('day'|'month'|'year')
setTime(hour, minute, 'day'|'night')
now returns a new instance
- New test suite
tests/methodChaining.test.js
covering chaining, immutability, time preservation, error cases, leap-year handling, and boundaries. - Type definitions updated in
types/Kenat.d.ts
to include new chainable APIs.
Changed
- Existing arithmetic methods now delegate to the new chainable API while preserving backward compatibility:
addDays(days)
,addMonths(months)
,addYears(years)
startOfMonth()
,endOfMonth()
Fixed
addDays
insrc/dayArithmetic.js
now correctly handles negative day offsets (moving backward across months/years).
Docs
- Inline JSDoc improvements around new methods (chainability and immutability).
Performance/Behavioral Notes
- All date operations are immutableβno mutation of the original
Kenat
instance. - Time component is preserved across date arithmetic by default.
Migration Notes
- No breaking changes. Existing APIs continue to work.
- Prefer new chainable methods for more fluent and readable code.
Examples
- Basic chaining
const date1 = new Kenat('2017/1/1');
const future = date1.add(7, 'days').add(1, 'months').add(1, 'years');
console.log('Original:', date1.format());
console.log('After chaining:', future.format());
console.log('Result:', future.getEthiopian());
Output:
Original: αα΅α¨α¨α 1 2017
After chaining: α₯α
αα΅ 8 2018
Result: { year: 2018, month: 2, day: 8 }
- Subtract operations
const date2 = new Kenat('2017/1/1');
const past = date2.subtract(7, 'days').subtract(1, 'months').subtract(1, 'years');
console.log('Original:', date2.format());
console.log('After subtracting:', past.format());
console.log('Result:', past.getEthiopian());
Output:
Original: αα΅α¨α¨α 1 2017
After subtracting: ααα 29 2015
Result: { year: 2015, month: 11, day: 29 }
- Mixed add and subtract
const date3 = new Kenat('2017/1/1');
const mixed = date3.add(7, 'days').subtract(1, 'months').add(1, 'years');
console.log('Original:', date3.format());
console.log('After mixed operations:', mixed.format());
console.log('Result:', mixed.getEthiopian());
Output:
Original: αα΅α¨α¨α 1 2017
After mixed operations: α³αα 5 2017
Result: { year: 2017, month: 13, day: 5 }
- Time preservation
const date4 = new Kenat('2017/1/1', { hour: 3, minute: 30, period: 'day' });
const withTime = date4.add(7, 'days').add(1, 'months');
console.log('Original:', date4.toString());
console.log('After operations:', withTime.toString());
console.log('Time preserved:', withTime.time);
Output:
Original: αα΅α¨α¨α 1 2017 03:30 α αα΅
After operations: α₯α
αα΅ 8 2017 03:30 α αα΅
Time preserved: { hour: 3, minute: 30, period: 'day' }
- startOf and endOf chaining
const date5 = new Kenat('2017/6/15');
const res = date5.startOf('month').add(7, 'days').endOf('day');
console.log('Original:', date5.format());
console.log('After startOf/endOf:', res.format());
console.log('Time set to:', res.time);
Output:
Original: α¨α«α²α΅ 15 2017
After startOf/endOf: α¨α«α²α΅ 8 2017
Time set to: { hour: 12, minute: 0, period: 'night' }
- Complex chaining
const date6 = new Kenat('2017/1/1');
const complex = date6
.startOf('month')
.add(14, 'days')
.setTime(12, 0, 'day')
.add(1, 'months')
.endOf('day')
.add(1, 'years');
console.log('Original:', date6.format());
console.log('After complex chaining:', complex.format());
console.log('Final result:', complex.getEthiopian());
Output:
Original: αα΅α¨α¨α 1 2017
After complex chaining: α₯α
αα΅ 15 2018
Final result: { year: 2018, month: 2, day: 15 }
- Leap year handling
const leapDate = new Kenat('2015/13/6');
const leapResult = leapDate.add(1, 'days').add(1, 'months');
console.log('Original (leap year):', leapDate.format());
console.log('After operations:', leapResult.format());
console.log('Result:', leapResult.getEthiopian());
Output:
Original (leap year): α³αα 6 2015
After operations: α₯α
αα΅ 1 2016
Result: { year: 2016, month: 2, day: 1 }
Calendar Modes & Data Structure Overhaul
This release marks a significant step forward for the Kenat library, introducing powerful new ways to view and filter the calendar. The highlight is the new Calendar Modes feature, alongside major improvements to the data structures and overall code quality that make the library more robust and easier to maintain.
β¨ New Features
-
Calendar Modes: You can now initialize the calendar in one of several modes to exclusively display certain types of holidays:
- Public Holidays Mode: A mode to display only official non-working public holidays. This is now the default mode for the
Kenat.getMonthCalendar()
static method. - Christian Mode: A comprehensive mode for Christian holidays, showing movable feasts from Bahire Hasab (like Fasika), major fixed holidays, and recurring monthly saints' days.
- Muslim Mode: A dedicated mode for Muslim holidays, which also automatically highlights every Friday as "Jummah" on the calendar grid.
- Public Holidays Mode: A mode to display only official non-working public holidays. This is now the default mode for the
-
Saints' Day Filter: Within Christian Mode, a new option allows users to toggle between viewing only major annual feasts ("Nigs") or showing all daily saints' commemorations for a less cluttered view.
π οΈ Improvements & Refactoring
- Refactored
nigs.js
Data Structure: The data structure for saints with multiple annual commemorations (e.g., Abuna Takla Haymanot) has been refactored. Events are now nested under a single parent saint object, eliminating data redundancy and making the structure more logical and scalable. - Refactored
MonthGrid
Logic: The coregenerate()
method in theMonthGrid
class has been broken down into smaller, single-responsibility helper methods. This significantly improves code readability and makes future maintenance and feature development easier. - Updated Holiday Data: Siklet (Good Friday) is now correctly tagged as a
PUBLIC
holiday, ensuring it appears in the "Public Holidays" mode.
β Fixes
- Fixed an issue where the calendar UI could fail to display certain holidays when multiple event types (e.g., a public holiday and a saint's day) occurred on the same date. The new styling logic correctly handles overlapping events.
- Resolved bugs in the demo application where navigation buttons and event modals would become unresponsive after certain operations.
v2.0.0
Description:
a major enhancement to the library by adding a complete, authentic Bahire Hasab (α£αα¨ αα³α₯) calculation engine and a powerful new API for querying and filtering holidays. This makes kenat
a more comprehensive and robust tool for developers building applications that require deep Ethiopian calendar and liturgical context.
β¨ Key Changes
1. Bahire Hasab Calculation Engine (/src/bahireHasab.js
)
- A new, self-contained module has been created to handle all traditional calculations based on the Bahire Hasab system.
- It accurately computes foundational values like
Amete Alem
,Wenber
,Metqi
, and the date ofNineveh
. - This module is now the single source of truth for all movable feast calculations, ensuring authenticity and maintainability.
2. Advanced Holiday API
getHoliday(holidayKey, year, options)
: A new top-level function to fetch a single, fully-detailed holiday object for a given year.getHolidaysForYear(year, options)
: A new convenience function to get a complete, sorted list of all holidays (fixed and movable) for an entire year..isHoliday()
Method: A new method on theKenat
class instance to easily check if any specific date is a holiday.new Kenat('2017/1/17').isHoliday()
.getBahireHasab()
Method: A new method on theKenat
class that returns a rich object containing all calculated Bahire Hasab values and the dates of all movable feasts for that year.
3. Powerful Holiday Filtering
- The
getHolidaysInMonth
,getHolidaysForYear
, andMonthGrid
functions now accept afilter
option. - Developers can easily request only the holidays they need by providing a single tag (e.g.,
{ filter: 'public' }
) or an array of tags ({ filter: ['christian', 'muslim'] }
). - The
HolidayTags
object is now exported, allowing for safe and readable filtering.
4. Internationalization (i18n) & Data Refactoring
- Full i18n for Holidays: All holiday names and descriptions have been moved to a centralized
holidayInfo
constant, allowing for easy translation and maintenance. - Bahire Hasab i18n: The
.getBahireHasab()
method now accepts alang
option and returns translated names for the Evangelist and New Year's Day. - DRY Principle: The core calculation logic in
bahireHasab.js
has been refactored into a single internal "engine" to avoid code duplication and improve maintainability.
5. Bug Fixes
- The logic for calculating Islamic holidays has been completely rewritten to correctly handle cases where holidays (like Moulid) can occur twice in the same Ethiopian solar year.
- Fixed a circular dependency between the
Kenat
andbahireHasab
modules.
This update significantly enhances the library's capabilities, making it a more powerful, convenient, and authentic resource for Ethiopian calendar development.
v1.5.1
Full Changelog: v1.5.0...v1.5.1
v1.4.0
π Kenat v1.4.0 Released! πͺπΉπ
Enhanced Ethiopian Time Support
Weβre excited to announce Kenat v1.4.0 β a major update focused on Ethiopian time calculations and formatting. This release brings powerful and flexible tools to work with Ethiopian time, including conversions, math, and display.
π§ Whatβs New in v1.4.0
β
New module: Time.js
β Introduces a clean, object-oriented Time
class
β Supports:
β’ add()
/ subtract()
Ethiopian time durations
β’ diff()
for time difference
β’ format()
with Geez numerals, custom styles
β
Renamed:
β timeConverter.js
β ethiopianTime.js
(more intuitive)
β
Functional Utilities Added to ethiopianTime.js
:
β addEthiopianTime()
β subtractEthiopianTime()
β getTimeDifference()
β formatEthiopianTime()
β
Improved Formatting:
β Display times with options like Geez numerals, period labels (αα
/ααα΅
), and minimal style
π¦ Update Now
npm install [email protected]
π Docs & examples coming soon.
v1.3.1
What's Changed
- Add flexible date formatting with language, weekday, Geez numerals, and time options to Ethiopian calendar by @MelakuDemeke in #12
- Add holiday names and enhance Islamic date conversions by @MelakuDemeke in #13
Full Changelog: v1.3.0...v1.3.1
v1.0.0
What's Changed
- Add time support to Kenat constructor and string output by @MelakuDemeke in #5
Full Changelog: v0.1.1...v1.0.0