feat(web): one date standard across the UI — "25 Qer 14:30"
Dates were a mix: catalog-formatted "25 Qershor 20:01" where screens used
formatRelativeDateTime, and browser-locale "7/6/2026, 9:34 AM" in ~20
places that called raw toLocaleString/-Date-/-Time-String. Unified:
- common.monthsShort in both catalogs (Jan/Shk/…/Qer/Korr/…/Dhj);
formatDate ("25 Qer", year only when not current), formatDateTime
("25 Qer 14:30", optional seconds), formatClock ("HH:mm", 24h) in
lib/format.ts. formatRelativeDateTime keeps Sot/Dje and switches its
older-dates branch to the same short months.
- Every raw toLocale* DATE call swept: shifts X-report line, plan
effective dates, sub version labels, drawer today feed, snapshot
tooltips, device footer checkedAt, event-detail timestamp (keeps
seconds — chain evidence), tariff composer active-since + version
sidebar. Number toLocaleString (thousand separators) untouched.
The catalogs in this commit also carry the keys for the two follow-up
commits (fee breakdown, composer increment labels).
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -85,6 +85,43 @@ function monthName(d: Date, t: TFn): string {
|
||||
return String(d.getMonth() + 1);
|
||||
}
|
||||
|
||||
/** Short month ("Qer", "Korr") from the catalog — the UI-wide date standard
|
||||
* (2026-07-06): every visible date reads "25 Qer" / "7 Korr 2025", never the
|
||||
* browser-locale "7/6/2026". Falls back to the full name, then the number. */
|
||||
function monthShort(d: Date, t: TFn): string {
|
||||
const months = t("common.monthsShort", { returnObjects: true });
|
||||
if (Array.isArray(months) && typeof months[d.getMonth()] === "string") {
|
||||
return months[d.getMonth()] as string;
|
||||
}
|
||||
return monthName(d, t);
|
||||
}
|
||||
|
||||
/** "HH:mm" (local, 24h) — the unified time-of-day everywhere ("—" for bad input). */
|
||||
export function formatClock(iso: string | null): string {
|
||||
if (!iso) return "—";
|
||||
const d = new Date(iso);
|
||||
return Number.isNaN(d.getTime()) ? "—" : hhmm(d);
|
||||
}
|
||||
|
||||
/** "25 Qer" (current year) / "25 Qer 2025" (other years) — the unified DATE. */
|
||||
export function formatDate(iso: string | null, t: TFn): string {
|
||||
if (!iso) return "—";
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return "—";
|
||||
const base = `${d.getDate()} ${monthShort(d, t)}`;
|
||||
return d.getFullYear() === new Date().getFullYear() ? base : `${base} ${d.getFullYear()}`;
|
||||
}
|
||||
|
||||
/** "25 Qer 14:30" (+ ":ss" when `seconds`) — the unified absolute DATE+TIME. Use
|
||||
* formatRelativeDateTime instead where "Sot/Dje" reads better (feeds, history). */
|
||||
export function formatDateTime(iso: string | null, t: TFn, opts?: { seconds?: boolean }): string {
|
||||
if (!iso) return "—";
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return "—";
|
||||
const sec = opts?.seconds ? `:${String(d.getSeconds()).padStart(2, "0")}` : "";
|
||||
return `${formatDate(iso, t)} ${hhmm(d)}${sec}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Human, day-relative date+time for sessions/logs/history. An event from earlier
|
||||
* today reads "Sot 10:48", yesterday "Dje 17:33", and anything older a localized
|
||||
@@ -101,9 +138,6 @@ export function formatRelativeDateTime(iso: string | null, t: TFn): string {
|
||||
const diff = dayDiff(d, new Date());
|
||||
if (diff === 0) return `${t("common.today")} ${hhmm(d)}`;
|
||||
if (diff === 1) return `${t("common.yesterday")} ${hhmm(d)}`;
|
||||
// Older (or future): "17 Qershor 10:48", with the year only if it differs.
|
||||
const sameYear = d.getFullYear() === new Date().getFullYear();
|
||||
const month = monthName(d, t);
|
||||
const date = sameYear ? `${d.getDate()} ${month}` : `${d.getDate()} ${month} ${d.getFullYear()}`;
|
||||
return `${date} ${hhmm(d)}`;
|
||||
// Older (or future): "17 Qer 10:48" — the short-month standard, year only if it differs.
|
||||
return `${formatDate(iso, t)} ${hhmm(d)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user