The region says which clock, this app says how to punctuate it

Auto was the region's answer whole, dot and all, so Denmark got 13.45
while every explicit setting beside it wrote 13:45. One screen punctuating
a time differently from the next is not local colour, it is an
inconsistency, and it was ours to fix rather than the locale's.

So the region is asked one question now — does this reader expect 13:45
or 01:45 pm, which is a real difference in how people tell the time — and
the printing is the same two lines for all three settings. Denmark, Poland
and Japan read 24-hour and get 13:45 from auto; the US reads 12-hour and
gets 01:45 pm from it, with the same colon and the same marker as
everywhere else.

The marker reads in English wherever it appears. That is the trade the
setting already made when it offered a 12-hour clock to regions that do
not use one.

This drops the formatToParts pass from the commit before it: once both
halves are fully specified there is nothing left to ask the locale, and
the answer is shorter written out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-03 20:34:51 +02:00
co-authored by Claude Opus 5
parent 263d35c688
commit 830ef0cde5
+45 -21
View File
@@ -87,32 +87,56 @@ export function formatDateTime(value) {
// //
// hourCycle rather than hour12: with hour12:false the en-US formatter prints // hourCycle rather than hour12: with hour12:false the en-US formatter prints
// midnight as 24:00. // midnight as 24:00.
// The setting decides *which* clock; this file decides how it is punctuated.
//
// That split is the whole of it. A region is worth asking whether a reader
// expects 13:45 or 01:45 pm — that is a real difference in how people tell the
// time. It is not worth asking whether the two numbers are joined by a colon or
// a dot: Danish writes 13.45, and one screen of DriverVault writing 13.45 while
// the next writes 13:45 is not local colour, it is an inconsistency. So every
// time this app prints comes out of the same two lines below, and the region is
// asked one question only, under "auto".
//
// The cost is that the am/pm marker reads in English everywhere. It is the same
// trade the setting itself makes: a 12-hour clock is not a convention most of
// these regions use, so choosing one — or living in a region that does — is
// choosing the clock that comes with it.
export function formatTime(value) { export function formatTime(value) {
if (!value) return "—"; if (!value) return "—";
const d = value instanceof Date ? value : new Date(value); const d = value instanceof Date ? value : new Date(value);
if (isNaN(d)) return "—"; if (isNaN(d)) return "—";
const opts = { hour: "2-digit", minute: "2-digit" }; const h = d.getHours();
const mode = prefs.timeFormat; const pad = (n) => String(n).padStart(2, "0");
if (mode !== "24" && mode !== "12") { const mm = pad(d.getMinutes());
// Auto is the region's, whole: 13.45 is how Danish writes a clock, and a
// setting that says "follow the region" has no business arguing with it.
return d.toLocaleTimeString(prefs.locale || undefined, opts);
}
// Asking for a clock outright is asking for its separator too. Somebody who const mode = prefs.timeFormat;
// picked "24-hour" means 13:45, not the 13.45 their region would have written const twelve = mode === "12" || (mode !== "24" && regionReadsTwelveHour());
// — the point of leaving auto was to stop the region deciding. Only the mark if (!twelve) return `${pad(h)}:${mm}`;
// between the hour and the minute is pinned: the rest is still the locale's, // 12 for both noon and midnight, and midnight is the am one.
// including whether there is an am/pm marker, what it reads and where it sits. return `${pad(h % 12 || 12)}:${mm} ${h < 12 ? "am" : "pm"}`;
opts.hourCycle = mode === "24" ? "h23" : "h12"; }
const parts = new Intl.DateTimeFormat(prefs.locale || undefined, opts).formatToParts(d);
return parts // Whether the chosen region tells the time on a 12-hour clock — the one question
.map((p, i) => // "auto" asks it. Cached because this is asked once per timestamp on a page that
p.type === "literal" && parts[i - 1]?.type === "hour" && parts[i + 1]?.type === "minute" // can hold a great many, and the answer only changes when the region does.
? ":" const twelveHourRegions = new Map();
: p.value
) function regionReadsTwelveHour() {
.join(""); const locale = prefs.locale || "";
if (!twelveHourRegions.has(locale)) {
let twelve = false;
try {
const cycle = new Intl.DateTimeFormat(locale || undefined, { hour: "numeric" })
.resolvedOptions().hourCycle;
twelve = cycle === "h11" || cycle === "h12";
} catch {
// An unusable locale is not a reason to print nothing; 24-hour is the
// safer default, being the one that cannot be read as the wrong half of
// the day.
}
twelveHourRegions.set(locale, twelve);
}
return twelveHourRegions.get(locale);
} }
// Every number we render goes through here so the grouping separator follows // Every number we render goes through here so the grouping separator follows