diff --git a/src/client/components/EventDetail.tsx b/src/client/components/EventDetail.tsx index 3c53703..f1941a7 100644 --- a/src/client/components/EventDetail.tsx +++ b/src/client/components/EventDetail.tsx @@ -281,7 +281,16 @@ export function EventDetail({ )} - {event.endPrecision === "day" && event.endsAt !== null && ( + {/* Gated on the same condition `boundaryMs` uses to apply the reset + shift, so the copy never claims a countdown the clock is not + actually running. A reader's own event is day-precision exactly as + often as a parsed one, but `extractionMethod` is "manual" — there + is no source to have "given" a date, and `readerInstant` already + resolved it to the instant they meant rather than a placeholder + `boundaryMs` has to reinterpret. */} + {event.endPrecision === "day" && + event.endsAt !== null && + event.extractionMethod === "parser" && (
The source gave a date but no time of day, so this counts down to
that day's {REGION_LABEL[region]} server reset — where these usually land, but a
diff --git a/test/custom-ui.test.tsx b/test/custom-ui.test.tsx
index c602dde..93b3cc7 100644
--- a/test/custom-ui.test.tsx
+++ b/test/custom-ui.test.tsx
@@ -7,6 +7,7 @@ import {
} from "../src/client/components/CustomForms.tsx";
import { YourOwn } from "../src/client/components/YourOwn.tsx";
import { EventRow } from "../src/client/components/EventRow.tsx";
+import { EventDetail } from "../src/client/components/EventDetail.tsx";
import { AUTHOR, Colophon, REPO_URL } from "../src/client/components/Colophon.tsx";
import { GameMetaProvider } from "../src/client/state/gameMeta.tsx";
import {
@@ -14,6 +15,7 @@ import {
CustomEvent,
type CustomEvents,
type CustomGames,
+ type DisplayEvent,
} from "../src/shared/custom.ts";
import { metaFor } from "../src/shared/games.ts";
import { clockFor } from "../src/shared/time.ts";
@@ -468,3 +470,81 @@ describe("the sheet says how often", () => {
expect(cadenceLabel(null)).toBe(null);
});
});
+
+describe("the derived-boundary note", () => {
+ const NOW = Date.parse("2026-08-25T12:00:00.000Z");
+
+ const noop = () => {};
+ const detailProps = {
+ completed: false,
+ ignored: false,
+ status: undefined,
+ effort: undefined,
+ note: "",
+ region: "europe" as const,
+ now: NOW,
+ daily: false,
+ detectedDaily: false,
+ dailyDays: [],
+ onDaily: noop,
+ onToggleDay: noop,
+ onIgnore: noop,
+ onStatus: noop,
+ onEffort: noop,
+ onNote: noop,
+ onClose: noop,
+ };
+
+ // A parser declining to guess a time of day — the case the note was written
+ // for. `dates.ts` stores the placeholder as 00:00Z, and `boundaryMs` reads
+ // it against the game's own server reset rather than literally.
+ const PARSED: DisplayEvent = {
+ id: "genshin:walpurgisnacht:2026-09-03",
+ game: "genshin",
+ title: "Walpurgisnacht",
+ type: "banner",
+ summary: null,
+ startsAt: "2026-08-20T00:00:00.000Z",
+ startPrecision: "day",
+ endsAt: "2026-09-03T00:00:00.000Z",
+ endPrecision: "day",
+ regionScoped: false,
+ regionEnds: null,
+ sourceUrl: "https://example.com/events",
+ sourceId: "genshin-game8-events",
+ status: "published",
+ confidence: 1,
+ extractionMethod: "parser",
+ version: 1,
+ firstSeenAt: AT,
+ updatedAt: AT,
+ };
+
+ test("present for a parser-sourced day-precision event", () => {
+ const html = renderToStaticMarkup(
+