luxon-hijri/.github/wiki/guides/advanced.md

3.3 KiB
Raw Blame History

Advanced Usage

Format tokens

formatHijriDate supports these tokens:

Token Example Description
D 130 Hijri day, no padding
DD 0130 Hijri day, zero-padded
M 112 Hijri month number, no padding
MM 0112 Hijri month number, zero-padded
MMMM Ramadan Full Hijri month name
MMMMM Ramaḍān (transliteration variant) Extended name (where available)
YY 46 Last two digits of Hijri year
YYYY 1446 Full Hijri year
import { toHijri, formatHijriDate } from 'luxon-hijri';

const h = toHijri(new Date('2025-03-20'));

console.log(formatHijriDate(h, 'DD/MM/YYYY'));    // 20/09/1446
console.log(formatHijriDate(h, 'D MMMM YYYY'));   // 20 Ramadan 1446
console.log(formatHijriDate(h, 'D MMMM YYYY AH')); // 20 Ramadan 1446 AH

Hijri date arithmetic with Luxon

Luxon handles Gregorian arithmetic. Combine with hijri-core conversions for Hijri-aware date math:

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

// Find the last day of this Ramadan
const today = new Date();
const h = toHijri(today);

if (h) {
  const lastDay  = daysInHijriMonth(h.hy, 9);   // 29 or 30
  const eidStart = toGregorian(h.hy, 10, 1);     // 1 Shawwal

  const eid = DateTime.fromJSDate(eidStart);
  console.log(`Eid al-Fitr ${h.hy}: ${eid.toFormat('MMMM d, yyyy')}`);
}

Generating a Hijri month calendar

import { toGregorian, daysInHijriMonth } from 'luxon-hijri';
import { DateTime } from 'luxon';

const HY = 1446;
const HM = 9; // Ramadan

const days  = daysInHijriMonth(HY, HM);
const NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

console.log(`Ramadan ${HY}\n`);
console.log(NAMES.join('  '));

const firstGreg = DateTime.fromJSDate(toGregorian(HY, HM, 1));
let line = '     '.repeat(firstGreg.weekday % 7); // Sunday = 0

for (let d = 1; d <= days; d++) {
  const greg = DateTime.fromJSDate(toGregorian(HY, HM, d));
  line += String(d).padStart(3) + '  ';
  if (greg.weekday === 6) { // Saturday ends row
    console.log(line);
    line = '';
  }
}
if (line.trim()) console.log(line);

FCNA vs UAQ differences

FCNA and UAQ can differ by a day around month transitions:

import { toHijri } from 'luxon-hijri';

const borderDates = [
  new Date('2025-02-28'),
  new Date('2025-03-01'),
  new Date('2025-03-02'),
];

for (const d of borderDates) {
  const uaq  = toHijri(d, { calendar: 'uaq' });
  const fcna = toHijri(d, { calendar: 'fcna' });

  const fmt = h => h ? `${h.hd}/${h.hm}/${h.hy}` : 'null';
  console.log(`${d.toISOString().slice(0, 10)}  UAQ: ${fmt(uaq)}  FCNA: ${fmt(fcna)}`);
}

Batch conversion

import { toHijri } from 'luxon-hijri';

const isoList = [
  '2025-01-01', '2025-03-01', '2025-03-30',
  '2025-06-06', '2025-12-31',
];

for (const iso of isoList) {
  const h = toHijri(new Date(iso));
  const result = h ? `${h.hd}/${h.hm}/${h.hy} AH` : 'out of range';
  console.log(`${iso}${result}`);
}