bc224a2291
Render the prepaid resource credit "remaining" and "expires" text in Persian with Jalali calendar when locale is fa: localized digits and day/hour/minute units for time left, and weekday + Jalali date + time for the expiry. Parts are assembled explicitly so order is stable regardless of the runtime's ICU pattern data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
99 lines
3.7 KiB
TypeScript
99 lines
3.7 KiB
TypeScript
export function formatBytes(bytes?: number): string {
|
|
if (!bytes) return '—';
|
|
if (bytes < 1024) return `${bytes} B`;
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
}
|
|
|
|
export function parseCpuToMillicores(cpu: string): number {
|
|
if (!cpu) return 0;
|
|
if (cpu.endsWith('m')) return parseFloat(cpu);
|
|
return parseFloat(cpu) * 1000;
|
|
}
|
|
|
|
export function parseMemoryToMi(mem: string): number {
|
|
if (!mem) return 0;
|
|
if (mem.endsWith('Gi')) return parseFloat(mem) * 1024;
|
|
if (mem.endsWith('Mi')) return parseFloat(mem);
|
|
if (mem.endsWith('Ki')) return parseFloat(mem) / 1024;
|
|
return parseFloat(mem);
|
|
}
|
|
|
|
/** Human-readable time left (days, hours, minutes). */
|
|
export function formatRemainingDurationMs(remainingMs: number): string {
|
|
const ms = Math.max(0, remainingMs);
|
|
const days = Math.floor(ms / 86400000);
|
|
const hours = Math.floor((ms % 86400000) / 3600000);
|
|
const minutes = Math.floor((ms % 3600000) / 60000);
|
|
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
|
|
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
if (minutes > 0) return `${minutes}m`;
|
|
return 'less than 1m';
|
|
}
|
|
|
|
/** Persian (با ارقام فارسی) human-readable time left, e.g. «۲۹ روز و ۱۹ ساعت و ۵۱ دقیقه». */
|
|
function formatRemainingDurationFa(remainingMs: number): string {
|
|
const ms = Math.max(0, remainingMs);
|
|
const days = Math.floor(ms / 86400000);
|
|
const hours = Math.floor((ms % 86400000) / 3600000);
|
|
const minutes = Math.floor((ms % 3600000) / 60000);
|
|
const fa = (n: number) => n.toLocaleString('fa-IR', { useGrouping: false });
|
|
const parts: string[] = [];
|
|
if (days > 0) parts.push(`${fa(days)} روز`);
|
|
if (hours > 0) parts.push(`${fa(hours)} ساعت`);
|
|
if (minutes > 0) parts.push(`${fa(minutes)} دقیقه`);
|
|
if (parts.length === 0) return 'کمتر از ۱ دقیقه';
|
|
return parts.join(' و ');
|
|
}
|
|
|
|
/** Locale-aware time left from an expiry timestamp. */
|
|
export function formatRemainingForLocale(
|
|
expiresAt: string | Date,
|
|
locale: string,
|
|
): string {
|
|
const date = typeof expiresAt === 'string' ? new Date(expiresAt) : expiresAt;
|
|
if (Number.isNaN(date.getTime())) return '—';
|
|
const remainingMs = date.getTime() - Date.now();
|
|
return locale === 'fa'
|
|
? formatRemainingDurationFa(remainingMs)
|
|
: formatRemainingDurationMs(remainingMs);
|
|
}
|
|
|
|
/**
|
|
* Expiry timestamp in the user's locale and timezone. For Persian it renders the
|
|
* Jalali date with weekday and time, e.g. «یکشنبه، ۲۹ تیر ۱۴۰۵، ساعت ۱۹:۵۸».
|
|
*/
|
|
export function formatExpiresAtLocal(
|
|
expiresAt: string | Date,
|
|
locale?: string,
|
|
): string {
|
|
const date = typeof expiresAt === 'string' ? new Date(expiresAt) : expiresAt;
|
|
if (Number.isNaN(date.getTime())) return '—';
|
|
if (locale === 'fa') {
|
|
// Assemble parts explicitly so the order is «روز هفته، روز ماه سال» regardless
|
|
// of the runtime's ICU pattern data.
|
|
const parts = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
}).formatToParts(date);
|
|
const get = (type: Intl.DateTimeFormatPartTypes) =>
|
|
parts.find((p) => p.type === type)?.value ?? '';
|
|
const weekday = get('weekday');
|
|
const dayNum = get('day');
|
|
const month = get('month');
|
|
const year = get('year');
|
|
const time = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: false,
|
|
}).format(date);
|
|
return `${weekday}، ${dayNum} ${month} ${year}، ساعت ${time}`;
|
|
}
|
|
return date.toLocaleString(locale || undefined, {
|
|
dateStyle: 'medium',
|
|
timeStyle: 'short',
|
|
});
|
|
}
|