/* ===================================================================== EIN TAGESDATUM IN ORTSZEIT — für Prüfungen (16.09.2026) `new Date().toISOString().slice(0, 10)` ist der bequemste Weg zu einem Datum und der falsche. toISOString rechnet in UTC; Server und Menschen stehen hier auf Europe/Berlin. Zwischen Mitternacht und zwei Uhr morgens liefert die Rechnung deshalb den VORTAG. In einer Prüfung heißt das: Sie ist tagsüber grün und nachts rot, ohne dass irgendetwas kaputt ist. Ein Fehlalarm, den man nicht nachstellen kann, ist der sicherste Weg, eine Prüfung abzuschalten. --------------------------------------------------------------------- WARUM NICHT AUS workspace.js Dort stehen `heuteLokal()` und `tagLokal()` schon, mit demselben Kommentar — aber wer sie importiert, zieht das ganze Haus mit: die Datenbank, die Umstellungen, die Zeitgeber. Eine Prüfung, die nur ein Datum braucht, soll dafür keinen Server hochfahren. Dieselbe Rechnung an zwei Stellen ist hier also Absicht, und es ist die Ausnahme, nicht die Regel: Es sind vier Zeilen ohne Verzweigung, sie können nicht auseinanderlaufen, ohne dass pruef-struktur es meldet. --------------------------------------------------------------------- UND WANN toISOString RICHTIG IST Wenn die Uhrzeit fest auf Mittag steht. `Date.parse("…T12:00:00Z")` plus oder minus ganze Tage bleibt in jeder Zeitzone am selben Kalendertag — deshalb rechnet der Server mit UTC-Mittag, wenn er Fristen VERGLEICHT. Nur beim ANZEIGEN und beim Erzeugen eines Tagesdatums gilt die Ortszeit. ===================================================================== */ const zwei = (n) => String(n).padStart(2, "0"); /** Der heutige Tag in Ortszeit, als `JJJJ-MM-TT`. */ export function heuteLokal() { const d = new Date(); return `${d.getFullYear()}-${zwei(d.getMonth() + 1)}-${zwei(d.getDate())}`; } /** Derselbe Tag, um `versatz` Tage verschoben. Negativ heißt zurück. * * ZWEI DINGE AM 22.09.2026 NACHGEZOGEN, beide durch einen echten Schaden: * * (1) `versatz = 0`. Dogi-Media rief `tagLokal()` ohne Argument auf und * bekam `"NaN-NaN-NaN"` zurück -- eine Zeichenkette, die aussieht * wie ein Datum und sich wie keines verhält. Im Vergleich * `"2026-12-31" < "NaN-NaN-NaN"` gewinnt das N, also galt JEDES * Stück mit Enddatum vom ersten Tag an als abgelaufen, und * "Kommt noch" gab es nie. Kein Absturz, keine Meldung, keine * Zeile im Protokoll -- nur eine Seite, die etwas anderes zeigt * als die Wahrheit. Die drei Kopien dieser Funktion waren an * dieser Stelle auseinandergelaufen: helfer-tag.mjs hatte die * Vorbelegung, die beiden anderen nicht. * * (2) Der Wurf bei einer Zahl, die keine ist. Eine Vorbelegung deckt * nur den leeren Aufruf ab; `tagLokal(irgendwas)` mit einer * undefinierten Variablen läge weiter still daneben. Ein Datum, * das sich nicht ausrechnen lässt, muss SCHEITERN und nicht * schweigen -- sonst wandert der Unsinn in die Datenbank und in * die Anzeige, und gefunden wird er Wochen später. */ export function tagLokal(versatz = 0) { if (!Number.isFinite(Number(versatz))) { throw new TypeError(`tagLokal: "${versatz}" ist keine Zahl von Tagen.`); } const d = new Date(Date.now() + versatz * 86400_000); return `${d.getFullYear()}-${zwei(d.getMonth() + 1)}-${zwei(d.getDate())}`; } /** Ein beliebiger Zeitpunkt als Tagesdatum in Ortszeit. * * Für Prüfungen, die von einem FESTEN Zeitpunkt aus rechnen statt von * jetzt — dort ist die Zeitzone genauso entscheidend. */ export function tagVon(zeitpunkt, versatz = 0) { const d = new Date(zeitpunkt + versatz * 86400_000); return `${d.getFullYear()}-${zwei(d.getMonth() + 1)}-${zwei(d.getDate())}`; }