Datum: der Tag kommt aus der Ortszeit, nicht aus UTC
GEFUNDEN UM 01:22, von pruef-kreislauf -- und nur, weil nachts
gearbeitet wurde.
Die Pruefung legt einen Wunsch mit dem HEUTIGEN Datum an und macht
daraus einen Termin. Gemessen:
geschickt: datum = 2026-10-01 (heuteLokal auf dem Server)
gespeichert: datum = 2026-09-30
Der Termin lag also in der Vergangenheit. Auf „Was ansteht" sortiert
er sich damit in den Abschnitt „Vorbei" ein -- und der ist mit
Absicht zugeklappt. Beim Wunsch stand „Daraus wurde ein Termin", auf
dem Brett war er nicht zu sehen. Zwei Stunden jede Nacht (im Winter
eine), genau in den Stunden, in denen hier gearbeitet wird.
URSACHE
const jetzt = () => new Date().toISOString(); // UTC
... jetzt().slice(0, 10) ... // UTC-Tag
ES WAR NICHT EINE STELLE. Nachgemessen: SECHZEHN in vierzehn Modulen,
davon ZEHN, die den falschen Tag in die Datenbank schreiben --
Termine, Wuensche, Highlights, Talente, Leads, Videos, Vorlagen --
und sechs, die „heute" vergleichen (ueberfaellige Aufgaben, Berichte,
die Frist einer Entwicklungsaufgabe).
NICHT ANGEFASST, WEIL RICHTIG: Rechnungen auf einem Datumstext mit
fester Uhrzeit (`Date.parse(tag + "T12:00:00Z") + n * 86400000`). Die
bleiben in jeder Zeitzone am selben Kalendertag -- kalender, teamlage
und serien machen es so, und das bleibt.
WARUM ES NIEMAND GEMERKT HAT
pruef-struktur sucht dieses Muster seit dem 06.09.2026. Aber:
- sie sah NUR in die `pruef-*.mjs`, nie in die Anwendung
- sie kannte die Schreibweise ueber eine FUNKTION nicht
(`const jetzt = () => ...` statt `const jetzt = ...`)
Die Wache stand vor den Pruefungen, nicht vor dem Haus -- derselbe
Fehler wie heute Nacht bei den Messports: eine Sicherung, die nur die
halbe Menge kennt, faellt in der anderen Haelfte aus, und zwar
lautlos, denn sie meldet ja „nichts gefunden".
Jetzt sieht sie in beides und kennt beide Schreibweisen. Beim ersten
scharfen Lauf fand sie sofort 23 weitere Stellen in den Pruefdateien
selbst -- dieselben Zeitbomben, gegen die sie gebaut worden war.
EINE ZWEITE WACHE, WEIL ICH SELBST HINEINGELAUFEN BIN
Mein Umbauwerkzeug hat in elf Modulen `heuteLokal()` eingesetzt und
die Einfuhr weggelassen: Es hat erst ersetzt und DANN gefragt, ob der
Name schon in der Datei steht -- da stand er, mein eigener Aufruf.
`node --check` sagt dazu nichts, „Laedt jedes Server-Modul?" auch
nicht: Die Datei ist syntaktisch tadellos. Erst der Aufruf faellt um
mit `ReferenceError: heuteLokal is not defined`. Gefunden hat es
pruef-video, zufaellig. Die anderen zehn waeren durchgerutscht.
Deshalb neu: „Ruft ein Modul etwas, das es nie eingefuehrt hat?" --
die Namen des Hauses aus den export-Zeilen gelesen, nicht
aufgezaehlt. 460 Aufrufe in 350 Dateien, alle mit Einfuhr.
pruef-kreislauf STELLT JETZT DIE RICHTIGE FRAGE
Sie war rot und hat den Fehler dabei nur gestreift: „`.kette` wird
nicht sichtbar", Zeitsperre nach 15 s. Das klingt nach der Anzeige
und schickt einen zur falschen Stelle. Neu:
- eine Zeile fragt das DATUM (ohne Browser, nennt den Fehler beim
Namen)
- der Browserteil klappt zu, was zu ist, und misst dann die Kette;
„gar nicht da" wird von „da und unsichtbar" unterschieden
NEBENBEFUND IN pruef-ics
Die Probe „fast richtig" war `echt.slice(0, -1) + "A"`. Der
Schluessel ist base64url; sein letztes Zeichen ist eines von
sechzehn. Endet er auf „A", IST die Probe der echte Schluessel, der
Server antwortet zu Recht mit 200, und die Pruefung meldet ein Loch,
das es nicht gibt -- einmal je sechzehn Laeufe. Heute Nacht zweimal
hintereinander, und die Suche ging eine halbe Stunde in eine
Aenderung, die damit nichts zu tun hatte.
GEPRUEFT
pruef-struktur 44 -> 59 Pruefungen, 0 Fehler
pruef-ics 37 -> 38, 0 Fehler
pruef-kreislauf Absturz bei Nr. 17 -> 25 Pruefungen, 0 Fehler
und gruen geblieben: treff 85, arten 28, video 74, uebernahme 39,
entwicklung 79, content 45, vorlagen 24, zuteilung 75,
scout-zuteilung 37, unterstuetzung 70, aufbewahrung 45,
bewerbung 91, treff-start 42, uebergang 65, nachwuchs 262,
auskunft 46, modi-ideen 30, neue-seiten 109, spicy 85,
wege-nach-draussen 67, aufgabenbrett 49, agentur 62,
bereiche-lesend 37 -- beide Haeuser
GEGENPROBEN, DIE WIRKLICH ROT WERDEN
- den UTC-Tag im `daraus`-Weg wieder eingebaut: pruef-kreislauf
meldet „er liegt HEUTE, nicht gestern (2026-09-30, heute ist
2026-10-01)", 25 Pruefungen, 1 Fehler -- und der Browserteil
bleibt gruen, weil er jetzt aufklappt. Jede Frage bei ihrer
eigenen Pruefung.
- eine Einfuhr aus workspace-video.js entfernt: die neue Wache
meldet „workspace-video.js: heuteLokal() (aus helfer-tag.mjs)"
- beide Erkennungen je gegen einen gebauten Rueckschritt geprueft
(Funktion, Variable, zwei Schritte, Date.now-Rechnung) und gegen
das, was NICHT anschlagen darf (UTC-Mittag, voller Zeitstempel,
fremdes Date, Name im Kommentar, Eigenschaft am Objekt)
EIN FEHLER BEIM UMBAU, HIER FESTGEHALTEN: Mein erster Lauf ueber die
Pruefdateien hat stumpf ersetzt und dabei KOMMENTARE umgeschrieben --
in sieben Dateien stand die alte Schreibweise als Beleg in der
Begruendung, und daraus wurde das Gegenteil. Bemerkt hat es der
Vergleich der Zahlen (34 Stellen statt der gemessenen 24), nicht die
Absicht. Zurueckgenommen und mit Schutz fuer Kommentare und
Zeichenketten wiederholt.
Datenbank vorher gesichert. Keine Schemaaenderung.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+238
-49
@@ -772,74 +772,263 @@ console.log(`\n=== Laedt jedes Server-Modul? ===`);
|
||||
Deshalb wird es hier gesucht, statt darauf zu warten, dass es wieder
|
||||
jemanden trifft. Gemeint ist nur das TAGESDATUM; ein Zeitstempel
|
||||
(`erstellt`) darf und soll UTC sein. */
|
||||
console.log(`\n=== Rechnen die Pruefungen mit Ortszeit? ===`);
|
||||
console.log(`\n=== Ruft ein Modul etwas, das es nie eingefuehrt hat? ===`);
|
||||
/* ===================================================================
|
||||
AM 01.10.2026 SELBST GEBAUT UND FAST AUSGELIEFERT.
|
||||
|
||||
Ein Werkzeug von mir hat in elf Modulen `heuteLokal()` eingesetzt
|
||||
und die Einfuhr weggelassen. `node --check` sagt dazu nichts, und
|
||||
„Laedt jedes Server-Modul?" ein Stueck weiter oben auch nicht: Die
|
||||
Datei ist syntaktisch tadellos. Erst der AUFRUF faellt um:
|
||||
|
||||
ReferenceError: heuteLokal is not defined
|
||||
|
||||
Gefunden hat es pruef-video -- zufaellig, weil dort genau diese
|
||||
Zeile geprueft wird. Die anderen zehn Module waeren durchgerutscht,
|
||||
und bei einer Zeile, die nur nachts laeuft, faellt das erst Wochen
|
||||
spaeter jemandem auf.
|
||||
|
||||
DAS IST EINE DRITTE SORTE FEHLER an dieser Stelle: nicht „die Datei
|
||||
ist kaputt" und nicht „die Datei tut das Falsche", sondern „die
|
||||
Datei faellt erst um, wenn jemand genau diese Zeile braucht".
|
||||
|
||||
ABGELEITET, NICHT AUFGEZAEHLT: Die Namen des Hauses kommen aus den
|
||||
`export`-Zeilen von workspace.js und der helfer-Module. Eine
|
||||
gepflegte Liste waere hier genau die, die beim naechsten Export
|
||||
nicht mitwaechst -- derselbe Fehler, an dem dieses Haus schon
|
||||
viermal haengengeblieben ist.
|
||||
=================================================================== */
|
||||
{
|
||||
const dateien = readdirSync(SERVER).filter((n) => n.startsWith("pruef-") && n.endsWith(".mjs"));
|
||||
ok(dateien.length >= 30, `${dateien.length} Pruefdateien durchgesehen`);
|
||||
/** Kommentare und Zeichenketten weg, Zeilen behalten. Sonst zaehlt
|
||||
* ein Name in einem SQL-Text oder in einer Begruendung als Aufruf. */
|
||||
const nurCode = (text) => text
|
||||
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "))
|
||||
.replace(/^\s*\/\/.*$/gm, "")
|
||||
.replace(/`(?:[^`\\]|\\.)*`/g, (m) => m.replace(/[^\n]/g, " "))
|
||||
.replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\"")
|
||||
.replace(/'(?:[^'\\\n]|\\.)*'/g, "''");
|
||||
|
||||
/** Was eine Datei nach aussen gibt -- aus ihren eigenen Zeilen. */
|
||||
const exporte = (text) => {
|
||||
const raus = new Set();
|
||||
for (const t of text.matchAll(
|
||||
/^export\s+(?:async\s+)?(?:function|const|let|class)\s+(\w+)/gm)) raus.add(t[1]);
|
||||
return raus;
|
||||
};
|
||||
|
||||
const quellen = ["workspace.js", "helfer-zeit.mjs", "helfer-tag.mjs"];
|
||||
const hausNamen = new Map(); // Name -> woher
|
||||
for (const q of quellen) {
|
||||
for (const n of exporte(readFileSync(join(SERVER, q), "utf8"))) hausNamen.set(n, q);
|
||||
}
|
||||
ok(hausNamen.size >= 30,
|
||||
`${hausNamen.size} ausgeleitete Namen aus ${quellen.length} Quellen gelesen`);
|
||||
|
||||
/** Welche Namen kennt diese Datei? Eingefuehrte, selbst erklaerte
|
||||
* und aus einer Zerlegung entnommene -- alles, was nicht zu einem
|
||||
* ReferenceError fuehren kann. */
|
||||
const bekannt = (code) => {
|
||||
const kennt = new Set();
|
||||
for (const t of code.matchAll(/import\s*\{([^}]*)\}\s*from/g)) {
|
||||
for (const stueck of t[1].split(",")) {
|
||||
const n = stueck.trim().split(/\s+as\s+/).pop().trim();
|
||||
if (n) kennt.add(n);
|
||||
}
|
||||
}
|
||||
for (const t of code.matchAll(/(?:const|let|var)\s*\{([^}]*)\}\s*=/g)) {
|
||||
for (const stueck of t[1].split(",")) {
|
||||
const n = stueck.trim().split(":").pop().trim();
|
||||
if (n) kennt.add(n);
|
||||
}
|
||||
}
|
||||
for (const t of code.matchAll(
|
||||
/(?:^|\s)(?:const|let|var|function|class)\s+(\w+)/g)) kennt.add(t[1]);
|
||||
for (const t of code.matchAll(/import\s+(\w+)\s+from/g)) kennt.add(t[1]);
|
||||
return kennt;
|
||||
};
|
||||
|
||||
const dateien = readdirSync(SERVER)
|
||||
.filter((n) => (n.endsWith(".js") || n.endsWith(".mjs")) && !quellen.includes(n));
|
||||
const fehlt = [];
|
||||
let gesucht = 0;
|
||||
for (const datei of dateien) {
|
||||
if (datei === "pruef-struktur.mjs") continue; /* siehe Gegenprobe */
|
||||
const code = nurCode(readFileSync(join(SERVER, datei), "utf8"));
|
||||
const kennt = bekannt(code);
|
||||
for (const [name, woher] of hausNamen) {
|
||||
/* Nur ein AUFRUF zaehlt. Ein Name in einer Eigenschaft
|
||||
(`x.heuteLokal`) ist etwas anderes und gehoert nicht hierher. */
|
||||
const ruft = new RegExp(`(^|[^\\w.])${name}\\s*\\(`, "m").test(code);
|
||||
if (!ruft) continue;
|
||||
gesucht++;
|
||||
if (!kennt.has(name)) fehlt.push(`${datei}: ${name}() (aus ${woher})`);
|
||||
}
|
||||
}
|
||||
ok(gesucht > 50, `${gesucht} Aufrufe von Hausnamen in ${dateien.length} Dateien angesehen`);
|
||||
ok(fehlt.length === 0,
|
||||
fehlt.length
|
||||
? `rufen etwas, das sie nicht eingefuehrt haben — das faellt erst beim`
|
||||
+ ` Aufruf um, nicht beim Laden:\n ` + fehlt.slice(0, 12).join("\n ")
|
||||
: "jeder Aufruf eines Hausnamens hat auch seine Einfuhr");
|
||||
|
||||
/* ---- GEGENPROBEN --------------------------------------------------
|
||||
Die Zeile darueber meldet „in Ordnung", solange sie nichts findet.
|
||||
Heute Nacht haette sie elf Dateien gefunden -- also einmal zeigen,
|
||||
dass sie das auch kann. */
|
||||
{
|
||||
const probeFehlt = (code, name) => {
|
||||
const c = nurCode(code);
|
||||
const ruft = new RegExp(`(^|[^\\w.])${name}\\s*\\(`, "m").test(c);
|
||||
return ruft && !bekannt(c).has(name);
|
||||
};
|
||||
ok(probeFehlt('const t = heuteLokal();', "heuteLokal"),
|
||||
"Gegenprobe: ein Aufruf ohne Einfuhr faellt auf");
|
||||
ok(!probeFehlt('import { heuteLokal } from "./workspace.js";\nconst t = heuteLokal();',
|
||||
"heuteLokal"), " mit Einfuhr NICHT");
|
||||
ok(!probeFehlt('const { heuteLokal } = await import("./workspace.js");\n'
|
||||
+ 'const t = heuteLokal();', "heuteLokal"), " und ueber eine Zerlegung auch nicht");
|
||||
ok(!probeFehlt('function heuteLokal() { return 1; }\nconst t = heuteLokal();',
|
||||
"heuteLokal"), " wer ihn selbst erklaert, ebenfalls nicht");
|
||||
ok(!probeFehlt('/* heuteLokal() waere hier falsch */\nconst t = 1;', "heuteLokal"),
|
||||
" und ein Name im Kommentar ist kein Aufruf");
|
||||
ok(!probeFehlt('const t = uhr.heuteLokal();', "heuteLokal"),
|
||||
" eine Eigenschaft an einem Objekt auch nicht");
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n=== Wird der TAG in Ortszeit gebildet -- ueberall? ===`);
|
||||
/* ===================================================================
|
||||
AM 01.10.2026 UM 01:22 GEWACHSEN, weil diese Wache versagt hatte.
|
||||
|
||||
Sie sah seit dem 06.09. nur in die `pruef-*.mjs`. In der ANWENDUNG
|
||||
standen zur selben Zeit SECHZEHN Stellen, die einen Kalendertag aus
|
||||
UTC bilden -- zehn davon schrieben ihn in die Datenbank.
|
||||
|
||||
Aufgefallen ist es nicht hier, sondern in pruef-kreislauf: Ein
|
||||
Wunsch mit dem heutigen Datum wurde zu einem Termin, und der trug
|
||||
`2026-09-30` statt `2026-10-01`. Auf dem Brett „Was ansteht" landete
|
||||
er damit unter „Vorbei" -- einem Abschnitt, der von sich aus
|
||||
zugeklappt ist. Der Termin war da und war nicht zu sehen.
|
||||
|
||||
Eine Sicherung, die nur die halbe Menge kennt, faellt in der
|
||||
anderen Haelfte aus -- lautlos, denn sie meldet ja „nichts
|
||||
gefunden". Deshalb sieht sie jetzt in BEIDES: in die Anwendung und
|
||||
in die Pruefungen.
|
||||
=================================================================== */
|
||||
{
|
||||
const pruefungen = readdirSync(SERVER)
|
||||
.filter((n) => (n.startsWith("pruef-") || n.startsWith("mess-")) && n.endsWith(".mjs"));
|
||||
/* DIE ANWENDUNG -- abgeleitet, nicht aufgezaehlt: alles, was der
|
||||
Dienst laedt, liegt als `.js` im Serverordner. Eine Liste von
|
||||
Dateinamen waere die naechste, die beim uebernaechsten Modul
|
||||
nicht mitwaechst. */
|
||||
const anwendung = readdirSync(SERVER).filter((n) => n.endsWith(".js"));
|
||||
const dateien = [...anwendung, ...pruefungen];
|
||||
ok(anwendung.length >= 30 && pruefungen.length >= 30,
|
||||
`${anwendung.length} Dateien der Anwendung und ${pruefungen.length} Pruef-/Messdateien durchgesehen`);
|
||||
|
||||
/* EINE UHRZEIT IM AUSDRUCK HEISST: DER TAG STEHT SCHON FEST.
|
||||
`Date.parse(tag + "T12:00:00Z") + n * 86400000` bleibt in jeder
|
||||
Zeitzone am selben Kalendertag -- das ist der dokumentierte Weg
|
||||
des Hauses (workspace-kalender, workspace-teamlage,
|
||||
workspace-serien). Erkannt wird das am Ausdruck selbst; eine
|
||||
Liste erlaubter Ausnahmen braucht es dadurch nicht, und eine
|
||||
Liste, die niemand pflegt, waere die naechste Stelle, an der es
|
||||
auseinanderlaeuft. */
|
||||
const festeUhrzeit = /T\d{2}:\d{2}:\d{2}Z/;
|
||||
|
||||
/** Alle Stellen einer Datei, an denen aus einem ZEITPUNKT ein
|
||||
* Kalendertag wird. Gibt Zeilennummern zurueck. */
|
||||
function utcTage(text) {
|
||||
const treffer = [];
|
||||
const zeile = (i) => text.slice(0, i).split("\n").length;
|
||||
|
||||
/* (1) Direkt: new Date(...).toISOString().slice(0, 10) */
|
||||
for (const t of text.matchAll(
|
||||
/new Date\(([^;]*?)\)\s*\.\s*toISOString\(\)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/g)) {
|
||||
if (festeUhrzeit.test(t[1])) continue;
|
||||
treffer.push(zeile(t.index));
|
||||
}
|
||||
|
||||
/* (2) Ueber einen NAMEN. Zuerst sammeln, welche Namen einen
|
||||
UTC-Zeitstempel liefern -- als Funktion ODER als Wert. Genau
|
||||
diese Schreibweise fehlte bis heute:
|
||||
|
||||
const jetzt = () => new Date().toISOString();
|
||||
... jetzt().slice(0, 10) ...
|
||||
|
||||
Und in zwei Schritten kommt sie auch vor:
|
||||
|
||||
const jetztIso = jetzt();
|
||||
... jetztIso.slice(0, 10) ... */
|
||||
const utcNamen = new Set();
|
||||
for (const t of text.matchAll(
|
||||
/(?:const|let)\s+(\w+)\s*=\s*(?:\(\)\s*=>\s*)?new Date\(\)\.toISOString\(\)/g)) {
|
||||
utcNamen.add(t[1]);
|
||||
}
|
||||
for (let runde = 0; runde < 2; runde++) {
|
||||
for (const t of text.matchAll(/(?:const|let)\s+(\w+)\s*=\s*(\w+)\(\)\s*;/g)) {
|
||||
if (utcNamen.has(t[2])) utcNamen.add(t[1]);
|
||||
}
|
||||
}
|
||||
for (const t of text.matchAll(/(\w+)(\(\))?\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/g)) {
|
||||
if (utcNamen.has(t[1])) treffer.push(zeile(t.index));
|
||||
}
|
||||
return treffer;
|
||||
}
|
||||
|
||||
const verdaechtig = [];
|
||||
for (const datei of dateien) {
|
||||
/* Die eigene Datei bleibt aussen vor: Die Gegenprobe unten MUSS
|
||||
das gesuchte Muster als Text enthalten, sonst könnte sie nicht
|
||||
beweisen, dass sie es erkennt. Sie würde sich sonst selbst
|
||||
anzeigen -- eine Warnung, die immer kommt, und die liest bald
|
||||
niemand mehr. */
|
||||
/* Die eigene Datei bleibt aussen vor: Die Gegenproben unten
|
||||
MUESSEN das gesuchte Muster als Text enthalten, sonst koennten
|
||||
sie nicht beweisen, dass sie es erkennen. Sie wuerde sich sonst
|
||||
selbst anzeigen -- eine Warnung, die immer kommt. */
|
||||
if (datei === "pruef-struktur.mjs") continue;
|
||||
/* KOMMENTARE RAUS, ZEILEN BEHALTEN (16.09.2026).
|
||||
|
||||
Hier stand `replace(…, " ")` -- ein Blockkommentar ueber zwanzig
|
||||
Zeilen wurde also zu einem Leerzeichen, und ab da zaehlte
|
||||
`split("\n").length` falsch. Gemeldet wurden Zeilennummern, die
|
||||
auf voellig anderen Code zeigten: pruef-eskalation.mjs:43 ist
|
||||
eine Zeile ueber Kekse.
|
||||
|
||||
Wer einer solchen Meldung nachgeht, findet dort nichts, haelt
|
||||
die Pruefung fuer kaputt und sieht beim naechsten Mal gar nicht
|
||||
mehr nach. Jetzt bleibt je Zeile eine Zeile stehen. */
|
||||
auf voellig anderen Code zeigten. Jetzt bleibt je Zeile eine
|
||||
Zeile stehen. */
|
||||
const text = readFileSync(join(SERVER, datei), "utf8")
|
||||
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "));
|
||||
/* Das Muster ist eng gefasst: NUR toISOString() gefolgt von einem
|
||||
Schnitt auf zehn Zeichen -- also genau die Bildung eines
|
||||
Tagesdatums. `new Date().toISOString()` allein (ein voller
|
||||
Zeitstempel) ist voellig in Ordnung und faellt nicht darunter. */
|
||||
for (const treffer of text.matchAll(/toISOString\(\)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/g)) {
|
||||
const zeile = text.slice(0, treffer.index).split("\n").length;
|
||||
verdaechtig.push(`${datei}:${zeile}`);
|
||||
}
|
||||
/* Und die zweite Schreibweise: erst in eine Variable, dann geschnitten. */
|
||||
for (const treffer of text.matchAll(/=\s*(\w+)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/g)) {
|
||||
const name = treffer[1];
|
||||
/* Nur melden, wenn diese Variable aus toISOString() stammt. */
|
||||
if (new RegExp(`(const|let)\\s+${name}\\s*=\\s*new Date\\(\\)\\.toISOString\\(\\)`).test(text)) {
|
||||
const zeile = text.slice(0, treffer.index).split("\n").length;
|
||||
verdaechtig.push(`${datei}:${zeile} (über ${name})`);
|
||||
}
|
||||
}
|
||||
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "))
|
||||
.replace(/^\s*\/\/.*$/gm, "");
|
||||
for (const z of utcTage(text)) verdaechtig.push(`${datei}:${z}`);
|
||||
}
|
||||
|
||||
ok(verdaechtig.length === 0,
|
||||
verdaechtig.length
|
||||
? `bilden ihr Tagesdatum aus UTC statt Ortszeit — nachts zwischen `
|
||||
+ `00:00 und 02:00 schlagen sie fälschlich an:\n `
|
||||
+ `00:00 und 02:00 liegt es einen Tag zurueck:\n `
|
||||
+ verdaechtig.join("\n ")
|
||||
: `keine Prüfung bildet ihr Tagesdatum aus UTC (alle nutzen helfer-zeit.mjs)`);
|
||||
: `keine der ${dateien.length} Dateien bildet einen Kalendertag aus UTC`);
|
||||
|
||||
/* GEGENPROBE: Findet das Muster überhaupt etwas? Ohne diesen
|
||||
Nachweis wäre "0 Treffer" auch dann grün, wenn der reguläre
|
||||
Ausdruck gar nichts erkennt. */
|
||||
const probe = "const jetzt = new Date().toISOString();\nconst heute = jetzt.slice(0, 10);";
|
||||
const erkanntA = /toISOString\(\)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/
|
||||
.test("new Date().toISOString().slice(0, 10)");
|
||||
const erkanntB = /=\s*(\w+)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/.test(probe)
|
||||
&& /(const|let)\s+jetzt\s*=\s*new Date\(\)\.toISOString\(\)/.test(probe);
|
||||
ok(erkanntA && erkanntB,
|
||||
"Gegenprobe: beide Schreibweisen werden erkannt (direkt und über eine Variable)");
|
||||
/* ---- GEGENPROBEN -------------------------------------------------
|
||||
Eine Erkennung, die nichts findet, sieht genauso aus wie eine, die
|
||||
nichts finden KANN. Heute Nacht war sie genau das: Die Schreibweise
|
||||
mit einer Funktion kannte sie nicht, und die Anwendung sah sie gar
|
||||
nicht an. Beides steht jetzt hier als Probe. */
|
||||
const probe = (code) => utcTage(code).length;
|
||||
ok(probe('const h = new Date().toISOString().slice(0, 10);') === 1,
|
||||
"Gegenprobe: die direkte Schreibweise faellt auf");
|
||||
ok(probe('const jetzt = () => new Date().toISOString();\nconst h = jetzt().slice(0, 10);') === 1,
|
||||
" die ueber eine FUNKTION ebenfalls — genau die, die heute Nacht durchrutschte");
|
||||
ok(probe('const jetzt = new Date().toISOString();\nconst h = jetzt.slice(0, 10);') === 1,
|
||||
" und die ueber eine Variable");
|
||||
ok(probe('const jetzt = () => new Date().toISOString();\n'
|
||||
+ 'const iso = jetzt();\nconst h = iso.slice(0, 10);') === 1,
|
||||
" auch ueber zwei Schritte");
|
||||
ok(probe('const g = new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10);') === 1,
|
||||
" und eine Rechnung auf Date.now()");
|
||||
|
||||
/* Und ein harmloser Zeitstempel darf NICHT anschlagen -- sonst wäre
|
||||
die Prüfung eine Warnung, die immer kommt. */
|
||||
const harmlos = /toISOString\(\)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/
|
||||
.test("const jetzt = new Date().toISOString();");
|
||||
ok(!harmlos, "Gegenprobe: ein voller Zeitstempel (ohne Schnitt) schlägt nicht an");
|
||||
ok(probe('const jetzt = new Date().toISOString();') === 0,
|
||||
"Gegenprobe: ein voller Zeitstempel ohne Schnitt schlaegt NICHT an");
|
||||
ok(probe('const b = new Date(Date.parse(tag + "T12:00:00Z") + n * 86400000)'
|
||||
+ '.toISOString().slice(0, 10);') === 0,
|
||||
" und die Rechnung auf UTC-Mittag ebenfalls nicht — die ist richtig so");
|
||||
ok(probe('const alsText = (d) => d.toISOString().slice(0, 10);') === 0,
|
||||
" und ein Date, das von aussen kommt, auch nicht");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
|
||||
Reference in New Issue
Block a user