Files
kavehhn fb4a98f83c
deploy-chabokan / deploy (push) Canceled after 0s
fix(date): add id and name to persianMonths definition
2026-08-19 07:17:35 +03:30

175 lines
4.9 KiB
JavaScript
Raw Permalink 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, id: 1, label: 'فروردین', name: 'فروردین' },
{ value: 2, id: 2, label: 'اردیبهشت', name: 'اردیبهشت' },
{ value: 3, id: 3, label: 'خرداد', name: 'خرداد' },
{ value: 4, id: 4, label: 'تیر', name: 'تیر' },
{ value: 5, id: 5, label: 'مرداد', name: 'مرداد' },
{ value: 6, id: 6, label: 'شهریور', name: 'شهریور' },
{ value: 7, id: 7, label: 'مهر', name: 'مهر' },
{ value: 8, id: 8, label: 'آبان', name: 'آبان' },
{ value: 9, id: 9, label: 'آذر', name: 'آذر' },
{ value: 10, id: 10, label: 'دی', name: 'دی' },
{ value: 11, id: 11, label: 'بهمن', name: 'بهمن' },
{ value: 12, id: 12, label: 'اسفند', name: 'اسفند' }
]
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,
}