Tidied up the project

This commit is contained in:
2026-05-30 08:59:57 +03:30
parent 5c372947dd
commit 10df869efb
19 changed files with 7064 additions and 4620 deletions

View File

@@ -1,13 +1,9 @@
import * as jMoment from "jalali-moment";
/**
* Converts an ISO date string or Date to Jalali (Persian) date and time.
* @param input - ISO date string (e.g. 2026-02-08T13:51:20.747+00:00) or Date
* @returns Tuple [dateStr, timeStr] e.g. ['1404/11/24', '13:37']
*/
export function toJalaliDateAndTime(
input: string | Date,
): [string, string] {
export function toJalaliDateAndTime(input: string | Date): [string, string] {
const d = new Date(input);
const dateStr = toJalaliDate(d);
const timeStr = toJalaliTime(d);
@@ -32,8 +28,7 @@ export function jalaliToGregorianDate(
): string | null {
if (input === null || input === undefined) return null;
const raw =
typeof input === "number" ? String(input) : String(input).trim();
const raw = typeof input === "number" ? String(input) : String(input).trim();
if (!raw) return null;
let year = 0;
@@ -58,20 +53,16 @@ export function jalaliToGregorianDate(
if (!year || !month || !day) return null;
// If the year already looks Gregorian (>= 1900), assume the caller already
// sent a Gregorian date and just normalise the formatting.
// If the year already looks Gregorian (>= 1900), just normalise formatting.
if (year >= 1900) {
const mm = String(month).padStart(2, "0");
const dd = String(day).padStart(2, "0");
const m = jMoment(`${year}-${mm}-${dd}`, "YYYY-MM-DD");
return m.isValid() ? m.format("YYYY-MM-DD") : null;
const d = new Date(`${year}-${mm}-${dd}`);
if (isNaN(d.getTime())) return null;
return `${year}-${mm}-${dd}`;
}
const mm = String(month).padStart(2, "0");
const dd = String(day).padStart(2, "0");
const jm = jMoment(`${year}-${mm}-${dd}`, "jYYYY-jMM-jDD");
if (!jm.isValid()) return null;
return jm.format("YYYY-MM-DD");
return jalaliPartsToGregorian(year, month, day);
}
/**
@@ -95,6 +86,92 @@ export function toJalaliTime(d: Date): string {
return `${hours}:${minutes}`;
}
/**
* Converts Jalali year/month/day parts to a Gregorian `YYYY-MM-DD` string
* using the algorithmic conversion (no external deps).
* Returns `null` if the resulting Gregorian date is invalid.
*/
function jalaliPartsToGregorian(
jYear: number,
jMonth: number,
jDay: number,
): string | null {
// Jalali to Julian Day Number, then to Gregorian
// Algorithm: https://www.fourmilab.ch/documents/calendar/
const jy = jYear - 979;
const jm = jMonth - 1;
const jd = jDay - 1;
let jDayNo =
365 * jy + Math.floor(jy / 4) - Math.floor(jy / 100) + Math.floor(jy / 400);
const jalaliMonthDays = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
for (let i = 0; i < jm; i++) {
jDayNo += jalaliMonthDays[i];
}
jDayNo += jd;
// Convert to Gregorian day number (offset from 1970-01-01 in Julian days)
const gDayNo = jDayNo + 79;
let gy = 1979 + 400 * Math.floor(gDayNo / 146097);
let days = gDayNo % 146097;
let leap = true;
if (days >= 36525) {
days--;
gy += 100 * Math.floor(days / 36524);
days %= 36524;
if (days >= 365) days++;
else leap = false;
}
gy += 4 * Math.floor(days / 1461);
days %= 1461;
if (days >= 366) {
leap = false;
days--;
gy += Math.floor(days / 365);
days %= 365;
}
const gregorianMonthDays = [
31,
leap ? 29 : 28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
];
let gm = 0;
for (let i = 0; i < 12; i++) {
if (days < gregorianMonthDays[i]) {
gm = i + 1;
break;
}
days -= gregorianMonthDays[i];
}
const gd = days + 1;
const mm = String(gm).padStart(2, "0");
const dd = String(gd).padStart(2, "0");
const result = `${gy}-${mm}-${dd}`;
// Sanity check
const check = new Date(result);
if (isNaN(check.getTime())) return null;
return result;
}
const PERSIAN_TO_ENGLISH: Record<string, string> = {
"۰": "0",
"۱": "1",