Files
Momento/memento-note/lib/utils/format-localized-date.ts
Antigravity 3bdbec575a feat(ui): panneau repliable, carnets plus lisibles et accueil plus clair
Harmonise la liste des notes et l’éditeur (titres, dates, retour),
rend la saisie rapide et les raccourcis de l’accueil utilisables
sur téléphone, et conserve le panneau latéral repliable.
2026-09-05 20:53:48 +00:00

114 lines
3.0 KiB
TypeScript
Raw 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.
import { format, type Locale } from 'date-fns'
import { toJalaali } from 'jalaali-js'
const JALALI_MONTHS_FA = [
'فروردین',
'اردیبهشت',
'خرداد',
'تیر',
'مرداد',
'شهریور',
'مهر',
'آبان',
'آذر',
'دی',
'بهمن',
'اسفند',
] as const
const PERSIAN_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const
/** Western digits → Persian (Extended Arabic-Indic) numerals, e.g. 1405 → ۱۴۰۵ */
export function toPersianDigits(input: string): string {
return input.replace(/\d/g, (ch) => PERSIAN_DIGITS[Number(ch)] ?? ch)
}
function pad2(n: number): string {
return n < 10 ? `0${n}` : `${n}`
}
function formatJalaliAbsolute(date: Date, pattern: string): string {
const { jy, jm, jd } = toJalaali(date)
const monthName = JALALI_MONTHS_FA[jm - 1]
const h = pad2(date.getHours())
const min = pad2(date.getMinutes())
let s: string
switch (pattern) {
case 'd MMM yyyy':
s = `${jd} ${monthName} ${jy}`
break
case 'd MMM yyyy · HH:mm':
s = `${jd} ${monthName} ${jy} · ${h}:${min}`
break
case 'd MMM yyyy HH:mm':
s = `${jd} ${monthName} ${jy} ${h}:${min}`
break
case 'd MMM · HH:mm':
s = `${jd} ${monthName} · ${h}:${min}`
break
case 'MMM d, yyyy':
s = `${monthName} ${jd}، ${jy}`
break
default:
s = `${jd} ${monthName} ${jy}`
}
return toPersianDigits(s)
}
/**
* Absolute calendar dates for Persian (`fa`) use the Solar Hijri (Jalali / هجری شمسی) calendar.
* Times remain in the user's local timezone. For relative phrases, keep using `formatDistanceToNow` with `faIR`.
*/
export function formatAbsoluteDateLocalized(
date: Date | string,
language: string,
pattern: string,
locale: Locale
): string {
const d = typeof date === 'string' ? new Date(date) : date
if (language === 'fa') {
return formatJalaliAbsolute(d, pattern)
}
return format(d, pattern, { locale })
}
const INTL_LOCALE: Record<string, string> = {
zh: 'zh-CN',
fa: 'fa-IR',
}
/**
* Calendar day for note lists and the editor, in the user's locale.
* Uses the local timezone (same as `formatAbsoluteDateLocalized`) so a late-evening
* note does not jump to the previous UTC day. Persian uses Jalali + Persian digits.
*/
export function formatNoteCalendarDate(
date: Date | string,
language: string,
): string {
const d = typeof date === 'string' ? new Date(date) : date
if (Number.isNaN(d.getTime())) return ''
if (language === 'fa') {
return formatJalaliAbsolute(d, 'd MMM yyyy')
}
const locale = INTL_LOCALE[language] || language
try {
return new Intl.DateTimeFormat(locale, {
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(d)
} catch {
return new Intl.DateTimeFormat('en', {
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(d)
}
}
export function dateTextDirection(language: string): 'rtl' | 'ltr' {
return language === 'fa' || language === 'ar' ? 'rtl' : 'ltr'
}