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:
@@ -13,7 +13,7 @@ Vorbild von **Apple Numbers**. Zwei Dinge stehen im Mittelpunkt:
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Vite-Devserver
|
||||
npm run test # Vitest-Suite (Engine + Store + IO), 265 Tests
|
||||
npm run test # Vitest-Suite (Engine + Store + IO), 297 Tests
|
||||
npm run build # Typecheck + Produktionsbuild
|
||||
npm run lint # ESLint (flat config)
|
||||
```
|
||||
@@ -263,7 +263,8 @@ Auswahl (Zelle, Bereich, Mehrfachauswahl, ganze Zeile/Spalte über den Kopf) →
|
||||
Inspector unter **Objekt · Auswahl**: horizontale **Ausrichtung**
|
||||
(links/mitte/rechts),
|
||||
**Zahlenformat** (`Automatisch`, `Ohne`, Ganzzahl, 1/2 Nachkommastellen,
|
||||
**Prozent** 0/2 Stellen — kombiniert mit der Locale unten), **Zell-Textfarbe**,
|
||||
**Prozent** 0/2 Stellen, **Datum**/**Zeit**/**Datum + Zeit** — kombiniert mit
|
||||
der Locale unten), **Zell-Textfarbe**,
|
||||
**Zell-Füllfarbe**, **Fett**/**Kursiv**, „Formatierung entfernen". Auch exakte
|
||||
**Spaltenbreite** und **Zeilenhöhe** der Fokuszelle.
|
||||
|
||||
@@ -399,7 +400,8 @@ darauf, `$` hin oder her. `F4` schaltet den Bezug unter dem Cursor durch die
|
||||
vier Kombinationen.
|
||||
|
||||
**Funktionen** (case-insensitiv, Trenner `;`): `SUM`, `AVG` (Alias `AVERAGE`),
|
||||
`MIN`, `MAX`, `COUNT`, `IF(Bedingung; Dann; Sonst?)` — plus alle über
|
||||
`MIN`, `MAX`, `COUNT`, `IF(Bedingung; Dann; Sonst?)`, `DATE(Jahr; Monat;
|
||||
Tag)`, `TIME(Std; Min; Sek)`, `TODAY()`, `NOW()` — plus alle über
|
||||
**„𝑓 Funktionen"** angelegten benutzerdefinierten Funktionen (siehe
|
||||
„Benutzerdefinierte Funktionen" oben), die sich für den Aufrufer identisch
|
||||
verhalten. Argumente gemischt: Einzelzellen, Bereiche (lokal oder `Name!…`),
|
||||
@@ -416,6 +418,33 @@ Zahl außer `0` sowie jeder nicht-leere Text gilt als wahr, z. B.
|
||||
Bedingung `0`. Ein benutzerdefiniertes „IF" ist nicht anlegbar — der Name
|
||||
kollidiert mit der eingebauten Funktion.
|
||||
|
||||
**Datum & Zeit**: ein Datum ist — wie in jeder Tabellenkalkulation — intern
|
||||
einfach eine **Zahl** (Tage seit dem 30.12.1899, Uhrzeit als Bruchteil eines
|
||||
Tages), keine eigene Zellart. Deshalb funktioniert Datums-Arithmetik ganz
|
||||
ohne Zusatzaufwand: `=fällig - rechnungsdatum` zählt Tage, `=rechnungsdatum
|
||||
+ 30` addiert 30 Tage. Zwei Wege zu einem Datumswert:
|
||||
- **Eingabe erkennen**: ein eindeutig lesbares Datum/eine Uhrzeit als reine
|
||||
Zelleingabe (nicht als Formel) wird beim Eintippen automatisch erkannt
|
||||
und sofort mit dem passenden Format angezeigt — genau wie in Excel/Numbers.
|
||||
Erkannt werden ISO (`2026-09-02`, auch mit `14:30`/`14:30:15` angehängt)
|
||||
und Punkt-Daten (`02.09.2026`, `02.09.26`, ebenfalls mit Uhrzeit), sowie
|
||||
reine Uhrzeiten (`14:30`). **Absichtlich nicht** erkannt: Schrägstrich-Daten
|
||||
(`02/09/2026`) — Tag-zuerst vs. Monat-zuerst ist über die unterstützten
|
||||
Locales (en-US ist monatzuerst, alle anderen tagzuerst) nicht eindeutig
|
||||
auflösbar; falsches Raten würde das Datum lautlos verfälschen, deshalb
|
||||
bleibt so ein Text unangetastet. Die automatische Formatierung überschreibt
|
||||
nie ein bereits explizit gesetztes Zellformat.
|
||||
- **Formel**: `DATE(Jahr; Monat; Tag)` und `TIME(Std; Min; Sek)` bauen einen
|
||||
Datums-/Zeitwert (Überlauf wie in Excel erlaubt: `DATE(2026;13;1)` =
|
||||
01.01.2027, `TIME(25;0;0)` = 01:00 des Folgetags-Anteils); `TODAY()` /
|
||||
`NOW()` liefern das aktuelle Datum bzw. Datum+Uhrzeit. Formel-Ergebnisse
|
||||
bekommen **kein** automatisches Zellformat (wie bei jeder anderen Formel
|
||||
auch) — das Zahlenformat-Dropdown auf „Datum"/„Zeit"/„Datum + Zeit" stellen.
|
||||
`TODAY`/`NOW` sind **nicht volatil**: diese Engine rechnet rein
|
||||
vorgänger-getrieben neu, ohne Timer — eine Zelle mit `=TODAY()`
|
||||
aktualisiert sich erst bei ihrer eigenen nächsten Neuberechnung, nicht von
|
||||
selbst beim Tageswechsel (siehe „Bekannte Grenzen" unten).
|
||||
|
||||
**Fehlermodell** (propagiert an abhängige Zellen):
|
||||
`#REF` (ungültiger/gelöschter Bezug, unbekannter Tabellen-/Blattname, oder ein
|
||||
unqualifizierter Tabellenname, der auf mehreren Blättern existiert und auch
|
||||
@@ -532,7 +561,7 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
|
||||
## Tests
|
||||
|
||||
`npm run test` — Vitest, 265 Tests:
|
||||
`npm run test` — Vitest, 297 Tests:
|
||||
|
||||
- **lexer / parser** — Präzedenz & Assoziativität (`^` vor `*/` vor `+-`, `^`
|
||||
rechts, `-2^2 = -(2^2)`), lokale/qualifizierte/quoted Bezüge, `!` **und** `::`,
|
||||
@@ -547,6 +576,15 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
Cross-Table, Case-/`::`-Toleranz, jeder Fehlerfall, Fehler-Propagierung,
|
||||
`IF` (beide Zweige, 2-/3-Argument-Form, Kurzschluss-Beweis, Vergleiche als
|
||||
Bedingung, Fehler in der Bedingung).
|
||||
- **Datum & Zeit** — `dates.ts`: `dateToSerial`/`timeSerial`-Rundtrip gegen
|
||||
bekannte Werte, Überlauf-Verhalten, `parseDateTimeLiteral` für jede
|
||||
erkannte Form (ISO/Punkt, mit/ohne Uhrzeit) sowie Ablehnung von
|
||||
Schrägstrich-Daten und ungültigen/rollierten Daten; `literalValue` wandelt
|
||||
eine erkannte Eingabe in ihren Serial um; `DATE`/`TIME`/`TODAY`/`NOW` als
|
||||
Formel (Basisfälle, Arity-/Typ-Fehler, Datums-Arithmetik); `formatValue`
|
||||
über mehrere Locales inkl. `plain`; Store: Auto-Format beim Eintippen
|
||||
(überschreibt nie ein bereits gesetztes Format), Namenskollision mit den
|
||||
neuen eingebauten Funktionen.
|
||||
- **graph** — `affected`, topologische Ordnung, Selbst-/2er-/längerer Zyklus.
|
||||
- **workbook / store** — Seed-Aggregate, Cross-Table-Summen, Edit-Ripple durch
|
||||
die ganze Kette, `#REF` nach Tabellen-/Blatt-Löschung, neues Tabellen-Objekt
|
||||
@@ -683,9 +721,16 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
reaktiv), **kein** echter Web Worker.
|
||||
- **xlsx-Formel-Übernahme** ist heuristisch (Separator-Swap unter Beachtung von
|
||||
Quotes); exotische Excel-Formeln fallen auf den Wert zurück.
|
||||
- Kein Text-Verkettungs-/Vergleichsoperator, keine benannten Bereiche (nur
|
||||
- Kein Text-Verkettungsoperator, keine benannten Bereiche (nur
|
||||
benutzerdefinierte *Funktionen*, keine benannten *Zellen/Bereiche*), kein
|
||||
Snapping der Canvas-Objekte.
|
||||
- **Datum & Zeit**: die automatische Erkennung beim Eintippen erfasst nur ISO-
|
||||
und Punkt-Daten, absichtlich **nicht** Schrägstrich-Daten (Tag-zuerst vs.
|
||||
Monat-zuerst ist über die unterstützten Locales nicht eindeutig auflösbar,
|
||||
s. o.). Formel-Ergebnisse (`=DATE(...)`, `=TODAY()`, `=A1+30`) bekommen kein
|
||||
automatisches Zellformat — wie jedes andere Formelergebnis auch. `TODAY()`/
|
||||
`NOW()` sind nicht volatil: sie aktualisieren sich erst bei der nächsten
|
||||
Neuberechnung ihrer eigenen Zelle, nicht spontan mit der Zeit.
|
||||
- **Benutzerdefinierte Funktionen** (Phase 1, siehe oben) bewusst ohne:
|
||||
Autovervollständigen des Funktionsnamens im Editor, Live-Vorschau des
|
||||
Ergebnisses im Bearbeiten-Dialog, variadische Parameter/Default-Werte, und
|
||||
|
||||
Reference in New Issue
Block a user