Datum- & Zeit-Formatierung: Serial-Zahlen, Auto-Erkennung, DATE/TIME/TODAY/NOW
Wie in jeder Tabellenkalkulation ist ein Datum intern einfach eine Zahl
("Serial": Tage seit 1899-12-30, Uhrzeit als Bruchteil eines Tages) --
Datums-Arithmetik (=fällig - rechnungsdatum, =rechnungsdatum + 30)
funktioniert dadurch automatisch, ganz ohne Änderung an Operatoren.
- Neues Modul engine/dates.ts: dateToSerial/timeSerial (Bausteine für
DATE()/TIME(), Überlauf wie Excel erlaubt), todaySerial/nowSerial
(lokales Kalenderdatum), serialToUtcDate (Anzeige), parseDateTimeLiteral
(Eingabe-Erkennung -- bewusst nur ISO/Punkt-Daten + Uhrzeit, KEINE
Schrägstrich-Daten, da Tag-/Monat-zuerst über die unterstützten Locales
nicht eindeutig auflösbar ist; ein zu einer ungültigen/rollierten
Kalenderdatum führender Kandidat wird strikt verworfen statt lautlos
zu überlaufen).
- workbook.ts: literalValue() erkennt Datum/Zeit vor dem Zahl-Test und
liefert direkt den Serial.
- functions.ts: neue eingebaute Funktionen DATE(jahr;monat;tag),
TIME(std;min;sek), TODAY(), NOW() -- TODAY/NOW sind bewusst nicht
volatil (dokumentierte Grenze: diese Engine rechnet rein
vorgänger-getrieben neu, ohne Timer).
- store/types.ts + format.ts: CellNumFmt um 'date'/'time'/'datetime'
erweitert; formatValue rendert über Intl.DateTimeFormat (UTC-Zeitzone,
pro Locale), 'plain' bekommt eine manuelle, locale-neutrale
Formatierung statt der Browser-Locale.
- store/document.ts: setCellInput erkennt eine getippte Datums-/
Zeit-Eingabe automatisch und setzt das passende Zellformat -- wie
Excel/Numbers --, überschreibt aber nie ein bereits explizit gesetztes
Format.
- Zahlenformat-Dropdown (SettingsPanel) und JSON-Validierung
(isCellNumFmt) brauchten keine Änderung -- beide sind bereits generisch
über NUM_FORMATS.
Tests: 32 neue (dates.spec.ts neu; workbook.spec.ts: literalValue +
DATE/TIME/TODAY/NOW-Formeln inkl. Datums-Arithmetik; format.spec.ts neu:
formatValue über mehrere Locales; document.spec.ts: Auto-Format beim
Eintippen, überschreibt nie ein gesetztes Format, Namenskollision mit
den neuen Funktionen). 265 -> 297 Tests, alle grün. vue-tsc/eslint/build
sauber. Live per Playwright verifiziert (Auto-Erkennung, Formel-Ergebnisse
ohne Auto-Format, Datums-Arithmetik, Schrägstrich-Daten bleiben Text).
READMEs aktualisiert (Funktionen, Zahlenformat, neuer "Datum & Zeit"-
Abschnitt in engine/README.md, Bekannte Grenzen, Testzahl; nebenbei eine
veraltete Aussage zu Vergleichsoperatoren in "Bekannte Grenzen" korrigiert
-- die gibt es seit der IF-Funktion bereits).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import { FormulaError, isError, type CellValue } from './types';
|
||||
import { dateToSerial, timeSerial, todaySerial, nowSerial, strictNum } from './dates';
|
||||
|
||||
export type FunctionFn = (values: CellValue[]) => CellValue;
|
||||
|
||||
@@ -70,6 +71,50 @@ export const FUNCTIONS: Record<string, FunctionFn> = {
|
||||
if (truthy) return thenValue;
|
||||
return values.length === 3 ? elseValue : 0;
|
||||
},
|
||||
|
||||
// Dates/times are plain numbers here (see dates.ts) -- DATE/TIME build one,
|
||||
// TODAY/NOW read the current one. `null` (empty cell) arguments count as 0,
|
||||
// same as everywhere else in this engine; a non-numeric text argument is
|
||||
// `#VALUE!`.
|
||||
DATE(values) {
|
||||
if (values.length !== 3) {
|
||||
return new FormulaError('#ERR', 'DATE expects 3 arguments (Jahr; Monat; Tag)');
|
||||
}
|
||||
const y = strictNum(values[0]);
|
||||
if (isError(y)) return y;
|
||||
const mo = strictNum(values[1]);
|
||||
if (isError(mo)) return mo;
|
||||
const d = strictNum(values[2]);
|
||||
if (isError(d)) return d;
|
||||
return dateToSerial(y, mo, d);
|
||||
},
|
||||
|
||||
TIME(values) {
|
||||
if (values.length !== 3) {
|
||||
return new FormulaError('#ERR', 'TIME expects 3 arguments (Stunde; Minute; Sekunde)');
|
||||
}
|
||||
const h = strictNum(values[0]);
|
||||
if (isError(h)) return h;
|
||||
const mi = strictNum(values[1]);
|
||||
if (isError(mi)) return mi;
|
||||
const s = strictNum(values[2]);
|
||||
if (isError(s)) return s;
|
||||
return timeSerial(h, mi, s);
|
||||
},
|
||||
|
||||
// Not "volatile": this engine only recalculates on precedent change, so a
|
||||
// cell holding =TODAY()/=NOW() refreshes when THAT cell is next re-edited
|
||||
// or recalculated, not spontaneously when the clock ticks over (documented
|
||||
// limitation, see README.md's "Bekannte Grenzen").
|
||||
TODAY(values) {
|
||||
if (values.length !== 0) return new FormulaError('#ERR', 'TODAY erwartet keine Argumente');
|
||||
return todaySerial();
|
||||
},
|
||||
|
||||
NOW(values) {
|
||||
if (values.length !== 0) return new FormulaError('#ERR', 'NOW erwartet keine Argumente');
|
||||
return nowSerial();
|
||||
},
|
||||
};
|
||||
|
||||
// AVERAGE is an alias of AVG.
|
||||
|
||||
Reference in New Issue
Block a user