UEA-Prodem

This commit is contained in:
2026-06-10 12:38:42 -03:00
parent 3f33154e16
commit c41625e542
9352 changed files with 1128292 additions and 14752 deletions
+28
View File
@@ -0,0 +1,28 @@
import type { Locale } from "date-fns";
import React from "react";
import { DateLib, type DateLibOptions } from "../index.js";
import type { DayPickerProps } from "../types/props.js";
/**
* Render the Hijri (Umm al-Qura) calendar.
*
* Defaults:
*
* - `locale`: `ar-SA`
* - `dir`: `rtl`
* - `numerals`: `arab`
* - `startMonth`: `1924-08-01`
* - `endMonth`: `2077-11-16`
*
* Out-of-range date props are clamped to the supported Umm al-Qura conversion
* range, preventing runtime conversion errors.
*/
export declare function DayPicker(props: DayPickerProps & {
locale?: Locale;
dir?: DayPickerProps["dir"];
numerals?: DayPickerProps["numerals"];
dateLib?: DayPickerProps["dateLib"];
}): React.JSX.Element;
/** Returns the date library used in the Hijri calendar. */
export declare const getDateLib: (options?: DateLibOptions) => DateLib;
export { arSA } from "../locale/ar-SA.js";
export { enUS } from "../locale/en-US.js";
+98
View File
@@ -0,0 +1,98 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.enUS = exports.arSA = exports.getDateLib = void 0;
exports.DayPicker = DayPicker;
const react_1 = __importDefault(require("react"));
const index_js_1 = require("../index.js");
const ar_SA_js_1 = require("../locale/ar-SA.js");
const hijriDateLib = __importStar(require("./lib/index.js"));
const range_js_1 = require("./utils/range.js");
function clampDateProp(date) {
if (!date) {
return undefined;
}
return (0, range_js_1.clampGregorianDate)(date);
}
/**
* Render the Hijri (Umm al-Qura) calendar.
*
* Defaults:
*
* - `locale`: `ar-SA`
* - `dir`: `rtl`
* - `numerals`: `arab`
* - `startMonth`: `1924-08-01`
* - `endMonth`: `2077-11-16`
*
* Out-of-range date props are clamped to the supported Umm al-Qura conversion
* range, preventing runtime conversion errors.
*/
function DayPicker(props) {
const { dateLib: dateLibProp, ...dayPickerProps } = props;
const hasStartBound = props.startMonth !== undefined ||
props.fromMonth !== undefined ||
props.fromYear !== undefined;
const hasEndBound = props.endMonth !== undefined ||
props.toMonth !== undefined ||
props.toYear !== undefined;
const clampedProps = {
...dayPickerProps,
month: clampDateProp(props.month),
defaultMonth: clampDateProp(props.defaultMonth),
today: clampDateProp(props.today),
startMonth: hasStartBound
? clampDateProp(props.startMonth)
: new Date(range_js_1.GREGORIAN_MIN_DATE),
endMonth: hasEndBound
? clampDateProp(props.endMonth)
: new Date(range_js_1.GREGORIAN_MAX_DATE),
fromMonth: clampDateProp(props.fromMonth),
toMonth: clampDateProp(props.toMonth),
};
return (react_1.default.createElement(index_js_1.DayPicker, { ...clampedProps, locale: props.locale ?? ar_SA_js_1.arSA, numerals: props.numerals ?? "arab", dir: props.dir ?? "rtl", dateLib: { ...hijriDateLib, ...dateLibProp } }));
}
/** Returns the date library used in the Hijri calendar. */
const getDateLib = (options) => {
return new index_js_1.DateLib(options, hijriDateLib);
};
exports.getDateLib = getDateLib;
var ar_SA_js_2 = require("../locale/ar-SA.js");
Object.defineProperty(exports, "arSA", { enumerable: true, get: function () { return ar_SA_js_2.arSA; } });
var en_US_js_1 = require("../locale/en-US.js");
Object.defineProperty(exports, "enUS", { enumerable: true, get: function () { return en_US_js_1.enUS; } });
+1
View File
@@ -0,0 +1 @@
export declare function addMonths(date: Date, amount: number): Date;
+9
View File
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addMonths = addMonths;
const conversion_js_1 = require("../utils/conversion.js");
const setMonth_js_1 = require("./setMonth.js");
function addMonths(date, amount) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
return (0, setMonth_js_1.setMonth)(date, hijri.monthIndex + amount);
}
+1
View File
@@ -0,0 +1 @@
export declare function addYears(date: Date, amount: number): Date;
+9
View File
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addYears = addYears;
const conversion_js_1 = require("../utils/conversion.js");
const setYear_js_1 = require("./setYear.js");
function addYears(date, amount) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
return (0, setYear_js_1.setYear)(date, hijri.year + amount);
}
@@ -0,0 +1 @@
export declare function differenceInCalendarMonths(dateLeft: Date, dateRight: Date): number;
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.differenceInCalendarMonths = differenceInCalendarMonths;
const conversion_js_1 = require("../utils/conversion.js");
function differenceInCalendarMonths(dateLeft, dateRight) {
const hijriLeft = (0, conversion_js_1.toHijriDate)(dateLeft);
const hijriRight = (0, conversion_js_1.toHijriDate)(dateRight);
return ((hijriLeft.year - hijriRight.year) * 12 +
(hijriLeft.monthIndex - hijriRight.monthIndex));
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachMonthOfInterval(interval: Interval): Date[];
@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.eachMonthOfInterval = eachMonthOfInterval;
const date_fns_1 = require("date-fns");
const conversion_js_1 = require("../utils/conversion.js");
function eachMonthOfInterval(interval) {
const start = (0, date_fns_1.toDate)(interval.start);
const end = (0, date_fns_1.toDate)(interval.end);
if (end.getTime() < start.getTime()) {
throw new RangeError("Invalid interval");
}
const startDate = (0, conversion_js_1.toHijriDate)(start);
const endDate = (0, conversion_js_1.toHijriDate)(end);
const months = [];
let currentYear = startDate.year;
let currentMonth = startDate.monthIndex;
const endYear = endDate.year;
const endMonth = endDate.monthIndex;
while (currentYear < endYear ||
(currentYear === endYear && currentMonth <= endMonth)) {
months.push((0, conversion_js_1.toGregorianDate)({ year: currentYear, monthIndex: currentMonth, day: 1 }));
currentMonth += 1;
if (currentMonth > 11) {
currentMonth = 0;
currentYear += 1;
}
}
return months;
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachYearOfInterval(interval: Interval): Date[];
+19
View File
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.eachYearOfInterval = eachYearOfInterval;
const date_fns_1 = require("date-fns");
const conversion_js_1 = require("../utils/conversion.js");
function eachYearOfInterval(interval) {
const start = (0, date_fns_1.toDate)(interval.start);
const end = (0, date_fns_1.toDate)(interval.end);
if (end.getTime() < start.getTime()) {
return [];
}
const startYear = (0, conversion_js_1.toHijriDate)(start).year;
const endYear = (0, conversion_js_1.toHijriDate)(end).year;
const years = [];
for (let year = startYear; year <= endYear; year += 1) {
years.push((0, conversion_js_1.toGregorianDate)({ year, monthIndex: 0, day: 1 }));
}
return years;
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfMonth(date: Date): Date;
+14
View File
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.endOfMonth = endOfMonth;
const conversion_js_1 = require("../utils/conversion.js");
const daysInMonth_js_1 = require("../utils/daysInMonth.js");
function endOfMonth(date) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
const day = (0, daysInMonth_js_1.getDaysInMonth)(hijri.year, hijri.monthIndex);
return (0, conversion_js_1.toGregorianDate)({
year: hijri.year,
monthIndex: hijri.monthIndex,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfYear(date: Date): Date;
+14
View File
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.endOfYear = endOfYear;
const conversion_js_1 = require("../utils/conversion.js");
const daysInMonth_js_1 = require("../utils/daysInMonth.js");
function endOfYear(date) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
const day = (0, daysInMonth_js_1.getDaysInMonth)(hijri.year, 11);
return (0, conversion_js_1.toGregorianDate)({
year: hijri.year,
monthIndex: 11,
day,
});
}
@@ -0,0 +1,5 @@
export type FallbackLocaleCode = "ar" | "en";
export type IntlNameWidth = "long" | "short" | "narrow";
export declare const getFallbackLocaleCode: (localeCode: string) => FallbackLocaleCode;
export declare const getFallbackMonthName: (date: Date, localeCode: string, width: IntlNameWidth) => string;
export declare const getFallbackWeekdayName: (date: Date, localeCode: string, width: IntlNameWidth) => string;
+111
View File
@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFallbackWeekdayName = exports.getFallbackMonthName = exports.getFallbackLocaleCode = void 0;
const conversion_js_1 = require("../utils/conversion.js");
const fallbackMonthNames = {
en: {
long: [
"Muharram",
"Safar",
"Rabi I",
"Rabi II",
"Jumada I",
"Jumada II",
"Rajab",
"Shaban",
"Ramadan",
"Shawwal",
"Dhu al-Qadah",
"Dhu al-Hijjah",
],
short: [
"Muh",
"Saf",
"Rab-I",
"Rab-II",
"Jum-I",
"Jum-II",
"Raj",
"Sha",
"Ram",
"Shw",
"Dhu-Q",
"Dhu-H",
],
narrow: ["M", "S", "R", "R", "J", "J", "R", "S", "R", "S", "D", "D"],
},
ar: {
long: [
"محرم",
"صفر",
"ربيع الأول",
"ربيع الآخر",
"جمادى الأولى",
"جمادى الآخرة",
"رجب",
"شعبان",
"رمضان",
"شوال",
"ذو القعدة",
"ذو الحجة",
],
short: [
"محرم",
"صفر",
"ربيع ١",
"ربيع ٢",
"جمادى ١",
"جمادى ٢",
"رجب",
"شعبان",
"رمضان",
"شوال",
"ذو القعدة",
"ذو الحجة",
],
narrow: ["م", "ص", "ر", "ر", "ج", "ج", "ر", "ش", "ر", "ش", "ذ", "ذ"],
},
};
const fallbackWeekdayNames = {
en: {
long: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
short: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
narrow: ["S", "M", "T", "W", "T", "F", "S"],
},
ar: {
long: [
"الأحد",
"الاثنين",
"الثلاثاء",
"الأربعاء",
"الخميس",
"الجمعة",
"السبت",
],
short: ["أحد", "اثن", "ثلا", "أرب", "خمي", "جمع", "سبت"],
narrow: ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
},
};
const getFallbackLocaleCode = (localeCode) => {
return localeCode.toLowerCase().startsWith("ar") ? "ar" : "en";
};
exports.getFallbackLocaleCode = getFallbackLocaleCode;
const getFallbackMonthName = (date, localeCode, width) => {
const hijri = (0, conversion_js_1.toHijriDate)(date);
const locale = (0, exports.getFallbackLocaleCode)(localeCode);
return fallbackMonthNames[locale][width][hijri.monthIndex];
};
exports.getFallbackMonthName = getFallbackMonthName;
const getFallbackWeekdayName = (date, localeCode, width) => {
const locale = (0, exports.getFallbackLocaleCode)(localeCode);
return fallbackWeekdayNames[locale][width][date.getDay()];
};
exports.getFallbackWeekdayName = getFallbackWeekdayName;
+3
View File
@@ -0,0 +1,3 @@
import type { FormatOptions as DateFnsFormatOptions } from "date-fns";
/** Hijri calendar formatting override. */
export declare function format(date: Date, formatStr: string, options?: DateFnsFormatOptions): string;
+149
View File
@@ -0,0 +1,149 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.format = format;
const conversion_js_1 = require("../utils/conversion.js");
const range_js_1 = require("../utils/range.js");
const fallbackLocaleData_js_1 = require("./fallbackLocaleData.js");
const DEFAULT_LOCALE_CODE = "ar-SA";
const BASE_NUMBERING_SYSTEM = "latn";
const UMM_AL_QURA_CALENDAR = "islamic-umalqura";
const getLocaleCode = (options) => {
return options?.locale?.code ?? DEFAULT_LOCALE_CODE;
};
const formatWithUmmAlQura = (date, localeCode, options) => {
try {
return new Intl.DateTimeFormat(localeCode, {
...options,
calendar: UMM_AL_QURA_CALENDAR,
numberingSystem: BASE_NUMBERING_SYSTEM,
}).format(date);
}
catch {
return undefined;
}
};
const formatNumber = (value) => {
return value.toString();
};
const formatPaddedNumber = (value) => {
return formatNumber(value).padStart(2, "0");
};
const formatMonthName = (date, localeCode, width) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
month: width,
});
return formatted ?? (0, fallbackLocaleData_js_1.getFallbackMonthName)(date, localeCode, width);
};
const formatWeekdayName = (date, localeCode, width) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
weekday: width,
});
return formatted ?? (0, fallbackLocaleData_js_1.getFallbackWeekdayName)(date, localeCode, width);
};
const formatDateStyle = (date, localeCode, style) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
dateStyle: style,
});
if (formatted) {
return formatted;
}
const hijri = (0, conversion_js_1.toHijriDate)(date);
const monthName = (0, fallbackLocaleData_js_1.getFallbackMonthName)(date, localeCode, "long");
switch (style) {
case "full":
return `${(0, fallbackLocaleData_js_1.getFallbackWeekdayName)(date, localeCode, "long")}, ${monthName} ${hijri.day}, ${hijri.year}`;
case "long":
return `${monthName} ${hijri.day}, ${hijri.year}`;
case "medium":
return `${formatPaddedNumber(hijri.day)} ${monthName} ${hijri.year}`;
case "short":
return `${hijri.monthIndex + 1}/${hijri.day}/${hijri.year}`;
}
};
const buildTimeFormat = (date, localeCode, formatStr) => {
const hour12 = formatStr.includes("a");
const formatted = formatWithUmmAlQura(date, localeCode, {
hour: "numeric",
minute: "numeric",
hour12,
});
if (formatted) {
return formatted;
}
try {
return new Intl.DateTimeFormat(localeCode, {
hour: "numeric",
minute: "numeric",
hour12,
numberingSystem: BASE_NUMBERING_SYSTEM,
}).format(date);
}
catch {
const minutes = formatPaddedNumber(date.getMinutes());
if (hour12) {
const hour = date.getHours() % 12 || 12;
const period = date.getHours() >= 12 ? "PM" : "AM";
return `${hour}:${minutes} ${period}`;
}
return `${formatNumber(date.getHours())}:${minutes}`;
}
};
/** Hijri calendar formatting override. */
function format(date, formatStr, options) {
const extendedOptions = options;
const localeCode = getLocaleCode(extendedOptions);
const hijri = (0, conversion_js_1.toHijriDate)(date);
const gregorian = (0, range_js_1.getGregorianDateParts)(date);
const isOutOfRange = (0, range_js_1.clampGregorianDate)(date) !== date;
const numericDate = isOutOfRange
? {
year: gregorian.year,
monthIndex: gregorian.month - 1,
day: gregorian.day,
}
: hijri;
switch (formatStr) {
case "LLLL y":
case "LLLL yyyy":
return `${formatMonthName(date, localeCode, "long")} ${formatNumber(numericDate.year)}`;
case "LLLL":
return formatMonthName(date, localeCode, "long");
case "LLL":
return formatMonthName(date, localeCode, "short");
case "PPP":
return formatDateStyle(date, localeCode, "long");
case "PPPP":
return formatDateStyle(date, localeCode, "full");
case "PP":
return formatDateStyle(date, localeCode, "medium");
case "P":
return formatDateStyle(date, localeCode, "short");
case "cccc":
return formatWeekdayName(date, localeCode, "long");
case "ccc":
return formatWeekdayName(date, localeCode, "short");
case "ccccc":
case "cccccc":
return formatWeekdayName(date, localeCode, "narrow");
case "yyyy":
case "y":
return formatNumber(numericDate.year);
case "yyyy-MM":
return `${formatNumber(numericDate.year)}-${formatPaddedNumber(numericDate.monthIndex + 1)}`;
case "yyyy-MM-dd":
return `${formatNumber(numericDate.year)}-${formatPaddedNumber(numericDate.monthIndex + 1)}-${formatPaddedNumber(numericDate.day)}`;
case "MM":
return formatPaddedNumber(numericDate.monthIndex + 1);
case "M":
return formatNumber(numericDate.monthIndex + 1);
case "dd":
return formatPaddedNumber(numericDate.day);
case "d":
return formatNumber(numericDate.day);
default:
if (/[Hh]/.test(formatStr) && /m/.test(formatStr)) {
return buildTimeFormat(date, localeCode, formatStr);
}
return formatDateStyle(date, localeCode, "medium");
}
}
+1
View File
@@ -0,0 +1 @@
export declare function getMonth(date: Date): number;
+7
View File
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMonth = getMonth;
const conversion_js_1 = require("../utils/conversion.js");
function getMonth(date) {
return (0, conversion_js_1.toHijriDate)(date).monthIndex;
}
+1
View File
@@ -0,0 +1 @@
export declare function getYear(date: Date): number;
+7
View File
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getYear = getYear;
const conversion_js_1 = require("../utils/conversion.js");
function getYear(date) {
return (0, conversion_js_1.toHijriDate)(date).year;
}
+17
View File
@@ -0,0 +1,17 @@
export * from "./addMonths.js";
export * from "./addYears.js";
export * from "./differenceInCalendarMonths.js";
export * from "./eachMonthOfInterval.js";
export * from "./eachYearOfInterval.js";
export * from "./endOfMonth.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./getMonth.js";
export * from "./getYear.js";
export * from "./isSameMonth.js";
export * from "./isSameYear.js";
export * from "./newDate.js";
export * from "./setMonth.js";
export * from "./setYear.js";
export * from "./startOfMonth.js";
export * from "./startOfYear.js";
+33
View File
@@ -0,0 +1,33 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./addMonths.js"), exports);
__exportStar(require("./addYears.js"), exports);
__exportStar(require("./differenceInCalendarMonths.js"), exports);
__exportStar(require("./eachMonthOfInterval.js"), exports);
__exportStar(require("./eachYearOfInterval.js"), exports);
__exportStar(require("./endOfMonth.js"), exports);
__exportStar(require("./endOfYear.js"), exports);
__exportStar(require("./format.js"), exports);
__exportStar(require("./getMonth.js"), exports);
__exportStar(require("./getYear.js"), exports);
__exportStar(require("./isSameMonth.js"), exports);
__exportStar(require("./isSameYear.js"), exports);
__exportStar(require("./newDate.js"), exports);
__exportStar(require("./setMonth.js"), exports);
__exportStar(require("./setYear.js"), exports);
__exportStar(require("./startOfMonth.js"), exports);
__exportStar(require("./startOfYear.js"), exports);
+1
View File
@@ -0,0 +1 @@
export declare function isSameMonth(dateLeft: Date, dateRight: Date): boolean;
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSameMonth = isSameMonth;
const conversion_js_1 = require("../utils/conversion.js");
function isSameMonth(dateLeft, dateRight) {
const hijriLeft = (0, conversion_js_1.toHijriDate)(dateLeft);
const hijriRight = (0, conversion_js_1.toHijriDate)(dateRight);
return (hijriLeft.year === hijriRight.year &&
hijriLeft.monthIndex === hijriRight.monthIndex);
}
+1
View File
@@ -0,0 +1 @@
export declare function isSameYear(dateLeft: Date, dateRight: Date): boolean;
+9
View File
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSameYear = isSameYear;
const conversion_js_1 = require("../utils/conversion.js");
function isSameYear(dateLeft, dateRight) {
const hijriLeft = (0, conversion_js_1.toHijriDate)(dateLeft);
const hijriRight = (0, conversion_js_1.toHijriDate)(dateRight);
return hijriLeft.year === hijriRight.year;
}
+1
View File
@@ -0,0 +1 @@
export declare function newDate(year: number, monthIndex: number, day: number): Date;
+7
View File
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.newDate = newDate;
const conversion_js_1 = require("../utils/conversion.js");
function newDate(year, monthIndex, day) {
return (0, conversion_js_1.toGregorianDate)({ year, monthIndex, day });
}
+1
View File
@@ -0,0 +1 @@
export declare function setMonth(date: Date, monthIndex: number): Date;
+27
View File
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setMonth = setMonth;
const conversion_js_1 = require("../utils/conversion.js");
const daysInMonth_js_1 = require("../utils/daysInMonth.js");
function setMonth(date, monthIndex) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
// Handle overflow/underflow of monthIndex
// Note: monthIndex argument is absolute month index for the year.
// E.g. setMonth(..., 13) sets to Safar next year.
let targetYear = hijri.year;
let targetMonth = monthIndex;
if (targetMonth > 11 || targetMonth < 0) {
targetYear += Math.floor(targetMonth / 12);
targetMonth = targetMonth % 12;
if (targetMonth < 0) {
targetMonth += 12;
}
}
const daysInTargetMonth = (0, daysInMonth_js_1.getDaysInMonth)(targetYear, targetMonth);
const day = Math.min(hijri.day, daysInTargetMonth);
return (0, conversion_js_1.toGregorianDate)({
year: targetYear,
monthIndex: targetMonth,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function setYear(date: Date, year: number): Date;
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setYear = setYear;
const conversion_js_1 = require("../utils/conversion.js");
const daysInMonth_js_1 = require("../utils/daysInMonth.js");
function setYear(date, year) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
const daysInTargetMonth = (0, daysInMonth_js_1.getDaysInMonth)(year, hijri.monthIndex);
const day = Math.min(hijri.day, daysInTargetMonth);
return (0, conversion_js_1.toGregorianDate)({
year,
monthIndex: hijri.monthIndex,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfMonth(date: Date): Date;
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startOfMonth = startOfMonth;
const conversion_js_1 = require("../utils/conversion.js");
function startOfMonth(date) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
return (0, conversion_js_1.toGregorianDate)({
year: hijri.year,
monthIndex: hijri.monthIndex,
day: 1,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfYear(date: Date): Date;
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startOfYear = startOfYear;
const conversion_js_1 = require("../utils/conversion.js");
function startOfYear(date) {
const hijri = (0, conversion_js_1.toHijriDate)(date);
return (0, conversion_js_1.toGregorianDate)({
year: hijri.year,
monthIndex: 0,
day: 1,
});
}
+5
View File
@@ -0,0 +1,5 @@
import type { HijriDate } from "./types.js";
/** Convert a Gregorian date to a Hijri date. */
export declare function toHijriDate(date: Date): HijriDate;
/** Convert a Hijri date back to the Gregorian calendar. */
export declare function toGregorianDate(hijri: HijriDate): Date;
+40
View File
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toHijriDate = toHijriDate;
exports.toGregorianDate = toGregorianDate;
const hijri_converter_1 = require("@tabby_ai/hijri-converter");
const range_js_1 = require("./range.js");
/** Convert a Gregorian date to a Hijri date. */
function toHijriDate(date) {
const clamped = (0, range_js_1.clampGregorianDate)(date);
const { year, month, day } = (0, range_js_1.getGregorianDateParts)(clamped);
// gregorianToHijri uses 1-indexed months
const hijri = (0, hijri_converter_1.gregorianToHijri)({ year, month, day });
return {
year: hijri.year,
monthIndex: hijri.month - 1, // Convert to 0-indexed
day: hijri.day,
};
}
/** Convert a Hijri date back to the Gregorian calendar. */
function toGregorianDate(hijri) {
const clamped = (0, range_js_1.clampHijriDate)(hijri);
// hijriToGregorian expects 1-indexed months. Probe down from the candidate
// day to handle invalid month/day combinations without throwing.
for (let day = clamped.day; day >= 1; day -= 1) {
try {
const gregorian = (0, hijri_converter_1.hijriToGregorian)({
year: clamped.year,
month: clamped.monthIndex + 1,
day,
});
return (0, range_js_1.clampGregorianDate)(new Date(gregorian.year, gregorian.month - 1, gregorian.day));
}
catch {
// Try a lower day for months that only have 29 days.
}
}
// Fallback to the minimum supported Gregorian date if conversion probing
// somehow fails for all days.
return new Date(range_js_1.GREGORIAN_MIN_DATE);
}
+1
View File
@@ -0,0 +1 @@
export declare function getDaysInMonth(year: number, monthIndex: number): number;
+24
View File
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDaysInMonth = getDaysInMonth;
const conversion_js_1 = require("./conversion.js");
const range_js_1 = require("./range.js");
const MAX_DAY_IN_HIJRI_MONTH = 30;
const MIN_DAY_IN_HIJRI_MONTH = 29;
function getDaysInMonth(year, monthIndex) {
const clamped = (0, range_js_1.clampHijriDate)({ year, monthIndex, day: 1 });
for (let day = MAX_DAY_IN_HIJRI_MONTH; day >= MIN_DAY_IN_HIJRI_MONTH; day -= 1) {
const candidateDate = (0, conversion_js_1.toGregorianDate)({
year: clamped.year,
monthIndex: clamped.monthIndex,
day,
});
const roundTrip = (0, conversion_js_1.toHijriDate)(candidateDate);
if (roundTrip.year === clamped.year &&
roundTrip.monthIndex === clamped.monthIndex &&
roundTrip.day === day) {
return day;
}
}
return MIN_DAY_IN_HIJRI_MONTH;
}
+21
View File
@@ -0,0 +1,21 @@
import type { HijriDate } from "./types.js";
type GregorianDateParts = {
year: number;
month: number;
day: number;
};
/** Returns Gregorian date parts while preserving years < 100. */
export declare function getGregorianDateParts(date: Date): GregorianDateParts;
/** Internal Gregorian lower bound supported by the Hijri converter. */
export declare const GREGORIAN_MIN_DATE: Date;
/** Internal Gregorian upper bound supported by the Hijri converter. */
export declare const GREGORIAN_MAX_DATE: Date;
/** Clamp a Gregorian date to the supported Hijri conversion range. */
export declare function clampGregorianDate(date: Date): Date;
/** Returns whether the provided Gregorian date was clamped. */
export declare function wasGregorianDateClamped(date: Date): boolean;
/** Clamp a Hijri date to the supported range and normalize month overflow. */
export declare function clampHijriDate(date: HijriDate): HijriDate;
/** Returns whether the provided Hijri date was clamped. */
export declare function wasHijriDateClamped(date: HijriDate): boolean;
export {};
+104
View File
@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GREGORIAN_MAX_DATE = exports.GREGORIAN_MIN_DATE = void 0;
exports.getGregorianDateParts = getGregorianDateParts;
exports.clampGregorianDate = clampGregorianDate;
exports.wasGregorianDateClamped = wasGregorianDateClamped;
exports.clampHijriDate = clampHijriDate;
exports.wasHijriDateClamped = wasHijriDateClamped;
const MAX_HIJRI_DAY = 30;
const MONTHS_PER_YEAR = 12;
const HIJRI_MIN = { year: 1343, monthIndex: 0, day: 1 };
const HIJRI_MAX = { year: 1500, monthIndex: 11, day: 30 };
const GREGORIAN_MIN = { year: 1924, month: 8, day: 1 };
const GREGORIAN_MAX = { year: 2077, month: 11, day: 16 };
function compareGregorianDates(left, right) {
if (left.year !== right.year) {
return left.year - right.year;
}
if (left.month !== right.month) {
return left.month - right.month;
}
return left.day - right.day;
}
function createDate(parts) {
return new Date(parts.year, parts.month - 1, parts.day);
}
function normalizeHijriMonth(year, monthIndex) {
let normalizedYear = year + Math.trunc(monthIndex / MONTHS_PER_YEAR);
let normalizedMonth = monthIndex % MONTHS_PER_YEAR;
if (normalizedMonth < 0) {
normalizedMonth += MONTHS_PER_YEAR;
normalizedYear -= 1;
}
return { year: normalizedYear, monthIndex: normalizedMonth };
}
/** Returns Gregorian date parts while preserving years < 100. */
function getGregorianDateParts(date) {
const useUTC = date.getFullYear() < 100;
return {
year: useUTC ? date.getUTCFullYear() : date.getFullYear(),
month: (useUTC ? date.getUTCMonth() : date.getMonth()) + 1,
day: useUTC ? date.getUTCDate() : date.getDate(),
};
}
/** Internal Gregorian lower bound supported by the Hijri converter. */
exports.GREGORIAN_MIN_DATE = createDate(GREGORIAN_MIN);
/** Internal Gregorian upper bound supported by the Hijri converter. */
exports.GREGORIAN_MAX_DATE = createDate(GREGORIAN_MAX);
/** Clamp a Gregorian date to the supported Hijri conversion range. */
function clampGregorianDate(date) {
const parts = getGregorianDateParts(date);
if (compareGregorianDates(parts, GREGORIAN_MIN) < 0) {
return createDate(GREGORIAN_MIN);
}
if (compareGregorianDates(parts, GREGORIAN_MAX) > 0) {
return createDate(GREGORIAN_MAX);
}
return date;
}
/** Returns whether the provided Gregorian date was clamped. */
function wasGregorianDateClamped(date) {
return clampGregorianDate(date) !== date;
}
/** Clamp a Hijri date to the supported range and normalize month overflow. */
function clampHijriDate(date) {
const normalized = normalizeHijriMonth(date.year, date.monthIndex);
const candidate = {
year: normalized.year,
monthIndex: normalized.monthIndex,
day: Math.min(Math.max(date.day, 1), MAX_HIJRI_DAY),
};
if (candidate.year < HIJRI_MIN.year) {
return { ...HIJRI_MIN };
}
if (candidate.year > HIJRI_MAX.year) {
return { ...HIJRI_MAX };
}
if (candidate.year === HIJRI_MIN.year &&
candidate.monthIndex < HIJRI_MIN.monthIndex) {
return { ...HIJRI_MIN };
}
if (candidate.year === HIJRI_MAX.year &&
candidate.monthIndex > HIJRI_MAX.monthIndex) {
return { ...HIJRI_MAX };
}
if (candidate.year === HIJRI_MIN.year &&
candidate.monthIndex === HIJRI_MIN.monthIndex &&
candidate.day < HIJRI_MIN.day) {
return { ...HIJRI_MIN };
}
if (candidate.year === HIJRI_MAX.year &&
candidate.monthIndex === HIJRI_MAX.monthIndex &&
candidate.day > HIJRI_MAX.day) {
return { ...HIJRI_MAX };
}
return candidate;
}
/** Returns whether the provided Hijri date was clamped. */
function wasHijriDateClamped(date) {
const clamped = clampHijriDate(date);
return (clamped.year !== date.year ||
clamped.monthIndex !== date.monthIndex ||
clamped.day !== date.day);
}
+5
View File
@@ -0,0 +1,5 @@
export type HijriDate = {
year: number;
monthIndex: number;
day: number;
};
+2
View File
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
+28
View File
@@ -0,0 +1,28 @@
import type { Locale } from "date-fns";
import React from "react";
import { DateLib, type DateLibOptions } from "../index.js";
import type { DayPickerProps } from "../types/props.js";
/**
* Render the Hijri (Umm al-Qura) calendar.
*
* Defaults:
*
* - `locale`: `ar-SA`
* - `dir`: `rtl`
* - `numerals`: `arab`
* - `startMonth`: `1924-08-01`
* - `endMonth`: `2077-11-16`
*
* Out-of-range date props are clamped to the supported Umm al-Qura conversion
* range, preventing runtime conversion errors.
*/
export declare function DayPicker(props: DayPickerProps & {
locale?: Locale;
dir?: DayPickerProps["dir"];
numerals?: DayPickerProps["numerals"];
dateLib?: DayPickerProps["dateLib"];
}): React.JSX.Element;
/** Returns the date library used in the Hijri calendar. */
export declare const getDateLib: (options?: DateLibOptions) => DateLib;
export { arSA } from "../locale/ar-SA.js";
export { enUS } from "../locale/en-US.js";
+55
View File
@@ -0,0 +1,55 @@
import React from "react";
import { DateLib, DayPicker as DayPickerComponent, } from "../index.js";
import { arSA } from "../locale/ar-SA.js";
import * as hijriDateLib from "./lib/index.js";
import { clampGregorianDate, GREGORIAN_MAX_DATE, GREGORIAN_MIN_DATE, } from "./utils/range.js";
function clampDateProp(date) {
if (!date) {
return undefined;
}
return clampGregorianDate(date);
}
/**
* Render the Hijri (Umm al-Qura) calendar.
*
* Defaults:
*
* - `locale`: `ar-SA`
* - `dir`: `rtl`
* - `numerals`: `arab`
* - `startMonth`: `1924-08-01`
* - `endMonth`: `2077-11-16`
*
* Out-of-range date props are clamped to the supported Umm al-Qura conversion
* range, preventing runtime conversion errors.
*/
export function DayPicker(props) {
const { dateLib: dateLibProp, ...dayPickerProps } = props;
const hasStartBound = props.startMonth !== undefined ||
props.fromMonth !== undefined ||
props.fromYear !== undefined;
const hasEndBound = props.endMonth !== undefined ||
props.toMonth !== undefined ||
props.toYear !== undefined;
const clampedProps = {
...dayPickerProps,
month: clampDateProp(props.month),
defaultMonth: clampDateProp(props.defaultMonth),
today: clampDateProp(props.today),
startMonth: hasStartBound
? clampDateProp(props.startMonth)
: new Date(GREGORIAN_MIN_DATE),
endMonth: hasEndBound
? clampDateProp(props.endMonth)
: new Date(GREGORIAN_MAX_DATE),
fromMonth: clampDateProp(props.fromMonth),
toMonth: clampDateProp(props.toMonth),
};
return (React.createElement(DayPickerComponent, { ...clampedProps, locale: props.locale ?? arSA, numerals: props.numerals ?? "arab", dir: props.dir ?? "rtl", dateLib: { ...hijriDateLib, ...dateLibProp } }));
}
/** Returns the date library used in the Hijri calendar. */
export const getDateLib = (options) => {
return new DateLib(options, hijriDateLib);
};
export { arSA } from "../locale/ar-SA.js";
export { enUS } from "../locale/en-US.js";
+1
View File
@@ -0,0 +1 @@
export declare function addMonths(date: Date, amount: number): Date;
+6
View File
@@ -0,0 +1,6 @@
import { toHijriDate } from "../utils/conversion.js";
import { setMonth } from "./setMonth.js";
export function addMonths(date, amount) {
const hijri = toHijriDate(date);
return setMonth(date, hijri.monthIndex + amount);
}
+1
View File
@@ -0,0 +1 @@
export declare function addYears(date: Date, amount: number): Date;
+6
View File
@@ -0,0 +1,6 @@
import { toHijriDate } from "../utils/conversion.js";
import { setYear } from "./setYear.js";
export function addYears(date, amount) {
const hijri = toHijriDate(date);
return setYear(date, hijri.year + amount);
}
@@ -0,0 +1 @@
export declare function differenceInCalendarMonths(dateLeft: Date, dateRight: Date): number;
@@ -0,0 +1,7 @@
import { toHijriDate } from "../utils/conversion.js";
export function differenceInCalendarMonths(dateLeft, dateRight) {
const hijriLeft = toHijriDate(dateLeft);
const hijriRight = toHijriDate(dateRight);
return ((hijriLeft.year - hijriRight.year) * 12 +
(hijriLeft.monthIndex - hijriRight.monthIndex));
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachMonthOfInterval(interval: Interval): Date[];
@@ -0,0 +1,26 @@
import { toDate } from "date-fns";
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
export function eachMonthOfInterval(interval) {
const start = toDate(interval.start);
const end = toDate(interval.end);
if (end.getTime() < start.getTime()) {
throw new RangeError("Invalid interval");
}
const startDate = toHijriDate(start);
const endDate = toHijriDate(end);
const months = [];
let currentYear = startDate.year;
let currentMonth = startDate.monthIndex;
const endYear = endDate.year;
const endMonth = endDate.monthIndex;
while (currentYear < endYear ||
(currentYear === endYear && currentMonth <= endMonth)) {
months.push(toGregorianDate({ year: currentYear, monthIndex: currentMonth, day: 1 }));
currentMonth += 1;
if (currentMonth > 11) {
currentMonth = 0;
currentYear += 1;
}
}
return months;
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachYearOfInterval(interval: Interval): Date[];
+16
View File
@@ -0,0 +1,16 @@
import { toDate } from "date-fns";
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
export function eachYearOfInterval(interval) {
const start = toDate(interval.start);
const end = toDate(interval.end);
if (end.getTime() < start.getTime()) {
return [];
}
const startYear = toHijriDate(start).year;
const endYear = toHijriDate(end).year;
const years = [];
for (let year = startYear; year <= endYear; year += 1) {
years.push(toGregorianDate({ year, monthIndex: 0, day: 1 }));
}
return years;
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfMonth(date: Date): Date;
+11
View File
@@ -0,0 +1,11 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
import { getDaysInMonth } from "../utils/daysInMonth.js";
export function endOfMonth(date) {
const hijri = toHijriDate(date);
const day = getDaysInMonth(hijri.year, hijri.monthIndex);
return toGregorianDate({
year: hijri.year,
monthIndex: hijri.monthIndex,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfYear(date: Date): Date;
+11
View File
@@ -0,0 +1,11 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
import { getDaysInMonth } from "../utils/daysInMonth.js";
export function endOfYear(date) {
const hijri = toHijriDate(date);
const day = getDaysInMonth(hijri.year, 11);
return toGregorianDate({
year: hijri.year,
monthIndex: 11,
day,
});
}
@@ -0,0 +1,5 @@
export type FallbackLocaleCode = "ar" | "en";
export type IntlNameWidth = "long" | "short" | "narrow";
export declare const getFallbackLocaleCode: (localeCode: string) => FallbackLocaleCode;
export declare const getFallbackMonthName: (date: Date, localeCode: string, width: IntlNameWidth) => string;
export declare const getFallbackWeekdayName: (date: Date, localeCode: string, width: IntlNameWidth) => string;
+105
View File
@@ -0,0 +1,105 @@
import { toHijriDate } from "../utils/conversion.js";
const fallbackMonthNames = {
en: {
long: [
"Muharram",
"Safar",
"Rabi I",
"Rabi II",
"Jumada I",
"Jumada II",
"Rajab",
"Shaban",
"Ramadan",
"Shawwal",
"Dhu al-Qadah",
"Dhu al-Hijjah",
],
short: [
"Muh",
"Saf",
"Rab-I",
"Rab-II",
"Jum-I",
"Jum-II",
"Raj",
"Sha",
"Ram",
"Shw",
"Dhu-Q",
"Dhu-H",
],
narrow: ["M", "S", "R", "R", "J", "J", "R", "S", "R", "S", "D", "D"],
},
ar: {
long: [
"محرم",
"صفر",
"ربيع الأول",
"ربيع الآخر",
"جمادى الأولى",
"جمادى الآخرة",
"رجب",
"شعبان",
"رمضان",
"شوال",
"ذو القعدة",
"ذو الحجة",
],
short: [
"محرم",
"صفر",
"ربيع ١",
"ربيع ٢",
"جمادى ١",
"جمادى ٢",
"رجب",
"شعبان",
"رمضان",
"شوال",
"ذو القعدة",
"ذو الحجة",
],
narrow: ["م", "ص", "ر", "ر", "ج", "ج", "ر", "ش", "ر", "ش", "ذ", "ذ"],
},
};
const fallbackWeekdayNames = {
en: {
long: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
short: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
narrow: ["S", "M", "T", "W", "T", "F", "S"],
},
ar: {
long: [
"الأحد",
"الاثنين",
"الثلاثاء",
"الأربعاء",
"الخميس",
"الجمعة",
"السبت",
],
short: ["أحد", "اثن", "ثلا", "أرب", "خمي", "جمع", "سبت"],
narrow: ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
},
};
export const getFallbackLocaleCode = (localeCode) => {
return localeCode.toLowerCase().startsWith("ar") ? "ar" : "en";
};
export const getFallbackMonthName = (date, localeCode, width) => {
const hijri = toHijriDate(date);
const locale = getFallbackLocaleCode(localeCode);
return fallbackMonthNames[locale][width][hijri.monthIndex];
};
export const getFallbackWeekdayName = (date, localeCode, width) => {
const locale = getFallbackLocaleCode(localeCode);
return fallbackWeekdayNames[locale][width][date.getDay()];
};
+3
View File
@@ -0,0 +1,3 @@
import type { FormatOptions as DateFnsFormatOptions } from "date-fns";
/** Hijri calendar formatting override. */
export declare function format(date: Date, formatStr: string, options?: DateFnsFormatOptions): string;
+146
View File
@@ -0,0 +1,146 @@
import { toHijriDate } from "../utils/conversion.js";
import { clampGregorianDate, getGregorianDateParts } from "../utils/range.js";
import { getFallbackMonthName, getFallbackWeekdayName, } from "./fallbackLocaleData.js";
const DEFAULT_LOCALE_CODE = "ar-SA";
const BASE_NUMBERING_SYSTEM = "latn";
const UMM_AL_QURA_CALENDAR = "islamic-umalqura";
const getLocaleCode = (options) => {
return options?.locale?.code ?? DEFAULT_LOCALE_CODE;
};
const formatWithUmmAlQura = (date, localeCode, options) => {
try {
return new Intl.DateTimeFormat(localeCode, {
...options,
calendar: UMM_AL_QURA_CALENDAR,
numberingSystem: BASE_NUMBERING_SYSTEM,
}).format(date);
}
catch {
return undefined;
}
};
const formatNumber = (value) => {
return value.toString();
};
const formatPaddedNumber = (value) => {
return formatNumber(value).padStart(2, "0");
};
const formatMonthName = (date, localeCode, width) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
month: width,
});
return formatted ?? getFallbackMonthName(date, localeCode, width);
};
const formatWeekdayName = (date, localeCode, width) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
weekday: width,
});
return formatted ?? getFallbackWeekdayName(date, localeCode, width);
};
const formatDateStyle = (date, localeCode, style) => {
const formatted = formatWithUmmAlQura(date, localeCode, {
dateStyle: style,
});
if (formatted) {
return formatted;
}
const hijri = toHijriDate(date);
const monthName = getFallbackMonthName(date, localeCode, "long");
switch (style) {
case "full":
return `${getFallbackWeekdayName(date, localeCode, "long")}, ${monthName} ${hijri.day}, ${hijri.year}`;
case "long":
return `${monthName} ${hijri.day}, ${hijri.year}`;
case "medium":
return `${formatPaddedNumber(hijri.day)} ${monthName} ${hijri.year}`;
case "short":
return `${hijri.monthIndex + 1}/${hijri.day}/${hijri.year}`;
}
};
const buildTimeFormat = (date, localeCode, formatStr) => {
const hour12 = formatStr.includes("a");
const formatted = formatWithUmmAlQura(date, localeCode, {
hour: "numeric",
minute: "numeric",
hour12,
});
if (formatted) {
return formatted;
}
try {
return new Intl.DateTimeFormat(localeCode, {
hour: "numeric",
minute: "numeric",
hour12,
numberingSystem: BASE_NUMBERING_SYSTEM,
}).format(date);
}
catch {
const minutes = formatPaddedNumber(date.getMinutes());
if (hour12) {
const hour = date.getHours() % 12 || 12;
const period = date.getHours() >= 12 ? "PM" : "AM";
return `${hour}:${minutes} ${period}`;
}
return `${formatNumber(date.getHours())}:${minutes}`;
}
};
/** Hijri calendar formatting override. */
export function format(date, formatStr, options) {
const extendedOptions = options;
const localeCode = getLocaleCode(extendedOptions);
const hijri = toHijriDate(date);
const gregorian = getGregorianDateParts(date);
const isOutOfRange = clampGregorianDate(date) !== date;
const numericDate = isOutOfRange
? {
year: gregorian.year,
monthIndex: gregorian.month - 1,
day: gregorian.day,
}
: hijri;
switch (formatStr) {
case "LLLL y":
case "LLLL yyyy":
return `${formatMonthName(date, localeCode, "long")} ${formatNumber(numericDate.year)}`;
case "LLLL":
return formatMonthName(date, localeCode, "long");
case "LLL":
return formatMonthName(date, localeCode, "short");
case "PPP":
return formatDateStyle(date, localeCode, "long");
case "PPPP":
return formatDateStyle(date, localeCode, "full");
case "PP":
return formatDateStyle(date, localeCode, "medium");
case "P":
return formatDateStyle(date, localeCode, "short");
case "cccc":
return formatWeekdayName(date, localeCode, "long");
case "ccc":
return formatWeekdayName(date, localeCode, "short");
case "ccccc":
case "cccccc":
return formatWeekdayName(date, localeCode, "narrow");
case "yyyy":
case "y":
return formatNumber(numericDate.year);
case "yyyy-MM":
return `${formatNumber(numericDate.year)}-${formatPaddedNumber(numericDate.monthIndex + 1)}`;
case "yyyy-MM-dd":
return `${formatNumber(numericDate.year)}-${formatPaddedNumber(numericDate.monthIndex + 1)}-${formatPaddedNumber(numericDate.day)}`;
case "MM":
return formatPaddedNumber(numericDate.monthIndex + 1);
case "M":
return formatNumber(numericDate.monthIndex + 1);
case "dd":
return formatPaddedNumber(numericDate.day);
case "d":
return formatNumber(numericDate.day);
default:
if (/[Hh]/.test(formatStr) && /m/.test(formatStr)) {
return buildTimeFormat(date, localeCode, formatStr);
}
return formatDateStyle(date, localeCode, "medium");
}
}
+1
View File
@@ -0,0 +1 @@
export declare function getMonth(date: Date): number;
+4
View File
@@ -0,0 +1,4 @@
import { toHijriDate } from "../utils/conversion.js";
export function getMonth(date) {
return toHijriDate(date).monthIndex;
}
+1
View File
@@ -0,0 +1 @@
export declare function getYear(date: Date): number;
+4
View File
@@ -0,0 +1,4 @@
import { toHijriDate } from "../utils/conversion.js";
export function getYear(date) {
return toHijriDate(date).year;
}
+17
View File
@@ -0,0 +1,17 @@
export * from "./addMonths.js";
export * from "./addYears.js";
export * from "./differenceInCalendarMonths.js";
export * from "./eachMonthOfInterval.js";
export * from "./eachYearOfInterval.js";
export * from "./endOfMonth.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./getMonth.js";
export * from "./getYear.js";
export * from "./isSameMonth.js";
export * from "./isSameYear.js";
export * from "./newDate.js";
export * from "./setMonth.js";
export * from "./setYear.js";
export * from "./startOfMonth.js";
export * from "./startOfYear.js";
+17
View File
@@ -0,0 +1,17 @@
export * from "./addMonths.js";
export * from "./addYears.js";
export * from "./differenceInCalendarMonths.js";
export * from "./eachMonthOfInterval.js";
export * from "./eachYearOfInterval.js";
export * from "./endOfMonth.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./getMonth.js";
export * from "./getYear.js";
export * from "./isSameMonth.js";
export * from "./isSameYear.js";
export * from "./newDate.js";
export * from "./setMonth.js";
export * from "./setYear.js";
export * from "./startOfMonth.js";
export * from "./startOfYear.js";
+1
View File
@@ -0,0 +1 @@
export declare function isSameMonth(dateLeft: Date, dateRight: Date): boolean;
+7
View File
@@ -0,0 +1,7 @@
import { toHijriDate } from "../utils/conversion.js";
export function isSameMonth(dateLeft, dateRight) {
const hijriLeft = toHijriDate(dateLeft);
const hijriRight = toHijriDate(dateRight);
return (hijriLeft.year === hijriRight.year &&
hijriLeft.monthIndex === hijriRight.monthIndex);
}
+1
View File
@@ -0,0 +1 @@
export declare function isSameYear(dateLeft: Date, dateRight: Date): boolean;
+6
View File
@@ -0,0 +1,6 @@
import { toHijriDate } from "../utils/conversion.js";
export function isSameYear(dateLeft, dateRight) {
const hijriLeft = toHijriDate(dateLeft);
const hijriRight = toHijriDate(dateRight);
return hijriLeft.year === hijriRight.year;
}
+1
View File
@@ -0,0 +1 @@
export declare function newDate(year: number, monthIndex: number, day: number): Date;
+4
View File
@@ -0,0 +1,4 @@
import { toGregorianDate } from "../utils/conversion.js";
export function newDate(year, monthIndex, day) {
return toGregorianDate({ year, monthIndex, day });
}
+1
View File
@@ -0,0 +1 @@
export declare function setMonth(date: Date, monthIndex: number): Date;
+24
View File
@@ -0,0 +1,24 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
import { getDaysInMonth } from "../utils/daysInMonth.js";
export function setMonth(date, monthIndex) {
const hijri = toHijriDate(date);
// Handle overflow/underflow of monthIndex
// Note: monthIndex argument is absolute month index for the year.
// E.g. setMonth(..., 13) sets to Safar next year.
let targetYear = hijri.year;
let targetMonth = monthIndex;
if (targetMonth > 11 || targetMonth < 0) {
targetYear += Math.floor(targetMonth / 12);
targetMonth = targetMonth % 12;
if (targetMonth < 0) {
targetMonth += 12;
}
}
const daysInTargetMonth = getDaysInMonth(targetYear, targetMonth);
const day = Math.min(hijri.day, daysInTargetMonth);
return toGregorianDate({
year: targetYear,
monthIndex: targetMonth,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function setYear(date: Date, year: number): Date;
+12
View File
@@ -0,0 +1,12 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
import { getDaysInMonth } from "../utils/daysInMonth.js";
export function setYear(date, year) {
const hijri = toHijriDate(date);
const daysInTargetMonth = getDaysInMonth(year, hijri.monthIndex);
const day = Math.min(hijri.day, daysInTargetMonth);
return toGregorianDate({
year,
monthIndex: hijri.monthIndex,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfMonth(date: Date): Date;
+9
View File
@@ -0,0 +1,9 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
export function startOfMonth(date) {
const hijri = toHijriDate(date);
return toGregorianDate({
year: hijri.year,
monthIndex: hijri.monthIndex,
day: 1,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfYear(date: Date): Date;
+9
View File
@@ -0,0 +1,9 @@
import { toGregorianDate, toHijriDate } from "../utils/conversion.js";
export function startOfYear(date) {
const hijri = toHijriDate(date);
return toGregorianDate({
year: hijri.year,
monthIndex: 0,
day: 1,
});
}
+5
View File
@@ -0,0 +1,5 @@
import type { HijriDate } from "./types.js";
/** Convert a Gregorian date to a Hijri date. */
export declare function toHijriDate(date: Date): HijriDate;
/** Convert a Hijri date back to the Gregorian calendar. */
export declare function toGregorianDate(hijri: HijriDate): Date;
+36
View File
@@ -0,0 +1,36 @@
import { gregorianToHijri, hijriToGregorian } from "@tabby_ai/hijri-converter";
import { clampGregorianDate, clampHijriDate, GREGORIAN_MIN_DATE, getGregorianDateParts, } from "./range.js";
/** Convert a Gregorian date to a Hijri date. */
export function toHijriDate(date) {
const clamped = clampGregorianDate(date);
const { year, month, day } = getGregorianDateParts(clamped);
// gregorianToHijri uses 1-indexed months
const hijri = gregorianToHijri({ year, month, day });
return {
year: hijri.year,
monthIndex: hijri.month - 1, // Convert to 0-indexed
day: hijri.day,
};
}
/** Convert a Hijri date back to the Gregorian calendar. */
export function toGregorianDate(hijri) {
const clamped = clampHijriDate(hijri);
// hijriToGregorian expects 1-indexed months. Probe down from the candidate
// day to handle invalid month/day combinations without throwing.
for (let day = clamped.day; day >= 1; day -= 1) {
try {
const gregorian = hijriToGregorian({
year: clamped.year,
month: clamped.monthIndex + 1,
day,
});
return clampGregorianDate(new Date(gregorian.year, gregorian.month - 1, gregorian.day));
}
catch {
// Try a lower day for months that only have 29 days.
}
}
// Fallback to the minimum supported Gregorian date if conversion probing
// somehow fails for all days.
return new Date(GREGORIAN_MIN_DATE);
}
+1
View File
@@ -0,0 +1 @@
export declare function getDaysInMonth(year: number, monthIndex: number): number;
+21
View File
@@ -0,0 +1,21 @@
import { toGregorianDate, toHijriDate } from "./conversion.js";
import { clampHijriDate } from "./range.js";
const MAX_DAY_IN_HIJRI_MONTH = 30;
const MIN_DAY_IN_HIJRI_MONTH = 29;
export function getDaysInMonth(year, monthIndex) {
const clamped = clampHijriDate({ year, monthIndex, day: 1 });
for (let day = MAX_DAY_IN_HIJRI_MONTH; day >= MIN_DAY_IN_HIJRI_MONTH; day -= 1) {
const candidateDate = toGregorianDate({
year: clamped.year,
monthIndex: clamped.monthIndex,
day,
});
const roundTrip = toHijriDate(candidateDate);
if (roundTrip.year === clamped.year &&
roundTrip.monthIndex === clamped.monthIndex &&
roundTrip.day === day) {
return day;
}
}
return MIN_DAY_IN_HIJRI_MONTH;
}
+21
View File
@@ -0,0 +1,21 @@
import type { HijriDate } from "./types.js";
type GregorianDateParts = {
year: number;
month: number;
day: number;
};
/** Returns Gregorian date parts while preserving years < 100. */
export declare function getGregorianDateParts(date: Date): GregorianDateParts;
/** Internal Gregorian lower bound supported by the Hijri converter. */
export declare const GREGORIAN_MIN_DATE: Date;
/** Internal Gregorian upper bound supported by the Hijri converter. */
export declare const GREGORIAN_MAX_DATE: Date;
/** Clamp a Gregorian date to the supported Hijri conversion range. */
export declare function clampGregorianDate(date: Date): Date;
/** Returns whether the provided Gregorian date was clamped. */
export declare function wasGregorianDateClamped(date: Date): boolean;
/** Clamp a Hijri date to the supported range and normalize month overflow. */
export declare function clampHijriDate(date: HijriDate): HijriDate;
/** Returns whether the provided Hijri date was clamped. */
export declare function wasHijriDateClamped(date: HijriDate): boolean;
export {};
+96
View File
@@ -0,0 +1,96 @@
const MAX_HIJRI_DAY = 30;
const MONTHS_PER_YEAR = 12;
const HIJRI_MIN = { year: 1343, monthIndex: 0, day: 1 };
const HIJRI_MAX = { year: 1500, monthIndex: 11, day: 30 };
const GREGORIAN_MIN = { year: 1924, month: 8, day: 1 };
const GREGORIAN_MAX = { year: 2077, month: 11, day: 16 };
function compareGregorianDates(left, right) {
if (left.year !== right.year) {
return left.year - right.year;
}
if (left.month !== right.month) {
return left.month - right.month;
}
return left.day - right.day;
}
function createDate(parts) {
return new Date(parts.year, parts.month - 1, parts.day);
}
function normalizeHijriMonth(year, monthIndex) {
let normalizedYear = year + Math.trunc(monthIndex / MONTHS_PER_YEAR);
let normalizedMonth = monthIndex % MONTHS_PER_YEAR;
if (normalizedMonth < 0) {
normalizedMonth += MONTHS_PER_YEAR;
normalizedYear -= 1;
}
return { year: normalizedYear, monthIndex: normalizedMonth };
}
/** Returns Gregorian date parts while preserving years < 100. */
export function getGregorianDateParts(date) {
const useUTC = date.getFullYear() < 100;
return {
year: useUTC ? date.getUTCFullYear() : date.getFullYear(),
month: (useUTC ? date.getUTCMonth() : date.getMonth()) + 1,
day: useUTC ? date.getUTCDate() : date.getDate(),
};
}
/** Internal Gregorian lower bound supported by the Hijri converter. */
export const GREGORIAN_MIN_DATE = createDate(GREGORIAN_MIN);
/** Internal Gregorian upper bound supported by the Hijri converter. */
export const GREGORIAN_MAX_DATE = createDate(GREGORIAN_MAX);
/** Clamp a Gregorian date to the supported Hijri conversion range. */
export function clampGregorianDate(date) {
const parts = getGregorianDateParts(date);
if (compareGregorianDates(parts, GREGORIAN_MIN) < 0) {
return createDate(GREGORIAN_MIN);
}
if (compareGregorianDates(parts, GREGORIAN_MAX) > 0) {
return createDate(GREGORIAN_MAX);
}
return date;
}
/** Returns whether the provided Gregorian date was clamped. */
export function wasGregorianDateClamped(date) {
return clampGregorianDate(date) !== date;
}
/** Clamp a Hijri date to the supported range and normalize month overflow. */
export function clampHijriDate(date) {
const normalized = normalizeHijriMonth(date.year, date.monthIndex);
const candidate = {
year: normalized.year,
monthIndex: normalized.monthIndex,
day: Math.min(Math.max(date.day, 1), MAX_HIJRI_DAY),
};
if (candidate.year < HIJRI_MIN.year) {
return { ...HIJRI_MIN };
}
if (candidate.year > HIJRI_MAX.year) {
return { ...HIJRI_MAX };
}
if (candidate.year === HIJRI_MIN.year &&
candidate.monthIndex < HIJRI_MIN.monthIndex) {
return { ...HIJRI_MIN };
}
if (candidate.year === HIJRI_MAX.year &&
candidate.monthIndex > HIJRI_MAX.monthIndex) {
return { ...HIJRI_MAX };
}
if (candidate.year === HIJRI_MIN.year &&
candidate.monthIndex === HIJRI_MIN.monthIndex &&
candidate.day < HIJRI_MIN.day) {
return { ...HIJRI_MIN };
}
if (candidate.year === HIJRI_MAX.year &&
candidate.monthIndex === HIJRI_MAX.monthIndex &&
candidate.day > HIJRI_MAX.day) {
return { ...HIJRI_MAX };
}
return candidate;
}
/** Returns whether the provided Hijri date was clamped. */
export function wasHijriDateClamped(date) {
const clamped = clampHijriDate(date);
return (clamped.year !== date.year ||
clamped.monthIndex !== date.monthIndex ||
clamped.day !== date.day);
}
+5
View File
@@ -0,0 +1,5 @@
export type HijriDate = {
year: number;
monthIndex: number;
day: number;
};
+1
View File
@@ -0,0 +1 @@
export {};