115 lines
2.8 KiB
TypeScript
115 lines
2.8 KiB
TypeScript
const MONTH_NAMES = [
|
|
'فروردین',
|
|
'اردیبهشت',
|
|
'خرداد',
|
|
'تیر',
|
|
'مرداد',
|
|
'شهریور',
|
|
'مهر',
|
|
'آبان',
|
|
'آذر',
|
|
'دی',
|
|
'بهمن',
|
|
'اسفند',
|
|
] as const;
|
|
|
|
function div(a: number, b: number) {
|
|
return Math.trunc(a / b);
|
|
}
|
|
|
|
function mod(a: number, b: number) {
|
|
return a - Math.trunc(a / b) * b;
|
|
}
|
|
|
|
export function toJalali(gy: number, gm: number, gd: number) {
|
|
const gdm = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
const gy2 = gm > 2 ? gy + 1 : gy;
|
|
let days =
|
|
355666 +
|
|
365 * gy +
|
|
div(gy2 + 3, 4) -
|
|
div(gy2 + 99, 100) +
|
|
div(gy2 + 399, 400) +
|
|
gd +
|
|
gdm[gm - 1];
|
|
let jy = -1595 + 33 * div(days, 12053);
|
|
days %= 12053;
|
|
jy += 4 * div(days, 1461);
|
|
days %= 1461;
|
|
if (days > 365) {
|
|
jy += div(days - 1, 365);
|
|
days = (days - 1) % 365;
|
|
}
|
|
const jm = days < 186 ? 1 + div(days, 31) : 7 + div(days - 186, 30);
|
|
const jd = 1 + (days < 186 ? days % 31 : (days - 186) % 30);
|
|
return { jy, jm, jd };
|
|
}
|
|
|
|
export function toGregorian(jy: number, jm: number, jd: number) {
|
|
const jy2 = jy + 1595;
|
|
let days =
|
|
-355668 +
|
|
365 * jy2 +
|
|
div(jy2, 33) * 8 +
|
|
div(mod(jy2, 33) + 3, 4) +
|
|
jd +
|
|
(jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);
|
|
let gy = 400 * div(days, 146097);
|
|
days %= 146097;
|
|
if (days > 36524) {
|
|
gy += 100 * div(--days, 36524);
|
|
days %= 36524;
|
|
if (days >= 365) days += 1;
|
|
}
|
|
gy += 4 * div(days, 1461);
|
|
days %= 1461;
|
|
if (days > 365) {
|
|
gy += div(days - 1, 365);
|
|
days = (days - 1) % 365;
|
|
}
|
|
const gd = days + 1;
|
|
const salA = [
|
|
0, 31, (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0 ? 29 : 28,
|
|
31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
|
|
];
|
|
let gm = 0;
|
|
let dayAcc = gd;
|
|
for (gm = 1; gm <= 12 && dayAcc > salA[gm]; gm += 1) {
|
|
dayAcc -= salA[gm];
|
|
}
|
|
return { gy, gm, gd: dayAcc };
|
|
}
|
|
|
|
export function dateToJalali(d: Date) {
|
|
return toJalali(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
|
}
|
|
|
|
export function jalaliToDate(jy: number, jm: number, jd: number): Date {
|
|
const { gy, gm, gd } = toGregorian(jy, jm, jd);
|
|
return new Date(gy, gm - 1, gd);
|
|
}
|
|
|
|
export function addJalaliMonths(jy: number, jm: number, delta: number) {
|
|
const idx = jy * 12 + (jm - 1) + delta;
|
|
return { jy: Math.floor(idx / 12), jm: (idx % 12) + 1 };
|
|
}
|
|
|
|
export function jalaliMonthName(jm: number): string {
|
|
return MONTH_NAMES[jm - 1] ?? '';
|
|
}
|
|
|
|
export function jalaliMonthKey(d: Date): string {
|
|
const { jy, jm } = dateToJalali(d);
|
|
return `${jy}-${String(jm).padStart(2, '0')}`;
|
|
}
|
|
|
|
export function startOfJalaliMonth(jy: number, jm: number): Date {
|
|
return jalaliToDate(jy, jm, 1);
|
|
}
|
|
|
|
export function endOfJalaliMonth(jy: number, jm: number): Date {
|
|
const next = addJalaliMonths(jy, jm, 1);
|
|
const startNext = jalaliToDate(next.jy, next.jm, 1);
|
|
return new Date(startNext.getTime() - 1);
|
|
}
|