refactor(time): make resetHourLocal a per-region map
A game's servers need not agree on the reset hour any more than on the offset. Reverse: 1999 and Nikke spell their 05:00 out for all three regions, so no reader's day key moves. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bf9d5d45cd
commit
72fe6b618b
+2
-1
@@ -397,7 +397,8 @@ already logged, which is the change this table warns about below.
|
||||
than a bent offset. It runs a single global server on UTC-5 and resets at **05:00**, so its day rolls
|
||||
at 10:00 UTC. `resetOffsets` alone cannot say that: landing 10:00 UTC through the offset would mean
|
||||
claiming a UTC-6 server, which is a lie every other reader of `serverOffsetUtc` would inherit. So
|
||||
`GameMeta.resetHourLocal` overrides `RESET_HOUR_LOCAL` per game, and is absent — meaning 04:00 — for
|
||||
`GameMeta.resetHourLocal` overrides `RESET_HOUR_LOCAL` per region, like `resetOffsets` — a game's
|
||||
servers need not agree on the hour any more than on the offset — and is absent — meaning 04:00 — for
|
||||
everything else, which is why introducing it moved no existing day key. Both facts come off the
|
||||
source: all 154 rows of the wiki's event list state `(UTC-5)`, and every one runs 05:00 → 04:59, an
|
||||
event ending one minute before the reset the next one starts on.
|
||||
|
||||
+10
-5
@@ -40,20 +40,25 @@ export interface GameMeta {
|
||||
*/
|
||||
resetOffsets?: Partial<Record<Region, number>> | undefined;
|
||||
/**
|
||||
* The hour of the server's own day this game rolls over on, when it is not
|
||||
* `RESET_HOUR_LOCAL` (04:00).
|
||||
* The hour of the server's own day this game rolls over on, per region, where
|
||||
* it is not `RESET_HOUR_LOCAL` (04:00).
|
||||
*
|
||||
* Most gacha servers reset at 04:00 local. Reverse: 1999 resets at 05:00, and
|
||||
* the difference is not cosmetic: `resetOffsets` alone cannot express it,
|
||||
* because bending a game's stated server offset to land the right instant
|
||||
* would put every other reader of that offset an hour out.
|
||||
*
|
||||
* Per region for the same reason `resetOffsets` is: a game's servers need not
|
||||
* agree on the hour either. Girls' Frontline 2's Asia server rolls at 05:00
|
||||
* UTC+8 while its global server rolls at 21:00 UTC-8, and one number per game
|
||||
* would have to lie about one of them. A region absent here takes 04:00.
|
||||
*
|
||||
* Like `resetOffsets` this feeds `dayKey`, which is a **localStorage key**, so
|
||||
* the same warning applies — changing it for a game that already has readers
|
||||
* re-labels the game-day their logged ticks fall in. Absent means 04:00, which
|
||||
* is why adding this field moved no existing game.
|
||||
*/
|
||||
resetHourLocal?: number | undefined;
|
||||
resetHourLocal?: Partial<Record<Region, number>> | undefined;
|
||||
}
|
||||
|
||||
export const GAMES: Record<GameId, GameMeta> = {
|
||||
@@ -86,7 +91,7 @@ export const GAMES: Record<GameId, GameMeta> = {
|
||||
// the source rather than from habit: all 154 rows on the wiki's event list
|
||||
// state `(UTC-5)`, and every one of them starts at 05:00 and ends at 04:59 —
|
||||
// an event ending one minute before the reset that the next one begins on.
|
||||
r1999: { id: "r1999", name: "Reverse: 1999", short: "R1999", hue: "#C9A227" , studio: "Bluepoch", dailyTasks: "Daily missions", resetOffsets: { asia: -5, america: -5, europe: -5 }, resetHourLocal: 5 },
|
||||
r1999: { id: "r1999", name: "Reverse: 1999", short: "R1999", hue: "#C9A227" , studio: "Bluepoch", dailyTasks: "Daily missions", resetOffsets: { asia: -5, america: -5, europe: -5 }, resetHourLocal: { asia: 5, america: 5, europe: 5 } },
|
||||
// No `resetOffsets` and no `resetHourLocal`, for the reason p5x has none: the
|
||||
// source states no time of day anywhere — every date on bluearchive.wiki's
|
||||
// Global schedule is a bare `YYYY-MM-DD`. Blue Archive Global does run one
|
||||
@@ -149,7 +154,7 @@ export const GAMES: Record<GameId, GameMeta> = {
|
||||
// Set in the same commit that ships the game, which costs nothing now and
|
||||
// could not be added later without re-labelling day keys readers had already
|
||||
// logged ticks under.
|
||||
nikke: { id: "nikke", name: "Goddess of Victory: Nikke", short: "Nikke", hue: "#E4572E", studio: "Shift Up", dailyTasks: "Daily missions, outpost", resetOffsets: { asia: 9, america: 9, europe: 9 }, resetHourLocal: 5 },
|
||||
nikke: { id: "nikke", name: "Goddess of Victory: Nikke", short: "Nikke", hue: "#E4572E", studio: "Shift Up", dailyTasks: "Daily missions, outpost", resetOffsets: { asia: 9, america: 9, europe: 9 }, resetHourLocal: { asia: 5, america: 5, europe: 5 } },
|
||||
uma: { id: "uma", name: "Umamusume: Pretty Derby", short: "Umamusume", hue: "#6FBF44", studio: "Cygames", dailyTasks: "Daily races, missions" },
|
||||
// No `resetOffsets`, and the source is silent in the strongest sense: every
|
||||
// boundary Honkai Impact 3rd has here is a **week bucket**, and every one of
|
||||
|
||||
+6
-5
@@ -59,10 +59,11 @@ export function serverOffsetUtc(region: Region, game?: LaneId): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* The hour of its own server day a game rolls over on.
|
||||
* The hour of its own server day a game rolls over on, for the reader's region.
|
||||
*
|
||||
* Almost always `RESET_HOUR_LOCAL`. A game that resets on a different hour says
|
||||
* so in `resetHourLocal` (`games.ts`) — Reverse: 1999 rolls at 05:00 — and that
|
||||
* so in `resetHourLocal` (`games.ts`), per region because a game's servers need
|
||||
* not agree — Reverse: 1999 rolls at 05:00 everywhere — and that
|
||||
* cannot be folded into `serverOffsetUtc`: shifting a game's stated offset to
|
||||
* land the right reset instant would misreport the server clock to everything
|
||||
* else that asks for it.
|
||||
@@ -70,9 +71,9 @@ export function serverOffsetUtc(region: Region, game?: LaneId): number {
|
||||
* A lane the reader invented has no server to know about and takes the default,
|
||||
* for the same reason `serverOffsetUtc` does.
|
||||
*/
|
||||
export function resetHourFor(game?: LaneId): number {
|
||||
export function resetHourFor(region: Region, game?: LaneId): number {
|
||||
const override =
|
||||
game === undefined ? undefined : GAMES[game as GameId]?.resetHourLocal;
|
||||
game === undefined ? undefined : GAMES[game as GameId]?.resetHourLocal?.[region];
|
||||
return override ?? RESET_HOUR_LOCAL;
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ export function resetHourFor(game?: LaneId): number {
|
||||
* here as a data change, not a constant.
|
||||
*/
|
||||
export function resetShiftMs(region: Region, game?: LaneId): number {
|
||||
return serverOffsetUtc(region, game) * HOUR - resetHourFor(game) * HOUR;
|
||||
return serverOffsetUtc(region, game) * HOUR - resetHourFor(region, game) * HOUR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -393,12 +393,12 @@ describe("a game whose day rolls on a different hour", () => {
|
||||
test("resetHourFor defaults to 04:00 for every other game", () => {
|
||||
// Adding the field must not have moved a single existing reader's day key,
|
||||
// which is why it is an override rather than a per-game table.
|
||||
expect(resetHourFor("r1999")).toBe(5);
|
||||
expect(resetHourFor("europe", "r1999")).toBe(5);
|
||||
for (const game of ["genshin", "endfield", "arknights", undefined] as const) {
|
||||
expect(resetHourFor(game)).toBe(RESET_HOUR_LOCAL);
|
||||
expect(resetHourFor("europe", game)).toBe(RESET_HOUR_LOCAL);
|
||||
}
|
||||
// A lane the reader invented has no server map and takes the default too.
|
||||
expect(resetHourFor("mygame:my-own-game")).toBe(RESET_HOUR_LOCAL);
|
||||
expect(resetHourFor("europe", "mygame:my-own-game")).toBe(RESET_HOUR_LOCAL);
|
||||
});
|
||||
|
||||
test("an event's checklist counts days on the 05:00 clock", () => {
|
||||
|
||||
Reference in New Issue
Block a user