aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/util/friendly.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/frontend/html/util/friendly.js')
-rw-r--r--src/zenserver/frontend/html/util/friendly.js74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/util/friendly.js b/src/zenserver/frontend/html/util/friendly.js
index a15252faf..f400bbce0 100644
--- a/src/zenserver/frontend/html/util/friendly.js
+++ b/src/zenserver/frontend/html/util/friendly.js
@@ -20,4 +20,78 @@ export class Friendly
static kib(x, p=0) { return Friendly.sep((BigInt(x) + 1023n) / (1n << 10n)|0n, p) + " KiB"; }
static mib(x, p=1) { return Friendly.sep( BigInt(x) / (1n << 20n), p) + " MiB"; }
static gib(x, p=2) { return Friendly.sep( BigInt(x) / (1n << 30n), p) + " GiB"; }
+
+ static duration(s)
+ {
+ const v = Number(s);
+ if (v >= 1) return Friendly.sep(v, 2) + " s";
+ if (v >= 0.001) return Friendly.sep(v * 1000, 2) + " ms";
+ if (v >= 0.000001) return Friendly.sep(v * 1000000, 1) + " µs";
+ return Friendly.sep(v * 1000000000, 0) + " ns";
+ }
+
+ /** Format a .NET-style TimeSpan string (e.g. "0:05:23.4560000") as a human-readable duration. */
+ static timespan(value)
+ {
+ if (typeof value === "number")
+ {
+ return Friendly._formatDurationMs(value);
+ }
+
+ const str = String(value);
+ const match = str.match(/^[+-]?(?:(\d+)\.)?(\d+):(\d+):(\d+)(?:\.(\d+))?$/);
+ if (!match)
+ {
+ return str;
+ }
+
+ const days = parseInt(match[1] || "0", 10);
+ const hours = parseInt(match[2], 10);
+ const minutes = parseInt(match[3], 10);
+ const seconds = parseInt(match[4], 10);
+ const frac = match[5] ? parseInt(match[5].substring(0, 3).padEnd(3, "0"), 10) : 0;
+ const totalMs = ((days * 86400 + hours * 3600 + minutes * 60 + seconds) * 1000) + frac;
+
+ return Friendly._formatDurationMs(totalMs);
+ }
+
+ static _formatDurationMs(ms)
+ {
+ const seconds = Math.floor(ms / 1000);
+ const minutes = Math.floor(seconds / 60);
+ const hours = Math.floor(minutes / 60);
+ const days = Math.floor(hours / 24);
+
+ if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`;
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
+ if (seconds > 0) return `${seconds}.${String(ms % 1000).padStart(3, "0")}s`;
+ return `${ms}ms`;
+ }
+
+ /** Format an ISO / .NET datetime string as a friendly local date+time. */
+ static datetime(value)
+ {
+ const d = new Date(value);
+ if (isNaN(d.getTime()))
+ {
+ return String(value);
+ }
+ return d.toLocaleString(undefined, {
+ year: "numeric", month: "short", day: "numeric",
+ hour: "2-digit", minute: "2-digit", second: "2-digit",
+ });
+ }
+
+ static bytes(x)
+ {
+ const v = BigInt(Math.trunc(Number(x)));
+ if (v >= (1n << 60n)) return Friendly.sep(Number(v) / Number(1n << 60n), 2) + " EiB";
+ if (v >= (1n << 50n)) return Friendly.sep(Number(v) / Number(1n << 50n), 2) + " PiB";
+ if (v >= (1n << 40n)) return Friendly.sep(Number(v) / Number(1n << 40n), 2) + " TiB";
+ if (v >= (1n << 30n)) return Friendly.sep(Number(v) / Number(1n << 30n), 2) + " GiB";
+ if (v >= (1n << 20n)) return Friendly.sep(Number(v) / Number(1n << 20n), 1) + " MiB";
+ if (v >= (1n << 10n)) return Friendly.sep(Number(v) / Number(1n << 10n), 0) + " KiB";
+ return Friendly.sep(Number(v), 0) + " B";
+ }
}