admin/lib/birthDate.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

60 lines
2.2 KiB
TypeScript

import { jalaaliMonthLength, toGregorian, toJalaali } from 'jalaali-js'
import { texts } from '@/texts'
export const PERSIAN_MONTHS = texts.common.jalaliMonths
export const BIRTH_DATE_MIN_YEAR = 1330
export const BIRTH_DATE_MAX_YEAR = 1395
export const BIRTH_DATE_DEFAULT_YEAR = 1370
export interface JalaliDateParts {
day: string
month: string
year: string
[key: string]: string
}
export function birthDateYears(): string[] {
return Array.from({ length: BIRTH_DATE_MAX_YEAR - BIRTH_DATE_MIN_YEAR + 1 }, (_, index) => String(BIRTH_DATE_MIN_YEAR + index))
}
export function clampJalaliDateParts(value: JalaliDateParts): JalaliDateParts {
const year = Math.min(BIRTH_DATE_MAX_YEAR, Math.max(BIRTH_DATE_MIN_YEAR, Number(value.year) || BIRTH_DATE_DEFAULT_YEAR))
const month = Math.min(12, Math.max(1, Number(value.month) || 1))
const maxDay = jalaaliMonthLength(year, month)
const day = Math.min(maxDay, Math.max(1, Number(value.day) || 1))
return { day: String(day), month: String(month), year: String(year) }
}
export function isoDateToJalaliParts(value: unknown): JalaliDateParts | null {
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null
const [year, month, day] = value.split('-').map(Number)
const parsed = new Date(Date.UTC(year, month - 1, day))
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) return null
const jalali = toJalaali(parsed.getUTCFullYear(), parsed.getUTCMonth() + 1, parsed.getUTCDate())
if (jalali.jy < BIRTH_DATE_MIN_YEAR || jalali.jy > BIRTH_DATE_MAX_YEAR) return null
return { day: String(jalali.jd), month: String(jalali.jm), year: String(jalali.jy) }
}
export function jalaliPartsToIsoDate(value: JalaliDateParts): string {
const clamped = clampJalaliDateParts(value)
const { gy, gm, gd } = toGregorian(Number(clamped.year), Number(clamped.month), Number(clamped.day))
return `${gy}-${String(gm).padStart(2, '0')}-${String(gd).padStart(2, '0')}`
}
export function isAllowedBirthDate(value: unknown): value is string {
return isoDateToJalaliParts(value) !== null
}
export function defaultBirthDateParts(): JalaliDateParts {
return { day: '1', month: '1', year: String(BIRTH_DATE_DEFAULT_YEAR) }
}