Locale-unabhängiges ISO-Datumsformat als Zellformat

Neues CellNumFmt 'dateiso' im Zahlenformat-Dropdown ("Datum (ISO) ·
2026-09-02"): zeigt ein Datum immer als YYYY-MM-DD, unabhängig von der
aktiven Locale (Zelle/Blatt/Dokument) -- anders als "Datum", das der
jeweiligen Locale folgt. Nützlich für export-nahe/sortierbare
Datumsspalten.

- store/types.ts: CellNumFmt um 'dateiso' erweitert.
- store/format.ts: die manuelle YYYY-MM-DD-Formatierung, die bisher nur
  für das 'plain'-Pseudo-Locale galt, in isoDateString() extrahiert und
  von einem neuen 'dateiso'-Fall in formatValue() wiederverwendet.
  NUM_FORMATS bekam den neuen Eintrag -- das Zahlenformat-Dropdown
  (SettingsPanel.vue) und die JSON-Validierung (isCellNumFmt) sind
  bereits generisch über NUM_FORMATS, keine Änderung nötig.

Tests: formatValue mit 'dateiso' über mehrere Locales (inkl. 'plain').
297 -> 298 Tests, alle grün. vue-tsc/eslint/build sauber. Live per
Playwright verifiziert: Zelle auf "Datum (ISO)" umgestellt zeigt
2026-09-02 und bleibt das auch nach Wechsel der Dokument-Locale auf
en-US, während eine andere Zelle ohne dieses Format der Locale folgt
(09/02/2026).

READMEs aktualisiert (Zahlenformat-Liste, "Datum & Zeit"-Abschnitt in
engine/README.md, Testzahl).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
wkh
2026-09-02 23:31:18 +02:00
co-authored by Claude Sonnet 5
parent e6cb18ab3c
commit af01aa5e53
5 changed files with 40 additions and 13 deletions
+14 -6
View File
@@ -43,6 +43,7 @@ export const NUM_FORMATS: ReadonlyArray<{ id: CellNumFmt; label: string }> = [
{ id: 'pct0', label: 'Prozent · 12 %' },
{ id: 'pct2', label: 'Prozent · 12,34 %' },
{ id: 'date', label: 'Datum · 02.09.2026' },
{ id: 'dateiso', label: 'Datum (ISO) · 2026-09-02' },
{ id: 'time', label: 'Zeit · 14:30' },
{ id: 'datetime', label: 'Datum + Zeit · 02.09.2026 14:30' },
];
@@ -63,13 +64,18 @@ function nf(locale: NumberLocale, opts: Intl.NumberFormatOptions): Intl.NumberFo
const pad2 = (n: number) => String(n).padStart(2, '0');
/** `'plain'` renders a fixed, locale-neutral ISO-ish shape by hand -- NOT
* via `Intl` with an `undefined` locale, which would silently fall back to
* the *browser's* locale instead of staying neutral. */
/** `YYYY-MM-DD`, hand-written rather than via `Intl` -- there's no locale
* that reliably reproduces ISO 8601, and this is also what the `'plain'`
* pseudo-locale uses for a date (an `Intl` call with an `undefined` locale
* would silently fall back to the *browser's* locale instead of staying
* neutral). Shared by the locale-independent `'dateiso'` cell format and
* by `formatDatePart`'s `'plain'` branch below. */
function isoDateString(d: Date): string {
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
}
function formatDatePart(d: Date, locale: NumberLocale): string {
if (locale === 'plain') {
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
}
if (locale === 'plain') return isoDateString(d);
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: '2-digit',
@@ -117,6 +123,8 @@ export function formatValue(
}).format(n);
case 'date':
return formatDatePart(serialToUtcDate(n), locale);
case 'dateiso':
return isoDateString(serialToUtcDate(n)); // same regardless of locale
case 'time':
return formatTimePart(serialToUtcDate(n), locale);
case 'datetime':