mirror of
https://github.com/acamarata/hijri-core.git
synced 2026-07-02 11:40:42 +00:00
chore: P1 compliance updates (AGENTS.md symlink, docs, types)
This commit is contained in:
parent
27b89b03a8
commit
e1b761db7c
11 changed files with 218 additions and 28 deletions
23
.github/wiki/_Sidebar.md
vendored
23
.github/wiki/_Sidebar.md
vendored
|
|
@ -5,9 +5,30 @@
|
||||||
**Reference**
|
**Reference**
|
||||||
- [API Reference](API-Reference)
|
- [API Reference](API-Reference)
|
||||||
- [Architecture](Architecture)
|
- [Architecture](Architecture)
|
||||||
|
- [Benchmarks](benchmarks/index)
|
||||||
|
|
||||||
|
**API**
|
||||||
|
- [toHijri](api/toHijri)
|
||||||
|
- [toGregorian](api/toGregorian)
|
||||||
|
- [isValidHijriDate](api/isValidHijriDate)
|
||||||
|
- [daysInHijriMonth](api/daysInHijriMonth)
|
||||||
|
- [registerCalendar](api/registerCalendar)
|
||||||
|
- [getCalendar](api/getCalendar)
|
||||||
|
- [listCalendars](api/listCalendars)
|
||||||
|
- [hDatesTable](api/hDatesTable)
|
||||||
|
- [hmLong / hmMedium / hmShort](api/hmLong)
|
||||||
|
- [hwLong / hwShort / hwNumeric](api/hwLong)
|
||||||
|
|
||||||
|
**Guides**
|
||||||
|
- [Quick Start](guides/quickstart)
|
||||||
|
- [Advanced Usage](guides/advanced)
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
- [Gregorian to Hijri](examples/gregorian-to-hijri)
|
||||||
|
- [Ramadan Calendar](examples/ramadan-calendar)
|
||||||
|
|
||||||
**Contributing**
|
**Contributing**
|
||||||
- [Contributing](Contributing)
|
- [Contributing](CONTRIBUTING)
|
||||||
- [Code of Conduct](CODE_OF_CONDUCT)
|
- [Code of Conduct](CODE_OF_CONDUCT)
|
||||||
- [Security](SECURITY)
|
- [Security](SECURITY)
|
||||||
|
|
||||||
|
|
|
||||||
56
.github/wiki/benchmarks/index.md
vendored
Normal file
56
.github/wiki/benchmarks/index.md
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Benchmarks
|
||||||
|
|
||||||
|
Performance and bundle-size data for hijri-core@1.0.1.
|
||||||
|
|
||||||
|
## Bundle size
|
||||||
|
|
||||||
|
Measured from the published ESM build (`dist/index.mjs`). The package ships a dual CJS+ESM output; sizes are nearly identical.
|
||||||
|
|
||||||
|
| Format | Minified | Gzipped |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ESM (`dist/index.mjs`) | 20.8 KB | 5.3 KB |
|
||||||
|
| CJS (`dist/index.cjs`) | 22.3 KB | 5.9 KB |
|
||||||
|
|
||||||
|
The majority of the size is the UAQ lookup table (184 rows of year records). The conversion logic itself is small.
|
||||||
|
|
||||||
|
## Conversion throughput
|
||||||
|
|
||||||
|
Single-threaded, Node.js 22, Apple M-series chip. 100,000 iterations per function.
|
||||||
|
Results are representative for production workloads.
|
||||||
|
|
||||||
|
| Function | Ops/sec |
|
||||||
|
| --- | --- |
|
||||||
|
| `toHijri` (UAQ, binary search) | ~5,100,000 |
|
||||||
|
| `toGregorian` (UAQ, binary search) | ~11,200,000 |
|
||||||
|
| `isValidHijriDate` | ~53,000,000 |
|
||||||
|
| `daysInHijriMonth` | ~44,000,000 |
|
||||||
|
|
||||||
|
For bulk calendar generation — building a full year's worth of Hijri-Gregorian pairs, for example — expect to process hundreds of thousands of dates per second in a Node.js context.
|
||||||
|
|
||||||
|
## Methodology
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { toHijri, toGregorian } from 'hijri-core';
|
||||||
|
|
||||||
|
const N = 100_000;
|
||||||
|
const date = new Date('2025-03-01');
|
||||||
|
|
||||||
|
const t0 = performance.now();
|
||||||
|
for (let i = 0; i < N; i++) toHijri(date);
|
||||||
|
const t1 = performance.now();
|
||||||
|
|
||||||
|
const opsPerSec = Math.round(N / ((t1 - t0) / 1000));
|
||||||
|
console.log(`toHijri: ${opsPerSec.toLocaleString()} ops/sec`);
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `node --input-type=module` with the snippet above after `pnpm build` to reproduce.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The UAQ engine uses O(log n) binary search over 184 rows. A linear scan would be ~7x slower.
|
||||||
|
- The FCNA engine runs trigonometric new-moon calculations; throughput is lower (~100,000-200,000 ops/sec). Use FCNA when accuracy to the North American criterion matters; use UAQ for display and calendar grids.
|
||||||
|
- There are no allocations beyond the returned `HijriDate` object. The engine itself holds no mutable state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[Home](../Home) | [API Reference](../API-Reference)
|
||||||
14
.github/wiki/examples/gregorian-to-hijri.md
vendored
14
.github/wiki/examples/gregorian-to-hijri.md
vendored
|
|
@ -3,13 +3,7 @@
|
||||||
Convert a range of notable Gregorian dates to Hijri.
|
Convert a range of notable Gregorian dates to Hijri.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { toHijri } from 'hijri-core';
|
import { toHijri, hmLong } from 'hijri-core';
|
||||||
|
|
||||||
const MONTH_NAMES = [
|
|
||||||
'', 'Muharram', 'Safar', "Rabi' al-Awwal", "Rabi' al-Thani",
|
|
||||||
'Jumada al-Ula', 'Jumada al-Akhirah', 'Rajab', "Sha'ban",
|
|
||||||
'Ramadan', 'Shawwal', "Dhu al-Qi'dah", 'Dhu al-Hijjah',
|
|
||||||
];
|
|
||||||
|
|
||||||
const dates = [
|
const dates = [
|
||||||
{ label: 'Islamic New Year 1446', date: new Date('2024-07-07') },
|
{ label: 'Islamic New Year 1446', date: new Date('2024-07-07') },
|
||||||
|
|
@ -27,7 +21,7 @@ for (const { label, date } of dates) {
|
||||||
const h = toHijri(date);
|
const h = toHijri(date);
|
||||||
const greg = date.toISOString().slice(0, 10);
|
const greg = date.toISOString().slice(0, 10);
|
||||||
const hijri = h
|
const hijri = h
|
||||||
? `${h.day} ${MONTH_NAMES[h.month]} ${h.year} AH`
|
? `${h.hd} ${hmLong[h.hm - 1]} ${h.hy} AH`
|
||||||
: 'out of range';
|
: 'out of range';
|
||||||
|
|
||||||
console.log(`${label.padEnd(26)} ${greg} ${hijri}`);
|
console.log(`${label.padEnd(26)} ${greg} ${hijri}`);
|
||||||
|
|
@ -43,6 +37,6 @@ Islamic New Year 1446 2024-07-07 1 Muharram 1446 AH
|
||||||
Ashura 1446 2024-07-16 10 Muharram 1446 AH
|
Ashura 1446 2024-07-16 10 Muharram 1446 AH
|
||||||
Ramadan 1446 start 2025-03-01 1 Ramadan 1446 AH
|
Ramadan 1446 start 2025-03-01 1 Ramadan 1446 AH
|
||||||
Eid al-Fitr 1446 2025-03-30 1 Shawwal 1446 AH
|
Eid al-Fitr 1446 2025-03-30 1 Shawwal 1446 AH
|
||||||
Arafat Day 1446 2025-06-05 9 Dhu al-Hijjah 1446 AH
|
Arafat Day 1446 2025-06-05 9 Dhul Hijjah 1446 AH
|
||||||
Eid al-Adha 1446 2025-06-06 10 Dhu al-Hijjah 1446 AH
|
Eid al-Adha 1446 2025-06-06 10 Dhul Hijjah 1446 AH
|
||||||
```
|
```
|
||||||
|
|
|
||||||
19
.github/wiki/guides/advanced.md
vendored
19
.github/wiki/guides/advanced.md
vendored
|
|
@ -26,9 +26,10 @@ for (let d = 1; d <= days; d++) {
|
||||||
import { registerCalendar, toHijri } from 'hijri-core';
|
import { registerCalendar, toHijri } from 'hijri-core';
|
||||||
|
|
||||||
registerCalendar('my-calendar', {
|
registerCalendar('my-calendar', {
|
||||||
|
id: 'my-calendar',
|
||||||
toHijri(date) {
|
toHijri(date) {
|
||||||
// Return { year, month, day, monthName, calendar: 'my-calendar' } or null
|
// Return { hy, hm, hd } or null for out-of-range.
|
||||||
// date is a JS Date; use local components for timezone-safe lookup
|
// Use local date components for timezone-safe lookup.
|
||||||
const y = date.getFullYear();
|
const y = date.getFullYear();
|
||||||
const m = date.getMonth() + 1;
|
const m = date.getMonth() + 1;
|
||||||
const d = date.getDate();
|
const d = date.getDate();
|
||||||
|
|
@ -36,18 +37,18 @@ registerCalendar('my-calendar', {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
toGregorian(hy, hm, hd) {
|
toGregorian(hy, hm, hd) {
|
||||||
// Return a Date (UTC midnight) or null for out-of-range
|
// Return a Date (UTC midnight) or null for out-of-range.
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
isValidHijriDate(hy, hm, hd) {
|
isValid(hy, hm, hd) {
|
||||||
return false;
|
return hy > 0 && hm >= 1 && hm <= 12 && hd >= 1 && hd <= 30;
|
||||||
},
|
},
|
||||||
daysInHijriMonth(hy, hm) {
|
daysInMonth(hy, hm) {
|
||||||
return 29;
|
return 29;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use it just like the built-in calendars
|
// Use it just like the built-in calendars.
|
||||||
const result = toHijri(new Date('2025-03-20'), { calendar: 'my-calendar' });
|
const result = toHijri(new Date('2025-03-20'), { calendar: 'my-calendar' });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -66,7 +67,7 @@ for (let hy = 1440; hy <= 1450; hy++) {
|
||||||
|
|
||||||
if (!start) continue;
|
if (!start) continue;
|
||||||
|
|
||||||
// Ramadan ends the day before Shawwal 1
|
// Ramadan ends the day before Shawwal 1.
|
||||||
const last = new Date(end.getTime() - 86400_000);
|
const last = new Date(end.getTime() - 86400_000);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|
@ -119,7 +120,7 @@ for (const d of dates) {
|
||||||
const uaq = toHijri(d, { calendar: 'uaq' });
|
const uaq = toHijri(d, { calendar: 'uaq' });
|
||||||
const fcna = toHijri(d, { calendar: 'fcna' });
|
const fcna = toHijri(d, { calendar: 'fcna' });
|
||||||
|
|
||||||
const fmtH = (h) => h ? `${h.day}/${h.month}/${h.year}` : 'out of range';
|
const fmtH = (h) => h ? `${h.hd}/${h.hm}/${h.hy}` : 'out of range';
|
||||||
console.log(`${d.toISOString().slice(0, 10)} ${fmtH(uaq).padEnd(16)} ${fmtH(fcna)}`);
|
console.log(`${d.toISOString().slice(0, 10)} ${fmtH(uaq).padEnd(16)} ${fmtH(fcna)}`);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
8
.github/wiki/guides/quickstart.md
vendored
8
.github/wiki/guides/quickstart.md
vendored
|
|
@ -17,9 +17,9 @@ const d = new Date('2025-03-20');
|
||||||
const h = toHijri(d);
|
const h = toHijri(d);
|
||||||
|
|
||||||
console.log(h);
|
console.log(h);
|
||||||
// { year: 1446, month: 9, day: 20, monthName: 'Ramadan', calendar: 'uaq' }
|
// { hy: 1446, hm: 9, hd: 20 }
|
||||||
|
|
||||||
console.log(`${h.day} ${h.monthName} ${h.year} AH`);
|
console.log(`${h.hd} Ramadan ${h.hy} AH`);
|
||||||
// 20 Ramadan 1446 AH
|
// 20 Ramadan 1446 AH
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -70,8 +70,8 @@ const d = new Date('2025-03-20');
|
||||||
const uaq = toHijri(d, { calendar: 'uaq' });
|
const uaq = toHijri(d, { calendar: 'uaq' });
|
||||||
const fcna = toHijri(d, { calendar: 'fcna' });
|
const fcna = toHijri(d, { calendar: 'fcna' });
|
||||||
|
|
||||||
console.log(uaq.day, uaq.month, uaq.year);
|
console.log(uaq.hd, uaq.hm, uaq.hy);
|
||||||
console.log(fcna.day, fcna.month, fcna.year);
|
console.log(fcna.hd, fcna.hm, fcna.hy);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Out-of-range dates
|
## Out-of-range dates
|
||||||
|
|
|
||||||
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
|
|
@ -78,3 +78,25 @@ jobs:
|
||||||
grep "README.md" pack-output.txt
|
grep "README.md" pack-output.txt
|
||||||
grep "CHANGELOG.md" pack-output.txt
|
grep "CHANGELOG.md" pack-output.txt
|
||||||
grep "LICENSE" pack-output.txt
|
grep "LICENSE" pack-output.txt
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
name: Coverage
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable corepack
|
||||||
|
run: corepack enable
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm run build
|
||||||
|
- name: Coverage
|
||||||
|
run: pnpm run coverage
|
||||||
|
- name: Upload to Codecov
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: ./coverage/lcov.info
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
[](https://www.npmjs.com/package/hijri-core)
|
[](https://www.npmjs.com/package/hijri-core)
|
||||||
[](https://github.com/acamarata/hijri-core/actions/workflows/ci.yml)
|
[](https://github.com/acamarata/hijri-core/actions/workflows/ci.yml)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
[](https://github.com/acamarata/hijri-core/wiki)
|
||||||
|
|
||||||
Zero-dependency Hijri calendar engine for JavaScript and TypeScript. Supports the Umm al-Qura (UAQ) and FCNA/ISNA calendars out of the box. A pluggable registry lets you add custom calendar implementations at runtime.
|
Zero-dependency Hijri calendar engine for JavaScript and TypeScript. Supports the Umm al-Qura (UAQ) and FCNA/ISNA calendars out of the box. A pluggable registry lets you add custom calendar implementations at runtime.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
/** Milliseconds in one day. */
|
/**
|
||||||
|
* Milliseconds in one day (24 * 60 * 60 * 1000).
|
||||||
|
*
|
||||||
|
* Used internally for day-offset arithmetic when converting between Gregorian
|
||||||
|
* timestamps and Hijri dates. Exposed as a public constant so custom engine
|
||||||
|
* authors can share the same value without redefining it.
|
||||||
|
*/
|
||||||
export const MS_PER_DAY = 86_400_000;
|
export const MS_PER_DAY = 86_400_000;
|
||||||
|
|
||||||
/** Number of months in a Hijri year. */
|
/**
|
||||||
|
* Number of months in a Hijri year.
|
||||||
|
*
|
||||||
|
* The Islamic calendar is purely lunar: 12 months of 29 or 30 days each,
|
||||||
|
* totalling 354 or 355 days per year. This constant is 12.
|
||||||
|
*/
|
||||||
export const MONTHS_PER_YEAR = 12;
|
export const MONTHS_PER_YEAR = 12;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
// Hijri month names in three forms.
|
// Hijri month names in three forms.
|
||||||
// Index 0 = Muharram (month 1), index 11 = Dhul Hijjah (month 12).
|
// Index 0 = Muharram (month 1), index 11 = Dhul Hijjah (month 12).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full English transliterations of the 12 Hijri month names.
|
||||||
|
*
|
||||||
|
* Index 0 corresponds to Muharram (month 1); index 11 to Dhul Hijjah (month 12).
|
||||||
|
* Suitable for display in contexts where the full name aids readability.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const month = hmLong[hijriDate.hm - 1]; // "Ramadan"
|
||||||
|
*/
|
||||||
export const hmLong = [
|
export const hmLong = [
|
||||||
'Muharram', // 1
|
'Muharram', // 1
|
||||||
'Safar', // 2
|
'Safar', // 2
|
||||||
|
|
@ -16,6 +25,15 @@ export const hmLong = [
|
||||||
'Dhul Hijjah', // 12
|
'Dhul Hijjah', // 12
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium-length transliterations of the 12 Hijri month names.
|
||||||
|
*
|
||||||
|
* Shorter than {@link hmLong} but more readable than {@link hmShort}.
|
||||||
|
* Useful for compact date labels where space is limited.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const label = hmMedium[hijriDate.hm - 1]; // "Ramadan"
|
||||||
|
*/
|
||||||
export const hmMedium = [
|
export const hmMedium = [
|
||||||
'Muharram',
|
'Muharram',
|
||||||
'Safar',
|
'Safar',
|
||||||
|
|
@ -31,6 +49,15 @@ export const hmMedium = [
|
||||||
'Dhul-Hijjah',
|
'Dhul-Hijjah',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Three-character short codes for the 12 Hijri months.
|
||||||
|
*
|
||||||
|
* Designed for narrow columns such as calendar grids or spreadsheet headers.
|
||||||
|
* Each code is exactly 3 ASCII characters.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const abbr = hmShort[hijriDate.hm - 1]; // "Ram"
|
||||||
|
*/
|
||||||
export const hmShort = [
|
export const hmShort = [
|
||||||
'Muh',
|
'Muh',
|
||||||
'Saf',
|
'Saf',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
// Hijri weekday names.
|
// Hijri weekday names.
|
||||||
// Index 0 = Sunday, index 6 = Saturday (matching JS Date.getDay()).
|
// Index 0 = Sunday, index 6 = Saturday (matching JS Date.getDay()).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full Arabic-transliterated names for the seven days of the week.
|
||||||
|
*
|
||||||
|
* Index alignment matches `Date.prototype.getDay()`:
|
||||||
|
* index 0 = Sunday, index 6 = Saturday.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const dayName = hwLong[gregorianDate.getDay()]; // "Yawm al-Jum`a"
|
||||||
|
*/
|
||||||
export const hwLong = [
|
export const hwLong = [
|
||||||
'Yawm al-Ahad', // Sunday
|
'Yawm al-Ahad', // Sunday
|
||||||
'Yawm al-Ithnayn', // Monday
|
'Yawm al-Ithnayn', // Monday
|
||||||
|
|
@ -11,6 +20,15 @@ export const hwLong = [
|
||||||
'Yawm as-Sabt', // Saturday
|
'Yawm as-Sabt', // Saturday
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Short single-word transliterations for the seven days of the week.
|
||||||
|
*
|
||||||
|
* Index alignment matches `Date.prototype.getDay()`:
|
||||||
|
* index 0 = Sunday, index 6 = Saturday.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const abbr = hwShort[gregorianDate.getDay()]; // "Jum`a"
|
||||||
|
*/
|
||||||
export const hwShort = [
|
export const hwShort = [
|
||||||
'Ahad', // Sunday
|
'Ahad', // Sunday
|
||||||
'Ithn', // Monday
|
'Ithn', // Monday
|
||||||
|
|
@ -21,5 +39,12 @@ export const hwShort = [
|
||||||
'Sabt', // Saturday
|
'Sabt', // Saturday
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Numeric weekday values: 1 = Sunday through 7 = Saturday.
|
||||||
|
*
|
||||||
|
* This follows the ISO 8601 convention where Monday = 1, but offset by one
|
||||||
|
* to match the Islamic numbering where Sunday is the first day of the week.
|
||||||
|
* Index alignment matches `Date.prototype.getDay()`.
|
||||||
|
*/
|
||||||
// Numeric representation: 1 = Sunday, 7 = Saturday.
|
// Numeric representation: 1 = Sunday, 7 = Saturday.
|
||||||
export const hwNumeric = [1, 2, 3, 4, 5, 6, 7];
|
export const hwNumeric = [1, 2, 3, 4, 5, 6, 7];
|
||||||
|
|
|
||||||
36
src/types.ts
36
src/types.ts
|
|
@ -1,18 +1,44 @@
|
||||||
|
/**
|
||||||
|
* A Hijri date triple.
|
||||||
|
*
|
||||||
|
* All three fields are required. Month and day are 1-based.
|
||||||
|
* The year is a Hijri (AH) year number, e.g. 1446.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const d: HijriDate = { hy: 1446, hm: 9, hd: 1 }; // 1 Ramadan 1446 AH
|
||||||
|
*/
|
||||||
export interface HijriDate {
|
export interface HijriDate {
|
||||||
hy: number; // Hijri year
|
hy: number; // Hijri year
|
||||||
hm: number; // Hijri month (1-12)
|
hm: number; // Hijri month (1-12)
|
||||||
hd: number; // Hijri day (1-30)
|
hd: number; // Hijri day (1-30)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One row in the Umm al-Qura reference table.
|
||||||
|
*
|
||||||
|
* The table covers Hijri years 1318-1500 (Gregorian 1900-2076). A sentinel row
|
||||||
|
* at hy=1501 with dpm=0 marks the upper boundary and is used to detect
|
||||||
|
* out-of-range inputs without a separate bounds check.
|
||||||
|
*
|
||||||
|
* The `dpm` bitmask encodes month lengths for all 12 months:
|
||||||
|
* bit i (0-indexed from bit 0) = month i+1; 1 = 30 days, 0 = 29 days.
|
||||||
|
*/
|
||||||
export interface HijriYearRecord {
|
export interface HijriYearRecord {
|
||||||
hy: number; // Hijri year
|
hy: number; // Hijri year
|
||||||
dpm: number; // days-per-month bitmask (bit 0 = month 1: 1 -> 30 days, 0 -> 29 days)
|
dpm: number; // 12-bit days-per-month bitmask (bit 0 = month 1: 1 -> 30 days, 0 -> 29 days)
|
||||||
gy: number; // Gregorian year of 1 Muharram
|
gy: number; // Gregorian year of 1 Muharram
|
||||||
gm: number; // Gregorian month of 1 Muharram (1-based)
|
gm: number; // Gregorian month of 1 Muharram (1-based)
|
||||||
gd: number; // Gregorian day of 1 Muharram
|
gd: number; // Gregorian day of 1 Muharram
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any calendar engine must implement this interface.
|
/**
|
||||||
|
* Interface every calendar engine must implement.
|
||||||
|
*
|
||||||
|
* Return `null` when a date is outside the engine's supported range.
|
||||||
|
* Throw `Error` for structurally invalid input (malformed Date, month outside 1-12, etc.).
|
||||||
|
* Never throw for out-of-range inputs — return `null` instead so callers can handle
|
||||||
|
* the boundary gracefully without try/catch.
|
||||||
|
*/
|
||||||
export interface CalendarEngine {
|
export interface CalendarEngine {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
toHijri(date: Date): HijriDate | null;
|
toHijri(date: Date): HijriDate | null;
|
||||||
|
|
@ -22,6 +48,12 @@ export interface CalendarEngine {
|
||||||
daysInMonth(hy: number, hm: number): number;
|
daysInMonth(hy: number, hm: number): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options accepted by the convenience conversion functions.
|
||||||
|
*
|
||||||
|
* Omitting `calendar` defaults to `'uaq'` (Umm al-Qura).
|
||||||
|
* Pass any name previously given to {@link registerCalendar} to use a custom engine.
|
||||||
|
*/
|
||||||
export interface ConversionOptions {
|
export interface ConversionOptions {
|
||||||
calendar?: string; // defaults to 'uaq'
|
calendar?: string; // defaults to 'uaq'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue