Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A

Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben).
Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature.

Vollstaendigkeits-Inventar
- docs/inventar.md mit 285 Eintraegen aus den Themenlisten von
  bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle
- crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide
  Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten

Bruchschritt (Puffer, Host, Korpus)
- Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui
  behaelt Farbabbildung, Widget und den neuen Terminal-Host
- Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus
  Ereignisse (Taste, Groessenaenderung, Abbruch)
- Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht
  bestehenden Sollausgaben blieben dabei unveraendert

Groessenunabhaengigkeit
- 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend
  der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor
  und VIEW PRINT geklemmt)
- Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener
  Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf

Bibliothek
- Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch)
- Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS
- Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON)
- PRINT USING, LPRINT USING, FORMAT$, SetFormatCC
- Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge)
- Datum und Zeit mit Serienwerten ab 1899
- Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#,
  IRR#, MIRR#, SLN#, SYD#, DDB#
- Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen),
  BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV
- System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung

Altlasten aus Phase 2
- ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar
  (prozeduruebergreifender Fixup im Codegenerator)
- DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum)
- Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest

Vom Inventar aufgedeckte Fehler
- Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht
  ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die
  String*-Routinen, LINE und OPEN "COMn:") -- geschlossen
- Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik,
  Record-Konvertierung, Rest der Dateisystemfunktionen
- bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar
  bildet die Vereinigung mit qb45advr.hlp

Neue Changes
- phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb)
- phase-3-ortszeit: zieht die UTC-Abweichung zurueck

Neue Abhaengigkeit: unicode-width.
Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei,
docs/bibliothek.md neu, PLAN.md fortgeschrieben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 06:54:19 +02:00
parent a6f005d023
commit 52ccbb5848
94 changed files with 10421 additions and 792 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-04

View File

