Files
mbmanager-api/utils/jalali.js
T
2026-08-19 06:45:50 +03:30

175 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Jalali (Solar Hijri / Shamsi) calendar conversion utilities (Backend)
*/
const persianMonths = [
{ value: 1, label: 'فروردین' },
{ value: 2, label: 'اردیبهشت' },
{ value: 3, label: 'خرداد' },
{ value: 4, label: 'تیر' },
{ value: 5, label: 'مرداد' },
{ value: 6, label: 'شهریور' },
{ value: 7, label: 'مهر' },
{ value: 8, label: 'آبان' },
{ value: 9, label: 'آذر' },
{ value: 10, label: 'دی' },
{ value: 11, label: 'بهمن' },
{ value: 12, label: 'اسفند' }
]
function toEnglishDigits(str) {
if (str === null || str === undefined) return ''
return String(str)
.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d))
.replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d))
}
function jalaliToGregorian(jy, jm, jd) {
jy = Number(jy)
jm = Number(jm)
jd = Number(jd)
let gy = jy > 979 ? 1600 : 621
jy = jy > 979 ? jy - 979 : jy
let days =
365 * jy +
Math.floor(jy / 33) * 8 +
Math.floor(((jy % 33) + 3) / 4) +
78 +
jd +
(jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186)
gy += 400 * Math.floor(days / 146097)
days %= 146097
if (days > 36524) {
gy += 100 * Math.floor(--days / 36524)
days %= 36524
if (days >= 365) days++
}
gy += 4 * Math.floor(days / 1461)
days %= 1461
if (days > 365) {
gy += Math.floor((days - 1) / 365)
days = (days - 1) % 365
}
let gd = days + 1
const isLeap = (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0
const sal_a = [0, 31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
let gm
for (gm = 0; gm < 13; gm++) {
const v = sal_a[gm]
if (gd <= v) break
gd -= v
}
return { gy, gm, gd }
}
function gregorianToJalali(gy, gm, gd) {
gy = Number(gy)
gm = Number(gm)
gd = Number(gd)
const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
let jy = gy > 1600 ? 979 : 0
gy = gy > 1600 ? gy - 1600 : gy - 621
const gy2 = gm > 2 ? gy + 1 : gy
let days =
365 * gy +
Math.floor((gy2 + 3) / 4) -
Math.floor((gy2 + 99) / 100) +
Math.floor((gy2 + 399) / 400) -
80 +
gd +
g_d_m[gm - 1]
jy += 33 * Math.floor(days / 12053)
days %= 12053
jy += 4 * Math.floor(days / 1461)
days %= 1461
if (days > 365) {
jy += Math.floor((days - 1) / 365)
days = (days - 1) % 365
}
const jm = days < 186 ? 1 + Math.floor(days / 31) : 7 + Math.floor((days - 186) / 30)
const jd = 1 + (days < 186 ? days % 31 : (days - 186) % 30)
return { jy, jm, jd }
}
function jalaliToDate(jy, jm, jd) {
const y = parseInt(toEnglishDigits(jy), 10)
const m = parseInt(toEnglishDigits(jm), 10)
const d = parseInt(toEnglishDigits(jd), 10)
if (isNaN(y) || isNaN(m) || isNaN(d) || y <= 0 || m < 1 || m > 12 || d < 1 || d > 31) {
return null
}
const { gy, gm, gd } = jalaliToGregorian(y, m, d)
return new Date(Date.UTC(gy, gm - 1, gd, 12, 0, 0))
}
function parseDateToJalali(dateVal) {
if (!dateVal) return null
if (dateVal instanceof Date || (typeof dateVal === 'string' && (dateVal.includes('T') || dateVal.includes('-')))) {
const d = new Date(dateVal)
if (!isNaN(d.getTime())) {
return gregorianToJalali(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate())
}
}
if (typeof dateVal === 'string' && dateVal.includes('/')) {
const clean = toEnglishDigits(dateVal.trim())
const parts = clean.split('/')
if (parts.length === 3) {
const jy = parseInt(parts[0], 10)
const jm = parseInt(parts[1], 10)
const jd = parseInt(parts[2], 10)
if (!isNaN(jy) && !isNaN(jm) && !isNaN(jd)) {
return { jy, jm, jd }
}
}
}
return null
}
function normalizeToDate(input) {
if (!input) return null
if (input instanceof Date && !isNaN(input.getTime())) return input
if (typeof input === 'string') {
const trimmed = input.trim()
if (trimmed.includes('/')) {
const parts = toEnglishDigits(trimmed).split('/')
if (parts.length === 3) {
return jalaliToDate(parts[0], parts[1], parts[2])
}
}
const d = new Date(trimmed)
if (!isNaN(d.getTime())) return d
}
return null
}
module.exports = {
persianMonths,
toEnglishDigits,
jalaliToGregorian,
gregorianToJalali,
jalaliToDate,
parseDateToJalali,
normalizeToDate,
}