No description
Find a file
2026-05-30 15:12:16 -04:00
.github ci: corepack before setup-node, scope prettier to src/, emit d.mts 2026-05-29 20:05:46 -04:00
src refactor: code quality improvements and Temporal Protocol compliance 2026-03-08 11:37:22 -04:00
.editorconfig refactor: code quality improvements and Temporal Protocol compliance 2026-03-08 11:37:22 -04:00
.gitignore refactor: code quality improvements and Temporal Protocol compliance 2026-03-08 11:37:22 -04:00
.npmrc chore: clear .npmrc to remove pnpm-only key that warns on npm publish 2026-02-25 15:09:09 -05:00
.nvmrc feat: initial release of temporal-hijri v1.0.0 2026-02-25 14:17:27 -05:00
CHANGELOG.md chore: bump to v1.0.1 2026-05-28 13:55:03 -04:00
eslint.config.mjs chore: adopt shared config packages (tsconfig, eslint, prettier) 2026-05-30 15:12:16 -04:00
LICENSE feat: initial release of temporal-hijri v1.0.0 2026-02-25 14:17:27 -05:00
package.json chore: adopt shared config packages (tsconfig, eslint, prettier) 2026-05-30 15:12:16 -04:00
pnpm-lock.yaml chore: adopt shared config packages (tsconfig, eslint, prettier) 2026-05-30 15:12:16 -04:00
pnpm-workspace.yaml feat: initial release of temporal-hijri v1.0.0 2026-02-25 14:17:27 -05:00
README.md chore: E6 polish wiki content + ADR-015 CI updates (P1) 2026-05-29 07:15:50 -04:00
test-cjs.cjs refactor: code quality improvements and Temporal Protocol compliance 2026-03-08 11:37:22 -04:00
test.mjs refactor: code quality improvements and Temporal Protocol compliance 2026-03-08 11:37:22 -04:00
tsconfig.json chore: adopt shared config packages (tsconfig, eslint, prettier) 2026-05-30 15:12:16 -04:00
tsup.config.ts ci: pin pnpm to version 10 in all CI jobs 2026-03-08 16:37:46 -04:00

npm version CI License: MIT

temporal-hijri

Temporal Calendar Protocol implementation for the Hijri calendar. Works with the TC39 Temporal proposal (Stage 3) and @js-temporal/polyfill.

Provides UaqCalendar (Umm al-Qura) and FcnaCalendar (FCNA/ISNA) as plug-in calendars for Temporal.PlainDate and related types. The underlying conversion logic comes from hijri-core, a zero-dependency Hijri engine with table-driven UAQ data and astronomical FCNA calculations.


Installation

pnpm add temporal-hijri hijri-core

If you are using the polyfill instead of the native Temporal API:

pnpm add temporal-hijri hijri-core @js-temporal/polyfill

Quick Start

import { Temporal } from '@js-temporal/polyfill'; // or use native Temporal
import { uaqCalendar } from 'temporal-hijri';

// Convert an ISO date to Hijri coordinates
const isoDate = Temporal.PlainDate.from('2023-03-23');

console.log(uaqCalendar.year(isoDate));      // 1444
console.log(uaqCalendar.month(isoDate));     // 9  (Ramadan)
console.log(uaqCalendar.day(isoDate));       // 1
console.log(uaqCalendar.monthCode(isoDate)); // "M09"
console.log(uaqCalendar.inLeapYear(isoDate)); // false (1444 is 354 days)

// Convert Hijri coordinates back to ISO
const ramadan = uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 1 });
console.log(ramadan.toString()); // "2023-03-23"

// Arithmetic in Hijri space
const { Duration } = Temporal;
const nextMonth = uaqCalendar.dateAdd(isoDate, new Duration(0, 1));
console.log(uaqCalendar.month(nextMonth)); // 10 (Shawwal)
console.log(nextMonth.toString());          // "2023-04-21"

Calendar Classes

UaqCalendar

Implements the Umm al-Qura calendar, the official calendar of Saudi Arabia. Month boundaries come from pre-calculated tables covering 1318-1500 AH (Gregorian 1900-2076). The most widely used Hijri calendar standard for civil and religious purposes.

import { UaqCalendar } from 'temporal-hijri';
const cal = new UaqCalendar(); // cal.id === 'hijri-uaq'

FcnaCalendar

Implements the FCNA/ISNA calendar used by the Fiqh Council of North America and the Islamic Society of North America. Month starts are determined by astronomical new moon calculation (Meeus Chapter 49): if conjunction occurs before 12:00 UTC, the month begins the next day; if at or after noon, it begins the day after that.

