diff --git a/.github/wiki/examples/gregorian-to-hijri.md b/.github/wiki/examples/gregorian-to-hijri.md new file mode 100644 index 0000000..0113613 --- /dev/null +++ b/.github/wiki/examples/gregorian-to-hijri.md @@ -0,0 +1,48 @@ +# Example: Gregorian to Hijri Conversion Table + +Convert a range of notable Gregorian dates to Hijri. + +```js +import { toHijri } 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 = [ + { label: 'Islamic New Year 1446', date: new Date('2024-07-07') }, + { label: 'Ashura 1446', date: new Date('2024-07-16') }, + { label: 'Ramadan 1446 start', date: new Date('2025-03-01') }, + { label: 'Eid al-Fitr 1446', date: new Date('2025-03-30') }, + { label: 'Arafat Day 1446', date: new Date('2025-06-05') }, + { label: 'Eid al-Adha 1446', date: new Date('2025-06-06') }, +]; + +console.log(`${'Event'.padEnd(26)} Gregorian Hijri`); +console.log('-'.repeat(62)); + +for (const { label, date } of dates) { + const h = toHijri(date); + const greg = date.toISOString().slice(0, 10); + const hijri = h + ? `${h.day} ${MONTH_NAMES[h.month]} ${h.year} AH` + : 'out of range'; + + console.log(`${label.padEnd(26)} ${greg} ${hijri}`); +} +``` + +Sample output: + +``` +Event Gregorian Hijri +-------------------------------------------------------------- +Islamic New Year 1446 2024-07-07 1 Muharram 1446 AH +Ashura 1446 2024-07-16 10 Muharram 1446 AH +Ramadan 1446 start 2025-03-01 1 Ramadan 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 +Eid al-Adha 1446 2025-06-06 10 Dhu al-Hijjah 1446 AH +``` diff --git a/.github/wiki/examples/ramadan-calendar.md b/.github/wiki/examples/ramadan-calendar.md new file mode 100644 index 0000000..0214ca5 --- /dev/null +++ b/.github/wiki/examples/ramadan-calendar.md @@ -0,0 +1,53 @@ +# Example: Ramadan Calendar Generator + +Generate the full Ramadan calendar for a given Hijri year showing Gregorian dates. + +```js +import { toGregorian, daysInHijriMonth } from 'hijri-core'; + +const HIJRI_YEAR = 1446; +const RAMADAN = 9; + +const totalDays = daysInHijriMonth(HIJRI_YEAR, RAMADAN); + +console.log(`Ramadan ${HIJRI_YEAR} AH — ${totalDays} days`); +console.log(''); +console.log('Day Gregorian Weekday'); +console.log('-'.repeat(32)); + +const DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + +for (let d = 1; d <= totalDays; d++) { + const greg = toGregorian(HIJRI_YEAR, RAMADAN, d); + const iso = greg.toISOString().slice(0, 10); + const weekday = DAYS[greg.getUTCDay()]; + + // Mark Jumu'ah + const marker = weekday === 'Friday' ? ' ★' : ''; + + console.log(`${String(d).padStart(3)} ${iso} ${weekday}${marker}`); +} +``` + +Sample output: + +``` +Ramadan 1446 AH — 30 days + +Day Gregorian Weekday +-------------------------------- + 1 2025-03-01 Saturday + 2 2025-03-02 Sunday + 3 2025-03-03 Monday + 4 2025-03-04 Tuesday + 5 2025-03-05 Wednesday + 6 2025-03-06 Thursday + 7 2025-03-07 Friday ★ + 8 2025-03-08 Saturday + ... + 28 2025-03-28 Friday ★ + 29 2025-03-29 Saturday + 30 2025-03-30 Sunday +``` + +> Eid al-Fitr falls on 1 Shawwal — the day after the last day of Ramadan. diff --git a/.github/wiki/guides/advanced.md b/.github/wiki/guides/advanced.md new file mode 100644 index 0000000..c35f00d --- /dev/null +++ b/.github/wiki/guides/advanced.md @@ -0,0 +1,130 @@ +# Advanced Usage + +## Iterating over a Hijri month + +Build a calendar grid for a given Hijri month: + +```js +import { daysInHijriMonth, toGregorian } from 'hijri-core'; + +const HY = 1446; +const HM = 9; // Ramadan + +const days = daysInHijriMonth(HY, HM); + +for (let d = 1; d <= days; d++) { + const greg = toGregorian(HY, HM, d); + console.log(`${String(d).padStart(2)} Ramadan — ${greg.toISOString().slice(0, 10)}`); +} +``` + +## Custom calendar engines + +`hijri-core` exposes a registry so you can plug in your own conversion engine — a lunar sighting authority, an adjusted algorithmic calendar, or a custom dataset: + +```js +import { registerCalendar, toHijri } from 'hijri-core'; + +registerCalendar('my-calendar', { + toHijri(date) { + // Return { year, month, day, monthName, calendar: 'my-calendar' } or null + // date is a JS Date; use local components for timezone-safe lookup + const y = date.getFullYear(); + const m = date.getMonth() + 1; + const d = date.getDate(); + // ... your logic + return null; + }, + toGregorian(hy, hm, hd) { + // Return a Date (UTC midnight) or null for out-of-range + return null; + }, + isValidHijriDate(hy, hm, hd) { + return false; + }, + daysInHijriMonth(hy, hm) { + return 29; + }, +}); + +// Use it just like the built-in calendars +const result = toHijri(new Date('2025-03-20'), { calendar: 'my-calendar' }); +``` + +## Generating a Ramadan calendar + +Print the Gregorian dates for Ramadan across multiple years: + +```js +import { toGregorian } from 'hijri-core'; + +const RAMADAN = 9; + +for (let hy = 1440; hy <= 1450; hy++) { + const start = toGregorian(hy, RAMADAN, 1); + const end = toGregorian(hy, RAMADAN + 1, 1); + + if (!start) continue; + + // Ramadan ends the day before Shawwal 1 + const last = new Date(end.getTime() - 86400_000); + + console.log( + `${hy} AH: ${start.toISOString().slice(0, 10)} — ${last.toISOString().slice(0, 10)}` + ); +} +``` + +## Date range validation + +Before batch-processing a range of dates, check the calendar bounds: + +```js +import { toHijri } from 'hijri-core'; + +function isSupportedDate(date) { + return toHijri(date) !== null; +} + +const dates = [ + new Date('1900-01-01'), // outside UAQ range + new Date('2000-01-01'), // inside range + new Date('2077-11-16'), // may be outside future range +]; + +for (const d of dates) { + const supported = isSupportedDate(d); + console.log(`${d.toISOString().slice(0, 10)}: ${supported ? 'supported' : 'out of range'}`); +} +``` + +## FCNA vs UAQ differences + +FCNA and UAQ can differ by one or two days around month transitions: + +```js +import { toHijri } from 'hijri-core'; + +const dates = [ + new Date('2025-03-01'), + new Date('2025-03-29'), + new Date('2025-03-30'), + new Date('2025-03-31'), +]; + +console.log('Date UAQ FCNA'); +console.log('-'.repeat(48)); + +for (const d of dates) { + const uaq = toHijri(d, { calendar: 'uaq' }); + const fcna = toHijri(d, { calendar: 'fcna' }); + + const fmtH = (h) => h ? `${h.day}/${h.month}/${h.year}` : 'out of range'; + console.log(`${d.toISOString().slice(0, 10)} ${fmtH(uaq).padEnd(16)} ${fmtH(fcna)}`); +} +``` + +## Related pages + +- [API Reference](../API-Reference) — full function signatures and types +- [Architecture](../Architecture) — calendar engine interface, table format, accuracy bounds diff --git a/.github/wiki/guides/quickstart.md b/.github/wiki/guides/quickstart.md new file mode 100644 index 0000000..ede3ad2 --- /dev/null +++ b/.github/wiki/guides/quickstart.md @@ -0,0 +1,94 @@ +# Quick Start + +Five minutes from install to Hijri date conversions. + +## Install + +```sh +npm install hijri-core +``` + +## Convert Gregorian to Hijri + +```js +import { toHijri } from 'hijri-core'; + +const d = new Date('2025-03-20'); +const h = toHijri(d); + +console.log(h); +// { year: 1446, month: 9, day: 20, monthName: 'Ramadan', calendar: 'uaq' } + +console.log(`${h.day} ${h.monthName} ${h.year} AH`); +// 20 Ramadan 1446 AH +``` + +## Convert Hijri to Gregorian + +```js +import { toGregorian } from 'hijri-core'; + +const greg = toGregorian(1446, 9, 1); + +console.log(greg.toISOString().slice(0, 10)); +// 2025-03-01 +``` + +## Check a Hijri date is valid + +```js +import { isValidHijriDate } from 'hijri-core'; + +isValidHijriDate(1446, 9, 30); // true — Ramadan 1446 has 30 days +isValidHijriDate(1446, 9, 31); // false — no 31st day in any Hijri month +isValidHijriDate(1446, 13, 1); // false — Hijri calendar has only 12 months +``` + +## Days in a month + +```js +import { daysInHijriMonth } from 'hijri-core'; + +daysInHijriMonth(1446, 9); // 30 (Ramadan 1446) +daysInHijriMonth(1446, 10); // 29 (Shawwal 1446) +``` + +## Choosing a calendar + +Two calendars are built in: + +| Name | Description | +| ---- | ----------- | +| `'uaq'` | Umm al-Qura (Saudi Arabia, official tabular calendar) — **default** | +| `'fcna'` | Fiqh Council of North America (astronomical computation for North America) | + +```js +import { toHijri } from 'hijri-core'; + +const d = new Date('2025-03-20'); + +const uaq = toHijri(d, { calendar: 'uaq' }); +const fcna = toHijri(d, { calendar: 'fcna' }); + +console.log(uaq.day, uaq.month, uaq.year); +console.log(fcna.day, fcna.month, fcna.year); +``` + +## Out-of-range dates + +Both built-in calendars cover a finite date range. `toHijri` and `toGregorian` return `null` when the input falls outside the supported range: + +```js +import { toHijri } from 'hijri-core'; + +const result = toHijri(new Date('1800-01-01')); +if (result === null) { + console.log('Date outside calendar range'); +} +``` + +## Next steps + +- [API Reference](../API-Reference) — all functions and types +- [Advanced Guide](advanced) — custom calendar engines, iteration, Ramadan calendars +- [Architecture](../Architecture) — calendar engine design, table format