No description
Find a file
2026-02-25 15:09:10 -05:00
.github/workflows ci: pin pnpm via packageManager field, drop hardcoded version 2026-02-25 14:00:19 -05:00
.wiki feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
src feat: v2.1.0 — delegate engine logic to hijri-core 2026-02-25 14:14:29 -05: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 feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05: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
CHANGELOG.md feat: v2.1.0 — delegate engine logic to hijri-core 2026-02-25 14:14:29 -05: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 feat: v2.1.0 — delegate engine logic to hijri-core 2026-02-25 14:14:29 -05:00
pnpm-lock.yaml feat: v2.1.0 — delegate engine logic to hijri-core 2026-02-25 14:14:29 -05: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 feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
test-cjs.cjs feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
test.mjs feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05:00
tsconfig.json feat: v2.0.0 — FCNA calendar, dual ESM/CJS build, weekday bug fix, full test suite 2026-02-25 13:25:11 -05: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 | null

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 (17, 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 | null = 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.