import { FcnaCalendar } from 'temporal-hijri';
const cal = new FcnaCalendar(); // cal.id === 'hijri-fcna'

HijriCalendar (base class)

The base implementation. Accepts any CalendarEngine from hijri-core. Use this to build a Temporal calendar from a custom engine registered via hijri-core's registerCalendar().

import { HijriCalendar } from 'temporal-hijri';
import { getCalendar, registerCalendar } from 'hijri-core';

// Register a custom engine first
registerCalendar('my-calendar', myEngine);
const cal = new HijriCalendar(getCalendar('my-calendar'));
// cal.id === 'hijri-my-calendar'

Convenience singletons

uaqCalendar and fcnaCalendar are pre-constructed instances. They are shared objects and safe to reuse across calls.

import { uaqCalendar, fcnaCalendar } from 'temporal-hijri';

API

All methods receive a Temporal.PlainDate with an ISO (Gregorian) calendar. The PlainDate carries the ISO year/month/day; the calendar object interprets those coordinates.

Method Returns Description
year(date) number Hijri year
month(date) number Hijri month (1-12)
monthCode(date) string Month code: "M01" through "M12"
day(date) number Day of the Hijri month (1-29 or 1-30)
daysInMonth(date) number Length of the Hijri month (29 or 30)
daysInYear(date) number Days in the Hijri year (354 or 355)
monthsInYear(date) number Always 12
inLeapYear(date) boolean true if the year has 355 days
dayOfWeek(date) number ISO weekday: 1=Monday, 7=Sunday
dayOfYear(date) number Day position within the Hijri year
weekOfYear(date) number Week position within the Hijri year
daysInWeek(date) number Always 7
dateFromFields(fields) Temporal.PlainDate Construct ISO PlainDate from {year, month, day} in Hijri
yearMonthFromFields(fields) Temporal.PlainYearMonth Construct from {year, month} in Hijri
monthDayFromFields(fields) Temporal.PlainMonthDay Construct from {month, day} in Hijri
dateAdd(date, duration) Temporal.PlainDate Add a duration; years/months applied in Hijri space, days in ISO space
dateUntil(one, two, options) Temporal.Duration Difference between two dates; supports `largestUnit: 'years'
mergeFields(fields, additional) Record Merge field objects (Temporal protocol requirement)
toString() string Calendar identifier ("hijri-uaq" or "hijri-fcna")

Calendar Systems

System ID Authority Method Coverage
Umm al-Qura hijri-uaq KACST / Saudi Arabia Pre-calculated tables 1318-1500 AH (1900-2076 CE)
FCNA/ISNA hijri-fcna Fiqh Council of North America Astronomical new moon (Meeus) Unlimited (calculated)

UAQ dates outside 1318-1500 AH throw RangeError. FCNA is unbounded but loses precision for very early dates.


Custom Calendars

Any engine registered in hijri-core can be wrapped in a Temporal calendar:

import { HijriCalendar } from 'temporal-hijri';
import { registerCalendar, getCalendar } from 'hijri-core';
import type { CalendarEngine } from 'hijri-core';

const myEngine: CalendarEngine = {
  id: 'local-sighting',
  toHijri(date) { /* ... */ return { hy, hm, hd }; },
  toGregorian(hy, hm, hd) { /* ... */ return new Date(...); },
  isValid(hy, hm, hd) { /* ... */ return true; },
  daysInMonth(hy, hm) { /* ... */ return 29; },
};

registerCalendar('local-sighting', myEngine);
const cal = new HijriCalendar(getCalendar('local-sighting'));
// cal.id === 'hijri-local-sighting'

TypeScript

All types are exported:

import type { HijriDate, ConversionOptions, HijriCalendarOptions } from 'temporal-hijri';

The package ships dual CJS/ESM builds with full .d.ts and .d.mts declarations.


Architecture

A thin adapter over hijri-core that maps the Temporal Calendar Protocol to Hijri calendar engine calls. The UaqCalendar and FcnaCalendar classes implement Temporal.CalendarProtocol: the package itself adds no calendar math.

For more detail see the Architecture wiki page.

Compatibility

  • Node.js 20, 22, 24
  • Any bundler supporting exports field (Vite, Webpack 5, Rollup, esbuild)
  • ESM (import) and CommonJS (require): both provided
  • No native Temporal required: works entirely with @js-temporal/polyfill

Documentation

Full reference, architecture notes, and algorithmic detail in the wiki.



Acknowledgments

Calendar data and algorithms provided by hijri-core. The Umm al-Qura table is derived from data published by the King Abdulaziz City for Science and Technology (KACST). FCNA new moon calculations follow Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49.


License

MIT. Copyright (c) 2026 Aric Camarata. See LICENSE.