ethiopian-dates
v1.0.2
Published
Ethiopian ↔ Gregorian calendar conversion with Amharic month names, Geez numerals, Ethiopian holidays, date formatting, and day of week. TypeScript, zero dependencies.
Maintainers
Readme
ethiopian-dates
You're building something for Ethiopian users and you need the date to read Sene 4, 2018 instead of June 11, 2026. This package does that conversion in both directions, and handles the parts you'd rather not write yourself: the 13th month, the leap-year rule, Geez numerals, Amharic names, and the full public holiday list including the movable ones.
Zero dependencies. Written in TypeScript, so you get types without installing anything extra.
npm install ethiopian-datesConvert a date
Pass year, month, day. Months are 1-indexed in both directions, and month 13 is Pagume.
import { toEthiopian, toGregorian } from 'ethiopian-dates';
toEthiopian(2024, 9, 11);
// { year: 2017, month: 1, day: 1, monthName: 'Meskerem', monthNameAmharic: 'መስከረም' }
toGregorian(2017, 1, 1);
// { year: 2024, month: 9, day: 11, monthName: 'September' }Bad dates throw a RangeError instead of silently converting to something wrong. February 30 won't get past you, and neither will Pagume 6 in a year where Pagume only has 5 days.
Get today
import { todayInEthiopian } from 'ethiopian-dates';
todayInEthiopian();
// { year: 2018, month: 10, day: 4, monthName: 'Sene', monthNameAmharic: 'ሰኔ' }Uses the local timezone. Pass a Date if you mean a different moment: todayInEthiopian(someDate).
Write Geez numerals
import { toGeez } from 'ethiopian-dates';
toGeez(2017); // '፳፻፲፯'
toGeez(1); // '፩'
toGeez(100); // '፻'Handles the quirks for you: there's no zero, and the coefficient ፩ is dropped before ፻ (100) and ፼ (10,000). Works up to 99,999,999.
Look up holidays
import { getHolidays, isHoliday } from 'ethiopian-dates';
getHolidays(2017);
// All public holidays that year, sorted, each with its Gregorian equivalent:
// [
// { name: 'Enkutatash', nameAmharic: 'እንቁጣጣሽ', month: 1, day: 1, movable: false,
// year: 2017, gregorian: { year: 2024, month: 9, day: 11, monthName: 'September' } },
// ...
// ]
isHoliday(2017, 8, 12);
// { name: 'Fasika (Easter)', nameAmharic: 'ፋሲካ', month: 8, day: 12, movable: true }
isHoliday(2017, 1, 2);
// nullThis covers all 12 public holidays, not just the fixed ones:
- Fixed dates: Enkutatash, Meskel, Genna, Timket, Adwa Victory Day, Workers' Day, Patriots' Victory Day
- Siklet (Good Friday) and Fasika (Easter), computed with the Orthodox computus that Ethiopia follows
- Eid al-Fitr, Eid al-Adha (Arafa), and Mawlid from the tabular Hijri calendar
Two things worth knowing before you ship. Islamic holidays carry approximate: true because the observed date follows the moon sighting and can land a day off from the tabular date. And because the lunar year is about 11 days shorter, an Islamic holiday occasionally falls twice in one Ethiopian year; Mawlid did in 2017 EC, and getHolidays returns both occurrences.
Format for your UI
import { formatEthiopian } from 'ethiopian-dates';
formatEthiopian(2017, 1, 1, 'DD MMMM YYYY');
// '01 Meskerem 2017'
formatEthiopian(2017, 1, 1, 'DD MMMM YYYY', 'am');
// '፩ መስከረም ፳፻፲፯'Tokens: YYYY year, MMMM month name, MM/M month number, DD/D day. The 'am' locale switches to Amharic month names and Geez numerals.
Get the day of the week
import { getDayOfWeek } from 'ethiopian-dates';
getDayOfWeek(2017, 1, 1);
// { name: 'Wednesday', nameAmharic: 'ረቡዕ', index: 2 }index runs 0 (Monday) through 6 (Sunday).
Constants you can import
| Export | What it is |
|---|---|
| ETHIOPIAN_MONTHS | All 13 months with English and Amharic names |
| ETHIOPIAN_HOLIDAYS | The 7 fixed-date holidays |
| WEEK_DAYS | The 7 days with Amharic names |
| isEthiopianLeapYear(year) | True when year % 4 === 3 |
| ethiopianMonthLength(year, month) | 30, or 5/6 for Pagume |
The calendar, in thirty seconds
Twelve months of exactly 30 days, then Pagume, a 13th month of 5 days (6 in leap years). Leap years come every four years with no century exceptions. The year count runs 7 to 8 behind the Gregorian one because Ethiopia kept an older calculation of the Annunciation. New Year (Enkutatash) falls on September 11, or September 12 right after an Ethiopian leap year.
Under the hood, every conversion goes through Julian Day Numbers, so the two calendars never touch each other directly and the leap-year edge cases fall out of the arithmetic instead of being special-cased.
Working on the package
Using the published package needs nothing special: dist/ is plain ES modules and runs on Node 18+ or any bundler. Developing it is pickier, because the tests run the TypeScript source directly through Node's type stripping:
npm install # pulls in TypeScript and builds dist/
npm test # needs Node 23.6+ to run the .ts tests natively
npm run buildThe test suite round-trips every single day across 13 years, checks known historical dates, covers the Geez numeral quirks, verifies holiday dates against real observances, and cross-checks a full year of weekdays against JavaScript's Date.
