SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
4.3 KB · 107 lines typescript
Raw Blame History
1/** Formatting helpers. All numerals are rendered in mono/tabular; all timestamps go through formatTime(). */23export type TimeMode = 'utc' | 'local';45const nf = (digits: number) => new Intl.NumberFormat('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits });6const NF0 = nf(0);7const NF1 = nf(1);8const NF2 = nf(2);910export function fmt(v: number | null | undefined, digits = 1): string {11  if (v == null || Number.isNaN(v)) return '—';12  return digits === 0 ? NF0.format(v) : digits === 2 ? NF2.format(v) : digits === 1 ? NF1.format(v) : nf(digits).format(v);13}14export const fmtInt = (v: number | null | undefined) => fmt(v, 0);1516/** Signed delta with a true minus sign: +6.3 / −2.1 / 0.0 */17export function fmtDelta(v: number | null | undefined, digits = 1): string {18  if (v == null || Number.isNaN(v)) return '—';19  const s = fmt(Math.abs(v), digits);20  if (v > 0) return `+${s}`;21  if (v < 0) return `−${s}`;22  return s;23}2425export function fmtPct(ratio: number | null | undefined, digits = 0): string {26  if (ratio == null || Number.isNaN(ratio)) return '—';27  return `${fmt(ratio * 100, digits)} %`;28}2930export function fmtMs(v: number | null | undefined, digits = 0): string {31  if (v == null || Number.isNaN(v)) return '—';32  return `${fmt(v, digits)} ms`;33}3435export function fmtBytes(b: number | null | undefined): string {36  if (b == null) return '—';37  const u = ['B', 'KB', 'MB', 'GB', 'TB'];38  let i = 0;39  let v = b;40  while (v >= 1024 && i < u.length - 1) {41    v /= 1024;42    i++;43  }44  return `${fmt(v, i === 0 ? 0 : 1)} ${u[i]}`;45}4647export function fmtRatio(v: number | null | undefined): string {48  if (v == null || Number.isNaN(v)) return '—';49  return `${fmt(v, v >= 10 ? 0 : 1)}×`;50}5152export function fmtDuration(seconds: number | null | undefined): string {53  if (seconds == null || Number.isNaN(seconds)) return '—';54  const s = Math.max(0, Math.round(seconds));55  if (s < 60) return `${s} s`;56  const m = Math.floor(s / 60);57  if (m < 60) return `${m} min`;58  const h = Math.floor(m / 60);59  const rm = m % 60;60  if (h < 48) return rm ? `${h} h ${rm} min` : `${h} h`;61  const d = Math.floor(h / 24);62  return `${d} d ${h % 24} h`;63}6465/** "3 s ago", "2 min ago", "4 h ago" — relative to `nowMs`. */66export function fmtAgo(ts: string | number | Date | null | undefined, nowMs = Date.now()): string {67  if (!ts) return '—';68  const t = typeof ts === 'number' ? ts : new Date(ts).getTime();69  if (Number.isNaN(t)) return '—';70  const s = Math.max(0, Math.round((nowMs - t) / 1000));71  if (s < 60) return `${s} s ago`;72  const m = Math.floor(s / 60);73  if (m < 60) return `${m} min ago`;74  const h = Math.floor(m / 60);75  if (h < 48) return `${h} h ago`;76  return `${Math.floor(h / 24)} d ago`;77}7879export type TimeStyle = 'time' | 'short' | 'full' | 'date' | 'month';8081/** Single time formatter. UTC is the default (spec §67: UTC storage, local display with a toggle). */82export function formatTime(ts: string | number | Date | null | undefined, mode: TimeMode = 'utc', style: TimeStyle = 'short'): string {83  if (!ts) return '—';84  const d = ts instanceof Date ? ts : new Date(ts);85  if (Number.isNaN(d.getTime())) return '—';86  const tz = mode === 'utc' ? 'UTC' : undefined;87  const opts: Intl.DateTimeFormatOptions =88    style === 'time'89      ? { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: tz }90      : style === 'date'91        ? { year: 'numeric', month: 'short', day: '2-digit', timeZone: tz }92        : style === 'month'93          ? { year: 'numeric', month: 'long', timeZone: tz }94          : style === 'full'95            ? { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: tz, timeZoneName: 'short' }96            : { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false, timeZone: tz };97  let out = new Intl.DateTimeFormat('en-GB', opts).format(d);98  if (mode === 'utc' && (style === 'short' || style === 'time')) out += ' UTC';99  return out;100}101102export function isoDate(ts: string | Date): string {103  return (ts instanceof Date ? ts : new Date(ts)).toISOString().slice(0, 10);104}105106export const MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];107