No description
Find a file
2026-03-08 11:42:29 -04:00
.github/workflows refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
.wiki refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
src refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
.editorconfig feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
.gitignore refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
.npmrc chore: clear .npmrc to remove pnpm-only key that warns on npm publish 2026-02-25 15:09:10 -05:00
.nvmrc feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
.prettierrc refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
CHANGELOG.md feat: v2.1.0 - delegate engine to hijri-core 2026-02-25 15:26:28 -05:00
eslint.config.mjs refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
LICENSE feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
package.json refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
pnpm-lock.yaml refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
pnpm-workspace.yaml feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
README.md refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
test-cjs.cjs refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
test.mjs refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
tsconfig.json refactor: code quality improvements across the board 2026-03-08 11:42:29 -04:00
tsup.config.ts feat: v2.1.0 — delegate engine logic to hijri-core 2026-02-25 14:14:29 -05:00

luxon-hijri

npm version CI License: MIT

Hijri/Gregorian date conversion and formatting. Supports two calendar systems: Umm al-Qura (default, table-based) and FCNA/ISNA (astronomical, all Hijri years). Built on Luxon.

Installation

npm install luxon-hijri

Quick Start

import { toHijri, toGregorian, formatHijriDate } from 'luxon-hijri';

// Gregorian to Hijri (Umm al-Qura, default)
const h = toHijri(new Date(2023, 2, 23, 12)); // March 23, 2023
// { hy: 1444, hm: 9, hd: 1 }

// Hijri to Gregorian
const g = toGregorian(1444, 9, 1); // 1 Ramadan 1444
// Date: 2023-03-23T00:00:00.000Z

// Format a Hijri date
formatHijriDate({ hy: 1444, hm: 9, hd: 1 }, 'iEEEE, iD iMMMM iYYYY ioooo');
// "Yawm al-Khamis, 1 Ramadan 1444 AH"

// FCNA/ISNA calendar (astronomical, works for all Hijri years)
toHijri(new Date(2025, 2, 1, 12), { calendar: 'fcna' }); // { hy: 1446, hm: 9, hd: 1 }
toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Date: 2025-03-01T00:00:00.000Z

API

toHijri(date, options?)

Converts a Gregorian Date to a Hijri date object.

function toHijri(date: Date, options?: ConversionOptions): HijriDate | null;

For 'uaq' (default): returns null if the date falls outside the table range (before 1 Muharram 1318 H / 1900-04-30, or at/after 1 Muharram 1501 H / 2077-11-17). Uses local date components.

For 'fcna': returns null only for dates before 1 AH. Uses UTC date components (FCNA boundaries are defined in UTC).

Throws Error("Invalid Gregorian date") if date is not a valid Date.

toHijri(new Date(2024, 6, 7, 12)); // { hy: 1446, hm: 1, hd: 1 }  (UAQ)
toHijri(new Date(2025, 2, 1, 12), { calendar: 'fcna' }); // { hy: 1446, hm: 9, hd: 1 }  (FCNA)
toHijri(new Date(1800, 0, 1)); // null - before UAQ table range

toGregorian(hy, hm, hd, options?)

Converts a Hijri date to a Gregorian Date at UTC midnight.

function toGregorian(hy: number, hm: number, hd: number, options?: ConversionOptions): Date;

Throws Error("Invalid Hijri date") if the date is invalid for the selected calendar.

toGregorian(1446, 1, 1); // Date: 2024-07-07T00:00:00.000Z  (UAQ)
toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Date: 2025-03-01T00:00:00.000Z  (FCNA)
toGregorian(1, 1, 1, { calendar: 'fcna' }); // Date: 0622-07-18T00:00:00.000Z  (Islamic epoch)

formatHijriDate(date, format)

Formats a Hijri date using the token patterns below. Tokens not listed pass through unchanged.

