fix: keep birthdays and show Shamsi dates
Imported customers keep their birthday, and assistant text plus remaining calendars use the Jalali date. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { GamificationEvent, GamificationTxDto } from '@mento/shared';
|
||||
import { GAMIFICATION_EVENTS } from '@mento/shared';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
import { formatJalaliDateTime, formatJalaliDay, toFaDigits } from '@/lib/jalali';
|
||||
|
||||
export { apiError } from '@/lib/routines';
|
||||
|
||||
@@ -81,15 +81,13 @@ export function timeAgo(iso: string): string {
|
||||
if (hours < 24) return `${toFaDigits(hours)} ساعت پیش`;
|
||||
const days = Math.floor(hours / 24);
|
||||
if (days < 7) return `${toFaDigits(days)} روز پیش`;
|
||||
return new Intl.DateTimeFormat('fa-IR', { dateStyle: 'medium' }).format(d);
|
||||
return formatJalaliDay(d);
|
||||
}
|
||||
|
||||
export function formatDateTime(iso: string): string {
|
||||
try {
|
||||
return new Intl.DateTimeFormat('fa-IR', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(iso));
|
||||
} catch {
|
||||
return iso;
|
||||
}
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return iso;
|
||||
return formatJalaliDateTime(d);
|
||||
}
|
||||
|
||||
export const REWARD_ICONS = ['gift', 'star', 'trophy', 'medal', 'crown', 'calendar-sun', 'bag', 'target', 'fire'] as const;
|
||||
|
||||
@@ -204,6 +204,11 @@ export function formatJalaliDayClock(d: Date): string {
|
||||
return `${toFaDigits(jd)} ${jalaliMonthName(jm)} ${formatClockFa(d)}`;
|
||||
}
|
||||
|
||||
/** مثل: ۸ شهریور ۱۴۰۵، ۱۶:۳۰ */
|
||||
export function formatJalaliDateTime(d: Date): string {
|
||||
return `${formatJalaliDay(d)}، ${formatClockFa(d)}`;
|
||||
}
|
||||
|
||||
export function calendarDaysBetween(from: Date, to: Date): number {
|
||||
return Math.round(
|
||||
(startOfLocalDay(to).getTime() - startOfLocalDay(from).getTime()) / 86_400_000,
|
||||
@@ -270,3 +275,93 @@ export function formatJalaliNumeric(d: Date): string {
|
||||
const { jy, jm, jd } = dateToJalali(d);
|
||||
return `${toFaDigits(jy)}/${toFaDigits(String(jm).padStart(2, '0'))}/${toFaDigits(String(jd).padStart(2, '0'))}`;
|
||||
}
|
||||
|
||||
const GREG_MONTHS: Array<[string, number]> = [
|
||||
['ژانویه', 1],
|
||||
['january', 1],
|
||||
['فوریه', 2],
|
||||
['february', 2],
|
||||
['مارس', 3],
|
||||
['march', 3],
|
||||
['آوریل', 4],
|
||||
['april', 4],
|
||||
['ژوئن', 6],
|
||||
['june', 6],
|
||||
['ژوئیه', 7],
|
||||
['جولای', 7],
|
||||
['july', 7],
|
||||
['آگوست', 8],
|
||||
['آگست', 8],
|
||||
['اوت', 8],
|
||||
['august', 8],
|
||||
['سپتامبر', 9],
|
||||
['september', 9],
|
||||
['اکتبر', 10],
|
||||
['october', 10],
|
||||
['نوامبر', 11],
|
||||
['november', 11],
|
||||
['دسامبر', 12],
|
||||
['december', 12],
|
||||
['مه', 5],
|
||||
['می', 5],
|
||||
['may', 5],
|
||||
];
|
||||
|
||||
function toAsciiDigits(value: string) {
|
||||
return value
|
||||
.replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)))
|
||||
.replace(/[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)));
|
||||
}
|
||||
|
||||
function gregMonthNumber(name: string): number | null {
|
||||
const key = name.trim().toLowerCase();
|
||||
return GREG_MONTHS.find(([label]) => label === key)?.[1] ?? null;
|
||||
}
|
||||
|
||||
function inferGregorianYear(month: number, day: number, now = new Date()): number {
|
||||
const gy = now.getFullYear();
|
||||
const gm = now.getMonth() + 1;
|
||||
const gd = now.getDate();
|
||||
if (month > gm || (month === gm && day > gd)) return gy - 1;
|
||||
return gy;
|
||||
}
|
||||
|
||||
/** «21 سپتامبر» و تاریخ میلادی عددی را در متن نمایشی به شمسی تبدیل میکند. */
|
||||
export function rewriteGregorianDates(text: string, now = new Date()): string {
|
||||
const monthAlt = GREG_MONTHS.map(([name]) => name).join('|');
|
||||
const dayMonth = new RegExp(
|
||||
`(^|[^\\p{L}\\d])([0-9۰-۹]{1,2})\\s*(${monthAlt})(?![\\p{L}\\u200c])(?:\\s+([0-9۰-۹]{4}))?`,
|
||||
'giu',
|
||||
);
|
||||
const monthDay = new RegExp(
|
||||
`(^|[^\\p{L}\\d])(${monthAlt})(?![\\p{L}\\u200c])\\s+([0-9۰-۹]{1,2})(?:\\s*,?\\s*([0-9۰-۹]{4}))?`,
|
||||
'giu',
|
||||
);
|
||||
const iso = /\b((?:19|20)\d{2})-(\d{2})-(\d{2})\b/g;
|
||||
|
||||
const stamp = (year: number, month: number, day: number) => {
|
||||
if (month < 1 || month > 12 || day < 1 || day > 31 || year < 1700 || year > 2500) return null;
|
||||
const j = toJalali(year, month, day);
|
||||
return `${toFaDigits(j.jy)}/${toFaDigits(String(j.jm).padStart(2, '0'))}/${toFaDigits(String(j.jd).padStart(2, '0'))}`;
|
||||
};
|
||||
const fromParts = (dayRaw: string, monthName: string, yearRaw?: string) => {
|
||||
const day = Number(toAsciiDigits(dayRaw));
|
||||
const month = gregMonthNumber(monthName);
|
||||
if (!month) return null;
|
||||
const year = yearRaw ? Number(toAsciiDigits(yearRaw)) : inferGregorianYear(month, day, now);
|
||||
return stamp(year, month, day);
|
||||
};
|
||||
|
||||
let out = text.replace(dayMonth, (full, lead: string, day: string, month: string, year?: string) => {
|
||||
const next = fromParts(day, month, year);
|
||||
return next ? `${lead}${next}` : full;
|
||||
});
|
||||
out = out.replace(monthDay, (full, lead: string, month: string, day: string, year?: string) => {
|
||||
const next = fromParts(day, month, year);
|
||||
return next ? `${lead}${next}` : full;
|
||||
});
|
||||
out = out.replace(iso, (full, year: string, month: string, day: string) => {
|
||||
return stamp(Number(year), Number(month), Number(day)) ?? full;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user