feat: add Stella Sora, from the wiki's front page rather than its banner list

This wiki publishes the same schedule twice and the fuller surface is the worse
one. /wiki/Banner_List has 55 clean rows with full wall clocks and states no
timezone anywhere on the page; the front page's Current Banners module emits
the same instants as real <time datetime="...-07:00"> elements. The two agree
exactly, which is strong evidence the list is UTC and is still only evidence —
so we read the surface that says what it means and pay four live banners
instead of a full history for it. If an editor ever states the zone on
Banner_List, that page becomes the better source immediately.

Two ways this could have failed silently. The template writes its BEM
underscores as &#95;&#95;, so a selector written against the class name a
browser shows matches nothing at all; and banner names are red links to
?action=edit&redlink=1, which Miraheze's robots.txt disallows, so a href
carrying a query is refused and the page URL stands in.

No resetOffsets, and here the source did state an offset: -07:00, which is US
Pacific and therefore shifts twice a year. That is the Fate/Grand Order gap
arriving through a page that looks like it answered the question.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-19 03:50:20 +02:00
co-authored by Claude Opus 5
parent 4dff14087f
commit ecbf9c0422
14 changed files with 1286 additions and 13 deletions
+38
View File
@@ -12,6 +12,7 @@ import {
parseSlashDateTimeRange,
parseYearFirstSlashRange,
parseIsoClockRangeUtc,
parseIsoOffsetInstant,
} from "../src/ingest/dates.ts";
describe("parseMonthDayYear", () => {
@@ -472,3 +473,40 @@ describe("parseIsoClockRangeUtc", () => {
).toBe("2026-08-06T13:00:00.000Z");
});
});
describe("parseIsoOffsetInstant", () => {
test("converts a stated offset to UTC", () => {
// Stella Sora's front page emits its banner window as real `<time datetime>`
// attributes, so the offset is in the markup rather than printed for a
// human to interpret.
expect(parseIsoOffsetInstant("2026-08-03T21:00-07:00")?.iso).toBe(
"2026-08-04T04:00:00.000Z",
);
expect(parseIsoOffsetInstant("2026-08-03T21:00-07:00")?.precision).toBe(
"exact",
);
});
test("accepts Z and seconds", () => {
expect(parseIsoOffsetInstant("2026-08-03T21:00:00Z")?.iso).toBe(
"2026-08-03T21:00:00.000Z",
);
});
test("requires the offset rather than defaulting to UTC", () => {
// The sibling `Banner_List` prints the same instants with no zone anywhere
// on the page. Reading those as UTC is the assumption this reader exists to
// refuse.
expect(parseIsoOffsetInstant("2026-08-03T21:00")).toBeNull();
expect(parseIsoOffsetInstant("2026-08-03 21:00-07:00")).toBeNull();
});
test("rejects an impossible calendar day, before the offset shifts it", () => {
// Converting first would quietly turn February 30 into a real March instant.
expect(parseIsoOffsetInstant("2026-02-30T21:00-07:00")).toBeNull();
});
test("is anchored, so it cannot read an instant out of prose", () => {
expect(parseIsoOffsetInstant("Starts 2026-08-03T21:00-07:00")).toBeNull();
});
});