import { texts } from '@/texts' const EMPTY_VALUE = '—' /** Product display timezone — keeps SSR (Docker UTC) and client (Iran) text identical. */ export const DISPLAY_TIME_ZONE = 'Asia/Tehran' const PERSIAN_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const /** Normalize Western digits and grouping to Persian digits for stable SSR/client output. */ export function toPersianDigits(value: string): string { return value.replace(/\d/g, (digit) => PERSIAN_DIGITS[Number(digit)]) } const defaultDateTimeOptions: Intl.DateTimeFormatOptions = { dateStyle: 'medium', timeStyle: 'short', timeZone: DISPLAY_TIME_ZONE, } function dayKeyInTimeZone(date: Date, timeZone: string): string | null { const parts = new Intl.DateTimeFormat('en-CA', { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', }).formatToParts(date) const year = parts.find((part) => part.type === 'year')?.value const month = parts.find((part) => part.type === 'month')?.value const day = parts.find((part) => part.type === 'day')?.value if (!year || !month || !day) return null return `${year}-${month}-${day}` } export function formatPersianDate( value: unknown, options: Intl.DateTimeFormatOptions = defaultDateTimeOptions, fallback = EMPTY_VALUE ): string { if (!value || (typeof value !== 'string' && !(value instanceof Date))) return fallback const date = value instanceof Date ? value : new Date(value) return Number.isFinite(date.getTime()) ? new Intl.DateTimeFormat('fa-IR', { timeZone: DISPLAY_TIME_ZONE, ...options }).format(date) : fallback } /** Local calendar day key (YYYY-MM-DD) in the product display timezone. */ export function toLocalDayKey(value: string | Date, timeZone = DISPLAY_TIME_ZONE): string | null { const date = value instanceof Date ? value : new Date(value) if (!Number.isFinite(date.getTime())) return null return dayKeyInTimeZone(date, timeZone) } /** Telegram-style day label: امروز / فردا / دیروز / full Persian date. */ export function formatChatDayLabel(value: string | Date, now = new Date()): string { const date = value instanceof Date ? value : new Date(value) if (!Number.isFinite(date.getTime())) return EMPTY_VALUE const todayKey = toLocalDayKey(now) const dayKey = toLocalDayKey(date) if (!todayKey || !dayKey) return EMPTY_VALUE if (dayKey === todayKey) return texts.common.today const tomorrow = new Date(now) tomorrow.setDate(tomorrow.getDate() + 1) if (dayKey === toLocalDayKey(tomorrow)) return texts.common.tomorrow const yesterday = new Date(now) yesterday.setDate(yesterday.getDate() - 1) if (dayKey === toLocalDayKey(yesterday)) return texts.common.yesterday return formatPersianDate(date, { dateStyle: 'medium' }) } /** e.g. `امروز ۱۸:۰۰ تا ۲۰:۳۰` for home category preview rows. */ export function formatEventDayTimeRange(startsAt: string | Date, endsAt: string | Date, now = new Date()): string { const day = formatChatDayLabel(startsAt, now) const from = formatPersianDate(startsAt, { hour: '2-digit', minute: '2-digit' }) const to = formatPersianDate(endsAt, { hour: '2-digit', minute: '2-digit' }) return `${day} ${from} تا ${to}` } export function formatIranianMobile(value: unknown, fallback = EMPTY_VALUE): string { if (typeof value !== 'string' || value.length === 0) return fallback return value.startsWith('98') && value.length === 12 ? `0${value.slice(2)}` : value } export function truncateValue(value: unknown, maxLength = 80, fallback = EMPTY_VALUE): string { if (typeof value !== 'string' || value.trim().length === 0) return fallback return value.length > maxLength ? `${value.slice(0, maxLength)}…` : value } /** Relative Persian time for review/activity timestamps (e.g. «۲ روز پیش»). */ export function formatRelativeTimePersian(value: unknown, now = new Date(), fallback = EMPTY_VALUE): string { if (!value || (typeof value !== 'string' && !(value instanceof Date))) return fallback const date = value instanceof Date ? value : new Date(value) if (!Number.isFinite(date.getTime())) return fallback const diffMs = Math.max(0, now.getTime() - date.getTime()) const diffMinutes = Math.floor(diffMs / 60_000) if (diffMinutes < 1) return texts.common.justNow if (diffMinutes < 60) { return texts.common.minutesAgo.replace('{count}', toPersianDigits(String(diffMinutes))) } const diffHours = Math.floor(diffMinutes / 60) if (diffHours < 24) { return texts.common.hoursAgo.replace('{count}', toPersianDigits(String(diffHours))) } const diffDays = Math.floor(diffHours / 24) if (diffDays < 7) { return texts.common.daysAgo.replace('{count}', toPersianDigits(String(diffDays))) } const diffWeeks = Math.floor(diffDays / 7) if (diffWeeks < 5) { return texts.common.weeksAgo.replace('{count}', toPersianDigits(String(diffWeeks))) } return formatPersianDate(date, { dateStyle: 'medium' }) }