feat(property): convert registrationDate to Date object and add Jalali utilities
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
+174
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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,
|
||||
}
|
||||
Reference in New Issue
Block a user