1const UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
2
3/** Binary units, matching what ZFS and the kernel report. */
4export function bytes(value: number) {
5 let unit = 0;
6 while (Math.abs(value) >= 1024 && unit < UNITS.length - 1) {
7 value /= 1024;
8 unit++;
9 }
10 return `${value.toFixed(unit === 0 ? 0 : value < 10 ? 1 : 0)} ${UNITS[unit]}`;
11}
12
13export const rate = (value: number) => bytes(value) + "/s";
14export const percent = (value: number) => `${value.toFixed(Number.isInteger(value) || value >= 10 ? 0 : 1)}%`;
15export const cores = (value: number) => value.toFixed(value < 10 ? 2 : 1);
16export const watts = (value: number) => `${Math.round(value)} W`;
17export const count = (value: number) => value.toLocaleString();
18export const plural = (n: number, one: string, many = one + "s") => `${count(n)} ${n === 1 ? one : many}`;
19export const celsius = (value: number) => `${Math.round(value)} °C`;
20
21const SPANS = [["mo", 30 * 86400], ["d", 86400], ["h", 3600], ["m", 60]] as const;
22
23/** Up to two units and no seconds past the first minute: "45s", "12m", "3h 5m", "19d 5h", "3mo 22d". */
24export function duration(seconds: number) {
25 const first = SPANS.findIndex(([, size]) => seconds >= size);
26 if (first === -1) return `${Math.floor(Math.max(0, seconds))}s`;
27 const [label, size] = SPANS[first]!;
28 const next = SPANS[first + 1];
29 const rest = next ? Math.floor((seconds % size) / next[1]) : 0;
30 return `${Math.floor(seconds / size)}${label}${rest ? ` ${rest}${next![0]}` : ""}`;
31}
32
33/** One unit while under a week old ("22h ago"), then the date. */
34export function ago(t: number) {
35 const seconds = Date.now() / 1000 - t;
36 if (seconds >= 7 * 86400) return date(t);
37 const unit = SPANS.find(([, size]) => seconds >= size);
38 return unit ? `${Math.floor(seconds / unit[1])}${unit[0]} ago` : "just now";
39}
40
41/** How long until `t`, from `now`: "in 3h 5m", or "soon" once it's due. */
42export function until(t: number, now = Date.now() / 1000) {
43 return t > now ? `in ${duration(t - now)}` : "soon";
44}
45
46export function clock(t: number) {
47 return new Date(t * 1000).toLocaleTimeString(undefined, { hour12: false });
48}
49
50export const date = (t: number) => new Date(t * 1000).toLocaleDateString(undefined, { dateStyle: "medium" });
51export const datetime = (t: number) => new Date(t * 1000).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" });