@@ -0,0 +1,180 @@
## Context
Siehe proposal.md — Why. Der Ist-Zustand ist eng umrissen:
- `tb-runtime::datetime` rechnet vollständig selbst: Kalenderumrechnung
(Howard Hinnant), serielle Datumswerte ab dem 30.12.1899, `WEEKDAY`,
Textformen. Diese Rechnung ist getestet und bleibt.
- Der einzige Punkt, an dem die Zeitzone eine Rolle spielt, ist
`jetzt_sekunden()` — Sekunden seit der Unix-Epoche. Alles Weitere
(`jetzt_serial`, `DATE$`, `TIME$`, `TIMER`, `NOW`) baut darauf auf.
- `tb-runtime` hat bislang eine einzige Abhängigkeit mit fachlichem
Gewicht: `unicode-width`.
Damit ist die Aufgabe klein: **eine Zahl** — der UTC-Versatz zu einem
Zeitpunkt — kommt künftig von außen.
## Goals / Non-Goals
**Goals:**
- Zeitfunktionen liefern das, was die Uhr des Rechners zeigt.
- Sommerzeit wird zum jeweiligen Zeitpunkt berücksichtigt.
- Die Berührungsfläche zur Bibliothek bleibt eine Funktion, damit sie
austauschbar ist und die getestete Kalenderrechnung unberührt bleibt.
- Zeitverhalten bleibt automatisiert prüfbar, ohne von der Zone des
Testrechners abzuhängen.
**Non-Goals:**
- Zeitzonenbehaftete Datentypen im Dialekt — das Vorbild kennt nur
Wanduhrzeit.
- Eine Sprachanbindung an die Bibliothek (Zonennamen, Umrechnung zwischen
Zonen). Das ist der Stufe-2-Eintrag im PLAN, nicht dieser Change.
- Historische Zonendaten jenseits dessen, was die Systemdatenbank
hergibt.
## Decisions
### D1: `jiff` als Zeitzonenbibliothek
*Gewählt:* `jiff` — sie liest die Zeitzonendatenbank des Systems und
liefert den Versatz zu einem Zeitpunkt ohne Einschränkung.
*Alternativen:*
| Bibliothek | Bewertung |
|---|---|
| `jiff` | Für genau diese Frage gebaut; Systemzonendatenbank; keine Sicherheitsauflage. **Gewählt.** |
| `chrono` (≥ 0.4.20) | Gleichwertig sicher, seit 0.4.20 ohne `localtime_r` (Zonenermittlung über `iana-time-zone`, Datenbank über einen `tz-rs`-Zweig). Etablierter, bringt aber Serde-/Ökosystem-Anbindung mit, die hier niemand braucht. Tragfähige zweite Wahl. |
| `time` | Der lokale Versatz ist auf Unix nur nach ausdrücklichem Opt-in in möglicherweise undefiniertes Verhalten verfügbar (`set_soundness`). **Verworfen** — das ist kein Preis für eine Uhrzeit. |
| Eigenbau (`iana-time-zone` + TZif-Parser) | Hieße, die Zonendatenbank selbst zu lesen und dauerhaft nachzupflegen. Genau das, wofür man eine Bibliothek nimmt. **Verworfen.** |
*Warum die Wahl leicht revidierbar bleibt:* Die Bibliothek wird an genau
einer Stelle aufgerufen (D2). Ein Wechsel zu `chrono` wäre ein Austausch
dieser einen Funktion.
### D2: Berührungsfläche ist eine Funktion
```
tb-runtime::datetime
utc_versatz(zeitpunkt: i64) -> Option<i32> // Sekunden, None = Zone unbekannt
└── einziger Aufruf der Bibliothek
jetzt_sekunden() ─┐
jetzt_serial() ├── rechnen weiter selbst
civil_from_days() │
weekday() … ─┘
```
Die Kalenderrechnung sieht die Bibliothek nie. Sie bekommt nach wie vor
Sekunden seit der Epoche — nur eben um den Versatz verschobene.
*Warum nicht die Bibliothek durchreichen:* Ihre Datums-/Zeittypen wären
ein zweites Kalendermodell neben dem des Dialekts (der bei 1899 zählt,
`WEEKDAY` = 1 für Sonntag, CURRENCY-nahe Serienwerte). Zwei Modelle
nebeneinander wären eine dauerhafte Fehlerquelle; die vorhandene Rechnung
ist getestet und bleibt maßgeblich.
### D3: Der Versatz gilt je Zeitpunkt, nicht je Programmlauf
`utc_versatz` bekommt den Zeitpunkt als Argument. Ein Programm, das über
eine Zeitumstellung hinweg läuft, folgt ihr; ein Programm, das mit
`DATE$ =` in einen anderen Teil des Jahres springt, bekommt den dort
gültigen Versatz.
*Preis:* Eine Zonenabfrage je Zeitabfrage. Bei den Aufrufmengen eines
Terminalprogramms belanglos; die Bibliothek hält die Zonendaten selbst
vor.
### D4: Zonenquelle ist umschaltbar — damit Tests nicht von der Maschine abhängen
```rust
pub enum Zeitzone {
/// Zone des Rechners (Vorgabe).
System,
/// Fester Versatz in Sekunden — für Tests und den Korpus.
Fest(i32),
/// Zone nicht ermittelbar; es gilt UTC.
Unbekannt,
}
```
`RtState` hält eine `Zeitzone`. Die Vorgabe ist `System`; schlägt die
Ermittlung fehl, wird daraus `Unbekannt`. Tests und Korpusprogramme
setzen `Fest(n)`.
*Warum nicht `TZ` in den Tests setzen:* Umgebungsvariablen sind
prozessweit, Tests laufen nebenläufig, und `set_var` ist seit Rust 2024
`unsafe`. Ein Zustand im `RtState` ist der offensichtliche Weg — er ist
genau so weit sichtbar wie nötig.
*Korpus:* Das Harness bekommt eine Kopfzeilen-Direktive analog zu
`' tb-screen:` und `' tb-tempdir`:
```text
' tb-tz: +02:00
```
Damit bleibt die Sollausgabe eines Zeitprogramms unabhängig davon, wo der
Testrechner steht.
### D5: Rückfallebene ist sichtbar, nicht still
Ohne ermittelbare Zone gilt UTC — ein Abbruch wäre unverhältnismäßig, ein
stiller Wechsel aber ein Verstoß gegen den Guiding Principle. Die Laufzeit
führt den Zustand mit, und der Zustand ist abfragbar; die Sprachreferenz
beschreibt ihn.
*Wie ein Programm es erfährt:* Über die vorhandene Systemabfrage
`ENVIRON$` wäre es geraten. Stattdessen bekommt die Laufzeit einen
benannten Zustand, den der Runner beim Start auf die Fehlerausgabe
schreibt, wenn er `Unbekannt` ist — einmal, nicht je Abfrage. Ein
Programm, das es programmatisch braucht, bekommt es in Stufe 2 über die
Bibliotheksanbindung.
### D6: Reihenfolge der Umsetzung
```
1. Abhaengigkeit + utc_versatz() -> isoliert testbar
2. jetzt_sekunden/jetzt_serial umstellen -> DATE$/TIME$/TIMER/NOW folgen
3. Zeitzone im RtState + Korpusdirektive -> Tests werden maschinenunabhaengig
4. Doku: Abweichung zurueckziehen, PLAN Stufe 2
```
## Risks / Trade-offs
- **Erste schwere Abhängigkeit der Laufzeit** → bewusst entschieden
(2026-09-04). Die Berührungsfläche ist eine Funktion (D2); ein Wechsel
der Bibliothek bleibt ein lokaler Eingriff.
- **Zeitabhängige Tests werden flatterig** → deshalb D4: kein Test und
kein Korpusprogramm fragt die echte Zone ab. Was die Systemzone
betrifft, wird über `Fest(n)` und `Unbekannt` geprüft.
- **Zeitumstellung erzeugt mehrdeutige Wanduhrzeiten** (Rückstellung) und
nicht existierende (Vorstellung) → betrifft nur die Richtung
Wanduhr → Zeitpunkt. Die Zeitfunktionen dieses Dialekts gehen die
andere Richtung (Zeitpunkt → Wanduhr) und sind davon nicht betroffen.
`DATEVALUE`/`TIMEVALUE` lesen Wanduhrzeit und rechnen gar nicht um.
`DATE$ =`/`TIME$ =` setzen einen Versatz gegen die aktuelle Wanduhrzeit
— auch das ist eindeutig.
- **Die Systemzonendatenbank kann fehlen** (schlanke Container) → D5.
- **Bauzeit und Binärgröße wachsen** → für ein Werkzeug, dessen Programme
Uhrzeiten anzeigen, ist eine falsche Uhrzeit der größere Preis.
## Migration Plan
Rein verhaltensändernd, ohne Formatbruch: `.tbc`-Dateien, Korpus-Golden-
Files und Schnittstellen bleiben unberührt. Das einzige beobachtbare
Verhalten, das sich ändert, ist der Wert von `DATE$`, `TIME$`, `TIMER`
und `NOW` — und zwar zum Richtigen hin.
Rücknahme: Ein Zurücksetzen auf UTC wäre `Zeitzone::Unbekannt` als
Vorgabe; die Abhängigkeit bliebe ungenutzt.
## Open Questions
- Ob die Bibliotheksanbindung in Stufe 2 (Zonennamen, Umrechnung zwischen
Zonen als BASIC-Funktionen) auf derselben Bibliothek aufsetzt oder eine
eigene Wahl trifft, entscheidet Stufe 2. Für diesen Change ohne
Wirkung — hier wird nur der Versatz gebraucht.

