● Zion Boggan
repos/Docket/lib/format.ts
zionboggan.com ↗
109 lines · typescript
History for this file →
1
 
2
 
3
export function formatUsd(n: number, opts?: { compact?: boolean }): string {
4
  if (!Number.isFinite(n)) return "$0";
5
  if (opts?.compact && Math.abs(n) >= 1_000_000_000) {
6
    return `$${(n / 1_000_000_000).toFixed(1)}B`;
7
  }
8
  if (opts?.compact && Math.abs(n) >= 1_000_000) {
9
    return `$${(n / 1_000_000).toFixed(0)}M`;
10
  }
11
  if (opts?.compact && Math.abs(n) >= 1_000) {
12
    return `$${(n / 1_000).toFixed(0)}K`;
13
  }
14
  return n.toLocaleString("en-US", {
15
    style: "currency",
16
    currency: "USD",
17
    maximumFractionDigits: 0,
18
  });
19
}
20
 
21
const FEDERAL_BILL_TYPES: Record<string, string> = {
22
  HR: "H.R.",
23
  S: "S.",
24
  HRES: "H.Res.",
25
  SRES: "S.Res.",
26
  HJRES: "H.J.Res.",
27
  SJRES: "S.J.Res.",
28
  HCONRES: "H.Con.Res.",
29
  SCONRES: "S.Con.Res.",
30
  HAMDT: "H.Amdt.",
31
  SAMDT: "S.Amdt.",
32
};
33
 
34
 
35
export function formatBillNumber(raw: string | null | undefined): string {
36
  if (!raw) return raw ?? "";
37
  const s = raw.trim();
38
  if (/[.\s]/.test(s)) return s;
39
  const m = s.match(/^([A-Za-z]+)(\d+)$/);
40
  if (!m) return s;
41
  const pretty = FEDERAL_BILL_TYPES[m[1].toUpperCase()];
42
  return pretty ? `${pretty} ${m[2]}` : s;
43
}
44
 
45
 
46
export function ordinalSuffix(n: number): string {
47
  const abs = Math.abs(Math.trunc(n));
48
  const mod100 = abs % 100;
49
  if (mod100 >= 11 && mod100 <= 13) return "th";
50
  switch (abs % 10) {
51
    case 1:
52
      return "st";
53
    case 2:
54
      return "nd";
55
    case 3:
56
      return "rd";
57
    default:
58
      return "th";
59
  }
60
}
61
 
62
export function ordinal(n: number): string {
63
  return `${n}${ordinalSuffix(n)}`;
64
}
65
 
66
 
67
export function congressLabel(congress: number | null | undefined): string {
68
  return congress && congress > 0 ? `${ordinal(congress)} Congress` : "";
69
}
70
 
71
export function formatDate(iso: string): string {
72
  if (!iso) return "\u2014";
73
  const d = new Date(iso + (iso.length === 10 ? "T00:00:00Z" : ""));
74
  if (Number.isNaN(d.getTime())) return "\u2014";
75
  return d.toLocaleDateString("en-US", {
76
    timeZone: "UTC",
77
    year: "numeric",
78
    month: "short",
79
    day: "2-digit",
80
  });
81
}
82
 
83
 
84
export function formatDateShort(iso: string): string {
85
  if (!iso) return "\u2014";
86
  const d = new Date(iso.length === 10 ? iso + "T00:00:00Z" : iso);
87
  if (Number.isNaN(d.getTime())) return "\u2014";
88
  return d.toLocaleDateString("en-US", {
89
    timeZone: "UTC",
90
    month: "short",
91
    day: "numeric",
92
  });
93
}
94
 
95
 
96
export function formatEt(iso: string): string {
97
  if (!iso) return "";
98
  if (!iso.includes("T") || /T00:00(:00)?(\.0+)?Z?$/.test(iso)) return "";
99
  const d = new Date(iso);
100
  if (Number.isNaN(d.getTime())) return "";
101
  return (
102
    d.toLocaleTimeString("en-US", {
103
      timeZone: "America/New_York",
104
      hour: "2-digit",
105
      minute: "2-digit",
106
      hour12: false,
107
    }) + " ET"
108
  );
109
}