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>
7.4 KiB
Context
Siehe proposal.md — Why. Der Ist-Zustand ist eng umrissen:
tb-runtime::datetimerechnet 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-runtimehat 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
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:
' 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)undUnbekanntgeprü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/TIMEVALUElesen 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.