View File

@@ -0,0 +1,76 @@
# Ortszeit statt UTC
## Why
`DATE$`, `TIME$`, `TIMER` und `NOW` liefern heute koordinierte Weltzeit.
Das ist keine Rekonstruktion des Vorbilds, sondern eine Notlösung: die
Standardbibliothek von Rust kennt keine Zeitzonen, und in
`phase-3-runtime-bildschirm` sollte die erste schwere Abhängigkeit der
Laufzeit nicht nebenbei entstehen. Die Abweichung steht in
docs/sprachreferenz.md.
Für ein BASIC zum Bauen von Terminal-Werkzeugen ist das ein echter
Mangel: Ein Programm, das eine Uhr anzeigt, ein Protokoll schreibt oder
einen Termin prüft, zeigt schlicht die falsche Zeit — je nach Zone um
Stunden daneben. Das Vorbild lief auf der Ortszeit des Rechners; jedes
übernommene Programm rechnet damit.
Der Projektinhaber hat am 2026-09-04 entschieden, dafür eine Bibliothek
in die Laufzeit aufzunehmen. Dieser Change zieht die Abweichung zurück.
## What Changes
- **Zeitfunktionen liefern Ortszeit**: `DATE$`, `TIME$`, `TIMER` und
`NOW` rechnen in der Zeitzone des Rechners statt in UTC. `TIMER` zählt
Sekunden seit der **lokalen** Mitternacht.
- **Sommerzeit wird berücksichtigt**: Der Versatz wird für den jeweiligen
Zeitpunkt bestimmt, nicht einmalig beim Programmstart — ein Programm,
das über einen Zeitumstellungspunkt hinweg läuft, folgt ihm.
- **Neue Abhängigkeit in `tb-runtime`**: eine Zeitzonenbibliothek, deren
Berührungsfläche bewusst auf eine einzige Frage beschränkt bleibt —
„welcher UTC-Versatz gilt zu diesem Zeitpunkt?". Die Kalenderrechnung
(serielle Datumswerte, `WEEKDAY`, Zerlegung) bleibt die vorhandene,
getestete Eigenimplementierung in `tb-runtime::datetime`.
- **Rückfallebene**: Lässt sich die Zeitzone des Rechners nicht
ermitteln, gilt UTC. Das Programm läuft weiter; die Rückfallebene ist
über eine Funktion der Laufzeit erkennbar und dokumentiert.
- **`DATE$ =` / `TIME$ =` bleiben ein Programmversatz** (sie stellen
nicht die Systemuhr) — sie wirken künftig auf die Ortszeit.
- **Abweichung zurückziehen**: Der Eintrag „Uhr läuft in UTC" entfällt
aus docs/sprachreferenz.md; an seine Stelle tritt die Beschreibung der
Zonenermittlung samt Rückfallebene.
- **Stufe 2 im PLAN**: Die Zeitzonenbibliothek wird in den Ideenspeicher
„Runtime-Bibliotheken als BASIC-Bibliotheken" aufgenommen — neben
crossterm —, damit BASIC-Programme später direkt auf Zonen zugreifen
können (Zonennamen, Versatz zu einem Zeitpunkt, Umrechnung zwischen
Zonen).
**Non-Goals:** Zeitzonenbehaftete Datentypen im Dialekt (das Vorbild
kennt nur lokale Wanduhrzeit); eine Sprachanbindung an die Bibliothek
(das ist der Stufe-2-Eintrag, nicht dieser Change); Änderungen an der
Zählweise serieller Datumswerte oder an `DATEVALUE`/`TIMEVALUE`, die
Wanduhrzeit lesen und daher keine Umrechnung brauchen.
## Capabilities
### Modified Capabilities
- `konsolen-basisbibliothek`: Die Datums- und Zeitfunktionen SHALL die
Ortszeit des Rechners liefern statt UTC, einschließlich Sommerzeit und
einer dokumentierten Rückfallebene.
## Impact
- `crates/tb-runtime/src/datetime.rs`: Versatzermittlung über die neue
Bibliothek; `jetzt_serial`/`jetzt_sekunden` liefern Ortszeit. Die
Kalenderrechnung bleibt unverändert.
- `crates/tb-runtime/src/builtins.rs`: `DATE$`, `TIME$`, `TIMER`, `NOW`
rechnen über die neue Funktion.
- `Cargo.toml` (Workspace) und `crates/tb-runtime/Cargo.toml`: neue
Abhängigkeit.
- `tests/compat`: Korpusprogramm für die Zeitfunktionen, das ohne
Abhängigkeit von der Zone des Testrechners prüfbar bleibt.
- `docs/sprachreferenz.md`: Abweichung „Uhr läuft in UTC" entfällt;
Zonenermittlung und Rückfallebene beschrieben.
- `docs/bibliothek.md`: Abschnitt Datum/Zeit fortgeschrieben.
- `PLAN.md`: Stufe-2-Eintrag um die Bibliothek erweitert.

