mirror of
https://github.com/acamarata/luxon-hijri.git
synced 2026-07-01 03:04:30 +00:00
203 lines
8.2 KiB
Markdown
203 lines
8.2 KiB
Markdown
# luxon-hijri
|
||
|
||
[](https://www.npmjs.com/package/luxon-hijri)
|
||
[](https://github.com/acamarata/luxon-hijri/actions/workflows/ci.yml)
|
||
[](./LICENSE)
|
||
|
||
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
|
||
|
||
```bash
|
||
npm install luxon-hijri
|
||
```
|
||
|
||
## Quick Start
|
||
|
||
```javascript
|
||
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.
|
||
|
||
```typescript
|
||
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`.
|
||
|
||
```javascript
|
||
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.
|
||
|
||
```typescript
|
||
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.
|
||
|
||
```javascript
|
||
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.
|
||
|
||
```typescript
|
||
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.
|
||
|
||
```typescript
|
||
function isValidHijriDate(hy: number, hm: number, hd: number, options?: ConversionOptions): boolean;
|
||
```
|
||
|
||
For `'uaq'` (default): year must be 1318–1500, month 1–12, day must not exceed the actual month length from the UAQ table.
|
||
|
||
For `'fcna'`: year must be ≥ 1, month 1–12, day must not exceed the computed FCNA month length.
|
||
|
||
### Types
|
||
|
||
```typescript
|
||
interface HijriDate {
|
||
hy: number; // Hijri year
|
||
hm: number; // Hijri month (1–12)
|
||
hd: number; // Hijri day (1–30)
|
||
}
|
||
|
||
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
|
||
|
||
```javascript
|
||
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 1318–1500 (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 1000–3000 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](https://github.com/acamarata/luxon-hijri/wiki/Architecture).
|
||
|
||
## Compatibility
|
||
|
||
- Node.js 20+ (ESM and CJS)
|
||
- Bundlers: webpack, Rollup, Vite, esbuild (tree-shakeable, `sideEffects: false`)
|
||
- TypeScript: full type definitions included
|
||
|
||
## TypeScript
|
||
|
||
```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](https://github.com/acamarata/luxon-hijri/wiki)
|
||
|
||
## Related
|
||
|
||
- [nrel-spa](https://www.npmjs.com/package/nrel-spa): NREL Solar Position Algorithm (pure JS)
|
||
- [pray-calc](https://www.npmjs.com/package/pray-calc): Islamic prayer times, depends on nrel-spa
|
||
- [solar-spa](https://www.npmjs.com/package/solar-spa): NREL SPA compiled to WebAssembly
|
||
|
||
## Acknowledgments
|
||
|
||
The Umm al-Qura table is derived from data published by the King Abdulaziz City for Science and Technology (KACST). The FCNA new moon algorithm follows Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49.
|
||
|
||
## License
|
||
|
||
MIT. Copyright (c) 2024-2026 Aric Camarata.
|
||
|
||
See [LICENSE](./LICENSE) for the full text.
|