/** * Tüm sayı / para / tarih biçimlendirmesi buradan yapılır (KAKEIBO_PROMPT §9). * * - Para: backend'den **kuruş cinsinden tamsayı** gelir → `1.250,00 ₺` * (para birimi rakamın sağında, arada bir boşluk) * - Miktar (altın, döviz): milyonda bir birim (`_e6`) → `12,50 gr` * - Tarih: `GG.AA.YYYY` */ export type CurrencyCode = 'TRY' | 'USD' | 'EUR' | 'XAU_GRAM' const CURRENCY_SUFFIX: Record = { TRY: '₺', USD: '$', EUR: '€', XAU_GRAM: 'gr', } const numberFormats = new Map() function numberFormat(decimals: number): Intl.NumberFormat { let f = numberFormats.get(decimals) if (!f) { f = new Intl.NumberFormat('tr-TR', { minimumFractionDigits: decimals, maximumFractionDigits: decimals, }) numberFormats.set(decimals, f) } return f } /** Ondalıklı sayıyı Türkçe biçimler: `1250.5` → `1.250,50` */ export function formatNumber(value: number, decimals = 2): string { return numberFormat(decimals).format(value) } /** Kuruş → `1.250,00 ₺`. `signed: true` ise pozitiflere `+` eklenir. */ export function formatMoney( kurus: number, currency: CurrencyCode = 'TRY', options: { signed?: boolean } = {}, ): string { const text = `${formatNumber(kurus / 100)} ${CURRENCY_SUFFIX[currency]}` return options.signed && kurus > 0 ? `+${text}` : text } /** Milyonda bir birim → `12,50 gr` (altın, döviz miktarı) */ export function formatQuantity(e6: number, unit: string, decimals = 2): string { return `${formatNumber(e6 / 1_000_000, decimals)} ${unit}` } /** Yüzde: `0.123` → `%12,3` (Türkçede % işareti başta) */ export function formatPercent(ratio: number, decimals = 1): string { return `%${formatNumber(ratio * 100, decimals)}` } /** * Kullanıcının yazdığı tutarı kuruşa çevirir. Geçersizse `null`. * * Kabul edilenler: `1250` · `1.250` · `1250,5` · `1.250,50` · `1250.50` (telefon klavyesi) * Kural: virgül varsa ondalık ayırıcıdır, noktalar binliktir. * Virgül yoksa ve son noktadan sonra 1–2 rakam varsa o nokta ondalıktır. * Float hatası olmasın diye hesap metin üzerinden yapılır. */ export function parseMoney(input: string): number | null { let s = input.replace(/[\s₺]/g, '') if (!s) return null let negative = false if (s.startsWith('-')) { negative = true s = s.slice(1) } let intPart: string let fracPart = '' if (s.includes(',')) { const parts = s.split(',') if (parts.length !== 2) return null intPart = parts[0].replace(/\./g, '') fracPart = parts[1] } else { const m = /^(.*)\.(\d{1,2})$/.exec(s) if (m && !m[1].includes('.')) { intPart = m[1] fracPart = m[2] } else { intPart = s.replace(/\./g, '') } } if (intPart === '') intPart = '0' if (!/^\d+$/.test(intPart) || !/^\d{0,2}$/.test(fracPart)) return null const kurus = Number(intPart) * 100 + Number(fracPart.padEnd(2, '0')) if (!Number.isSafeInteger(kurus)) return null return negative ? -kurus : kurus } /** `"2026-12-09"` veya ISO zaman damgası → `09.12.2026` */ export function formatDate(value: string | Date): string { if (typeof value === 'string') { const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value) if (m) return `${m[3]}.${m[2]}.${m[1]}` value = new Date(value) } const dd = String(value.getDate()).padStart(2, '0') const mm = String(value.getMonth() + 1).padStart(2, '0') return `${dd}.${mm}.${value.getFullYear()}` } /** Yerel tarih → `"2026-12-09"` (backend'in takvim tarihi biçimi) */ export function toIsoDate(date: Date): string { const mm = String(date.getMonth() + 1).padStart(2, '0') const dd = String(date.getDate()).padStart(2, '0') return `${date.getFullYear()}-${mm}-${dd}` } /** `"2026-12-07"` → `07.12` (hafta aralıkları gibi kısa gösterimler) */ export function formatDayMonth(iso: string): string { const m = /^\d{4}-(\d{2})-(\d{2})$/.exec(iso) return m ? `${m[2]}.${m[1]}` : iso } const weekdayFormat = new Intl.DateTimeFormat('tr-TR', { weekday: 'long', timeZone: 'UTC' }) /** `"2026-11-12"` → `Perşembe` (UTC üzerinden: saat dilimi günü kaydırmaz) */ export function formatWeekday(iso: string): string { const [y, m, d] = iso.split('-').map(Number) return weekdayFormat.format(new Date(Date.UTC(y, m - 1, d))) }