View File

@@ -0,0 +1,58 @@
## ADDED Requirements
### Requirement: Zeitfunktionen liefern Ortszeit
`DATE$`, `TIME$`, `TIMER` und `NOW` SHALL den Zeitpunkt in der Zeitzone
des Rechners liefern, nicht in koordinierter Weltzeit. `TIMER` SHALL die
Sekunden seit der **lokalen** Mitternacht zählen. Der maßgebliche Versatz
SHALL für den jeweils abgefragten Zeitpunkt bestimmt werden, nicht einmal
beim Programmstart — ein Programm, das über eine Zeitumstellung hinweg
läuft, folgt ihr. Die Zerlegungsfunktionen (`DAY`, `MONTH`, `YEAR`,
`WEEKDAY`, `HOUR`, `MINUTE`, `SECOND`) arbeiten unverändert auf seriellen
Werten und rechnen selbst nicht um.
#### Scenario: Datum und Uhrzeit stimmen mit der Systemuhr überein
- **WHEN** ein Programm `DATE$` und `TIME$` abfragt
- **THEN** entsprechen sie der Ortszeit des Rechners zum Abfragezeitpunkt
#### Scenario: TIMER zählt ab lokaler Mitternacht
- **WHEN** `TIMER` abgefragt wird
- **THEN** entspricht der Wert den Sekunden seit der letzten lokalen Mitternacht, nicht seit der UTC-Mitternacht
#### Scenario: NOW und die Zerlegungsfunktionen passen zusammen
- **WHEN** `n# = NOW` gebildet und `HOUR(n#)` ausgewertet wird
- **THEN** liefert `HOUR(n#)` dieselbe Stunde, die auch `TIME$` nennt
#### Scenario: Sommerzeit wirkt zum jeweiligen Zeitpunkt
- **WHEN** in einer Zone mit Sommerzeit ein Zeitpunkt vor und einer nach der Umstellung abgefragt wird
- **THEN** unterscheiden sich die beiden Versätze um die Umstellungsspanne
### Requirement: Zonenermittlung mit Rückfallebene
Die Laufzeit SHALL die Zeitzone des Rechners über die
Betriebssystemeinstellung bestimmen. Lässt sie sich nicht ermitteln,
SHALL UTC gelten und das Programm ohne Fehler weiterlaufen. Ob die
Rückfallebene greift, MUST für ein Programm erkennbar sein und in
docs/sprachreferenz.md beschrieben stehen; ein stiller Wechsel auf UTC
MUST NOT vorkommen.
#### Scenario: Zone nicht ermittelbar
- **WHEN** die Zeitzone des Rechners nicht bestimmt werden kann
- **THEN** rechnen die Zeitfunktionen in UTC und das Programm läuft ohne Fehler weiter
#### Scenario: Rückfallebene ist erkennbar
- **WHEN** ein Programm den Zonenzustand abfragt
- **THEN** unterscheidet die Antwort erkennbar zwischen ermittelter Zone und Rückfall auf UTC
### Requirement: Programmversatz wirkt auf die Ortszeit
`DATE$ = text$` und `TIME$ = text$` SHALL weiterhin nicht die Systemuhr
stellen, sondern einen programmeigenen Versatz setzen. Dieser Versatz
SHALL auf die **Ortszeit** wirken: nach `TIME$ = "10:20:30"` nennt
`TIME$` diese Uhrzeit, und `NOW`, `HOUR` und `TIMER` stimmen damit
überein.
#### Scenario: Gesetzte Uhrzeit wird zurückgelesen
- **WHEN** `TIME$ = "10:20:30"` ausgeführt und danach `TIME$` abgefragt wird
- **THEN** nennt `TIME$` `10:20:30`
#### Scenario: Versatz wirkt auf alle Zeitfunktionen
- **WHEN** `DATE$ = "01-02-1993"` gesetzt und danach `YEAR(NOW)` ausgewertet wird
- **THEN** liefert `YEAR(NOW)` 1993

