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
+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 {};