function formatHijriDate(date: HijriDate, format: string): string;
Token Output Example
iYYYY Year, 4 digits 1444
iYY Year, last 2 digits 44
iMMMM Month, full name Ramadan
iMMM Month, medium name Ramadan
iMM Month, zero-padded 09
iM Month, no padding 9
iDD Day, zero-padded 01
iD Day, no padding 1
iEEEE Weekday, full name Yawm al-Khamis
iEEE Weekday, abbreviated Kham
iE Weekday, numeric (Sun=1) 5
ioooo Era, full AH
iooo Era, abbreviated AH
HH, H, hh, h Hour (via Luxon) 14, 14, 02, 2
mm, m Minute (via Luxon) 05, 5
ss, s Second (via Luxon) 30, 30
a AM/PM AM
z, zz, zzz Timezone UTC
Z, ZZ Timezone offset +00:00

isValidHijriDate(hy, hm, hd, options?)

Returns true if the Hijri date is valid for the selected calendar.

function isValidHijriDate(hy: number, hm: number, hd: number, options?: ConversionOptions): boolean;

For 'uaq' (default): year must be 13181500, month 112, day must not exceed the actual month length from the UAQ table.

For 'fcna': year must be ≥ 1, month 112, day must not exceed the computed FCNA month length.

Types

interface HijriDate {
  hy: number; // Hijri year
  hm: number; // Hijri month (112)
  hd: number; // Hijri day (130)
}

type CalendarSystem = 'uaq' | 'fcna';

interface ConversionOptions {
  calendar?: CalendarSystem; // default: 'uaq'
}

interface HijriYearRecord {
  hy: number; // Hijri year
  dpm: number; // days-per-month bitmask (bit 0 = month 1, 1 = 30 days, 0 = 29 days)
  gy: number; // Gregorian year of 1 Muharram
  gm: number; // Gregorian month of 1 Muharram
  gd: number; // Gregorian day of 1 Muharram
}

Additional exports

import {
  hDatesTable, // HijriYearRecord[] - the full Umm al-Qura table (184 entries)
  hmLong, // string[12] - full month names
  hmMedium, // string[12] - medium month names
  hmShort, // string[12] - abbreviated month names
  hwLong, // string[7] - full weekday names (Sunday first)
  hwShort, // string[7] - abbreviated weekday names
  hwNumeric, // number[7] - weekday numbers (1-7, Sunday=1)
  formatPatterns, // Record<string, string> - token reference
} from 'luxon-hijri';

Calendar Systems

Umm al-Qura ('uaq', default): Official Saudi calendar, table-based, covers Hijri years 13181500 (April 1900 to November 2076). Authoritative for Saudi Arabia and widely used across the Arab world.

FCNA/ISNA ('fcna'): Used by the Fiqh Council of North America and ISNA. Astronomical criterion: if the new moon conjunction occurs before 12:00 UTC on day D, the month begins at midnight of D+1; otherwise D+2. Works for all Hijri years (no range limit). New moon times use the full Meeus Chapter 49 algorithm, accurate to within a few minutes for 10003000 CE.

Architecture

The UAQ engine is a pure table lookup with binary search (O(log 183)). The FCNA engine computes new moon times astronomically using the Meeus Ch.49 formula: 3 to 5 trigonometric evaluations per call, sub-millisecond on any modern JS engine.

For more detail see the Architecture wiki page.

Compatibility

  • Node.js 20+ (ESM and CJS)
  • Bundlers: webpack, Rollup, Vite, esbuild (tree-shakeable, sideEffects: false)
  • TypeScript: full type definitions included

TypeScript

import { toHijri, toGregorian, formatHijriDate, isValidHijriDate } from 'luxon-hijri';
import type { HijriDate, HijriYearRecord, CalendarSystem, ConversionOptions } from 'luxon-hijri';

const h: HijriDate | null = toHijri(new Date());
const g: Date = toGregorian(1444, 9, 1, { calendar: 'fcna' });

Documentation

Full API reference, architecture notes, calendar background, and format token guide: https://github.com/acamarata/luxon-hijri/wiki

  • nrel-spa: NREL Solar Position Algorithm (pure JS)
  • pray-calc: Islamic prayer times, depends on nrel-spa
  • solar-spa: NREL SPA compiled to WebAssembly

License

MIT. Copyright (c) 2024-2026 Aric Camarata.

See LICENSE for the full text.