View File

@@ -0,0 +1,35 @@
## 1. Versatzermittlung
- [ ] 1.1 `jiff` als Workspace-Abhängigkeit aufnehmen und in `tb-runtime` einbinden; verifiziert dadurch, dass `cargo build -p tb-runtime` durchläuft und `cargo tree -p tb-runtime` die Bibliothek genau einmal zeigt
- [ ] 1.2 `datetime::utc_versatz(zeitpunkt: i64) -> Option<i32>` als einzigen Aufrufpunkt der Bibliothek schreiben (Sekunden Versatz, `None` = Zone nicht ermittelbar); verifiziert durch Unit-Test, dass der Versatz für einen festen Zeitpunkt im gültigen Bereich 50400…50400 liegt
- [ ] 1.3 Sommerzeit prüfen: für dieselbe Zone einen Zeitpunkt im Januar und einen im Juli abfragen; verifiziert durch Unit-Test, dass sich die Versätze in einer Zone mit Sommerzeit unterscheiden und in einer ohne gleich bleiben
- [ ] 1.4 Sicherstellen, dass die Kalenderrechnung unberührt bleibt; verifiziert dadurch, dass `datetime.rs` außerhalb von `utc_versatz` keinen Bibliotheksaufruf enthält und die bestehenden `datetime`-Tests unverändert grün sind
## 2. Zonenzustand in der Laufzeit
- [ ] 2.1 `Zeitzone { System, Fest(i32), Unbekannt }` einführen und in `RtState` halten; Vorgabe `System`, bei fehlgeschlagener Ermittlung `Unbekannt`; verifiziert durch Unit-Test je Variante
- [ ] 2.2 `jetzt_sekunden`/`jetzt_serial` auf den Zonenzustand umstellen; verifiziert durch Unit-Test, dass `Fest(3600)` genau eine Stunde gegenüber `Unbekannt` verschiebt
- [ ] 2.3 `DATE$`, `TIME$`, `TIMER` und `NOW` über den Zonenzustand rechnen; verifiziert durch Test, dass `HOUR(NOW)` und die Stunde aus `TIME$` bei `Fest(n)` übereinstimmen
- [ ] 2.4 `TIMER` zählt ab lokaler Mitternacht; verifiziert durch Test, dass sich `TIMER` bei `Fest(3600)` gegenüber `Unbekannt` um 3600 verschiebt (modulo Tageslänge)
- [ ] 2.5 `DATE$ =` / `TIME$ =` wirken auf die Ortszeit; verifiziert durch Test, dass `TIME$` nach `TIME$ = "10:20:30"` genau diesen Wert nennt und `HOUR(NOW)` 10 liefert
## 3. Rückfallebene
- [ ] 3.1 Bei nicht ermittelbarer Zone auf UTC zurückfallen, ohne Fehler; verifiziert durch Test mit `Zeitzone::Unbekannt`, dass alle Zeitfunktionen Werte liefern
- [ ] 3.2 Den Rückfall beim Programmstart einmalig auf die Fehlerausgabe melden (nicht je Abfrage); verifiziert dadurch, dass `tbc run` bei `Unbekannt` genau eine Meldung schreibt und die Standardausgabe unberührt bleibt
- [ ] 3.3 Rückfallebene in docs/sprachreferenz.md beschreiben; verifiziert durch den Abschnittseintrag
## 4. Prüfbarkeit ohne Abhängigkeit von der Testmaschine
- [ ] 4.1 Kopfzeilen-Direktive `' tb-tz: ±HH:MM` im Korpus-Harness auswerten und als `Zeitzone::Fest` setzen; verifiziert dadurch, dass ein Korpusprogramm ohne die Direktive weiterhin läuft und eines mit ihr den gesetzten Versatz verwendet
- [ ] 4.2 Korpusprogramm `zeitzone.bas` mit festem Versatz: `DATE$`/`TIME$`/`TIMER`/`NOW` gegen eine per `DATE$ =`/`TIME$ =` gesetzte Uhrzeit prüfen; verifiziert dadurch, dass die Sollausgabe unabhängig von der Zone des Testrechners stimmt
- [ ] 4.3 Bestehendes `datumzeit.bas` auf die Direktive umstellen, damit es auch nach der Umstellung deterministisch bleibt; verifiziert dadurch, dass seine Sollausgabe unverändert bleibt
## 5. Abschluss
- [ ] 5.1 Abweichung „Uhr läuft in UTC" aus docs/sprachreferenz.md entfernen und durch die Beschreibung der Zonenermittlung ersetzen; verifiziert dadurch, dass der Abweichungspunkt nicht mehr auftaucht
- [ ] 5.2 `docs/bibliothek.md`, Abschnitt Datum/Zeit, fortschreiben; verifiziert durch den geänderten Abschnitt
- [ ] 5.3 PLAN.md Stufe 2, Eintrag „Runtime-Bibliotheken als BASIC-Bibliotheken", um die Zeitzonenbibliothek erweitern (Zonennamen, Versatz zu einem Zeitpunkt, Umrechnung zwischen Zonen) — neben crossterm; verifiziert durch den ergänzten Eintrag
- [ ] 5.4 PLAN.md Phase 3: den Hinweis auf die UTC-Abweichung durch den Verweis auf diesen Change ersetzen; verifiziert durch den aktualisierten Abschnitt
- [ ] 5.5 Inventar unverändert lassen und den Abgleichstest prüfen; verifiziert dadurch, dass `cargo test -p tb-frontend --test inventar` grün bleibt (die Elemente waren bereits implementiert, nur ihr Zeitbezug ändert sich)
- [ ] 5.6 Vollständige Suite ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist