4f838d99c1
Build and Deploy / Build & Push (push) Successful in 3m7s
Adds a cron-driven sampler that snapshots Queue-Times waits and Six Flags Fast Lane data every 5 minutes into a new ride_wait_samples table, and a clickable per-ride detail page at /park/[id]/ride/[slug] with Today / 7d / 30d Recharts views plus a 30d uptime pill. Rides are keyed by Queue-Times' stable qt_ride_id so renames don't fragment history. Samples store pre-bucketed local_date and local_time in the park's IANA timezone so aggregations are pure SQL and DST-safe. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
111 lines
5.4 KiB
TypeScript
111 lines
5.4 KiB
TypeScript
/**
|
||
* Timezone bucketing tests for ride wait samples.
|
||
*
|
||
* Samples are stored with UTC `recorded_at` and pre-bucketed `local_date`
|
||
* + `local_time` columns in the park's IANA timezone. These columns are what
|
||
* the aggregation queries group on, so the bucketing has to be DST-safe
|
||
* across spring forward and fall back.
|
||
*
|
||
* Run with: npm test
|
||
*/
|
||
|
||
import { test } from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { formatLocalDate, formatLocalTime } from "../lib/timezone";
|
||
|
||
// ── Basic cases ──────────────────────────────────────────────────────────────
|
||
|
||
test("formatLocalDate produces YYYY-MM-DD in the target zone", () => {
|
||
// 2026-05-29 17:00 UTC = 2026-05-29 13:00 ET = 2026-05-29 10:00 PT
|
||
const d = new Date("2026-05-29T17:00:00Z");
|
||
assert.equal(formatLocalDate(d, "America/New_York"), "2026-05-29");
|
||
assert.equal(formatLocalDate(d, "America/Los_Angeles"), "2026-05-29");
|
||
});
|
||
|
||
test("formatLocalDate rolls to previous day for late UTC times in west zones", () => {
|
||
// 2026-05-30 04:00 UTC = 2026-05-29 21:00 PT
|
||
const d = new Date("2026-05-30T04:00:00Z");
|
||
assert.equal(formatLocalDate(d, "America/Los_Angeles"), "2026-05-29");
|
||
assert.equal(formatLocalDate(d, "America/New_York"), "2026-05-30");
|
||
});
|
||
|
||
test("formatLocalTime produces HH:MM in 24-hour format", () => {
|
||
// 2026-05-29 23:30 UTC = 2026-05-29 19:30 ET = 2026-05-29 16:30 PT
|
||
const d = new Date("2026-05-29T23:30:00Z");
|
||
assert.equal(formatLocalTime(d, "America/New_York"), "19:30");
|
||
assert.equal(formatLocalTime(d, "America/Los_Angeles"), "16:30");
|
||
});
|
||
|
||
// ── DST: spring forward (2026-03-08 in US: 2 AM → 3 AM) ──────────────────────
|
||
|
||
test("spring forward: time before transition shows in standard offset", () => {
|
||
// 2026-03-08 07:30 UTC = 2026-03-08 02:30 EST (before transition completes)
|
||
// Actually: at 2026-03-08 07:00 UTC = 2026-03-08 03:00 EDT (after transition)
|
||
// Use a clearly-before time:
|
||
const before = new Date("2026-03-08T06:30:00Z"); // 01:30 EST
|
||
assert.equal(formatLocalDate(before, "America/New_York"), "2026-03-08");
|
||
assert.equal(formatLocalTime(before, "America/New_York"), "01:30");
|
||
});
|
||
|
||
test("spring forward: time after transition shows in DST offset", () => {
|
||
// 2026-03-08 07:30 UTC = 2026-03-08 03:30 EDT (DST in effect)
|
||
const after = new Date("2026-03-08T07:30:00Z");
|
||
assert.equal(formatLocalDate(after, "America/New_York"), "2026-03-08");
|
||
assert.equal(formatLocalTime(after, "America/New_York"), "03:30");
|
||
});
|
||
|
||
test("spring forward: local_date is consistent across the missing hour", () => {
|
||
// The skipped hour is 02:00–03:00 EST. Samples bracketing it should still
|
||
// bucket to the same local_date.
|
||
const before = new Date("2026-03-08T06:30:00Z"); // 01:30 EST
|
||
const after = new Date("2026-03-08T07:30:00Z"); // 03:30 EDT
|
||
assert.equal(formatLocalDate(before, "America/New_York"), formatLocalDate(after, "America/New_York"));
|
||
});
|
||
|
||
// ── DST: fall back (2026-11-01 in US: 2 AM → 1 AM) ────────────────────────────
|
||
|
||
test("fall back: time before transition shows in DST offset", () => {
|
||
// 2026-11-01 05:30 UTC = 2026-11-01 01:30 EDT (before fall-back at 2 AM EDT)
|
||
const beforeFallback = new Date("2026-11-01T05:30:00Z");
|
||
assert.equal(formatLocalDate(beforeFallback, "America/New_York"), "2026-11-01");
|
||
assert.equal(formatLocalTime(beforeFallback, "America/New_York"), "01:30");
|
||
});
|
||
|
||
test("fall back: time after transition shows in standard offset", () => {
|
||
// 2026-11-01 07:30 UTC = 2026-11-01 02:30 EST (after fall-back)
|
||
const afterFallback = new Date("2026-11-01T07:30:00Z");
|
||
assert.equal(formatLocalDate(afterFallback, "America/New_York"), "2026-11-01");
|
||
assert.equal(formatLocalTime(afterFallback, "America/New_York"), "02:30");
|
||
});
|
||
|
||
test("fall back: the same local hour repeats but local_date stays stable", () => {
|
||
// 2026-11-01 05:30 UTC = 01:30 EDT
|
||
// 2026-11-01 06:30 UTC = 01:30 EST (second occurrence of 01:30 — fall back)
|
||
const first = new Date("2026-11-01T05:30:00Z");
|
||
const second = new Date("2026-11-01T06:30:00Z");
|
||
assert.equal(formatLocalTime(first, "America/New_York"), "01:30");
|
||
assert.equal(formatLocalTime(second, "America/New_York"), "01:30");
|
||
assert.equal(formatLocalDate(first, "America/New_York"), formatLocalDate(second, "America/New_York"));
|
||
});
|
||
|
||
// ── Cross-zone: a single UTC moment buckets differently per park ─────────────
|
||
|
||
test("midnight UTC straddles the local-date boundary for west-coast parks", () => {
|
||
const utcMidnight = new Date("2026-06-15T00:00:00Z");
|
||
// Eastern: still 2026-06-14 20:00
|
||
assert.equal(formatLocalDate(utcMidnight, "America/New_York"), "2026-06-14");
|
||
// Pacific: 2026-06-14 17:00
|
||
assert.equal(formatLocalDate(utcMidnight, "America/Los_Angeles"), "2026-06-14");
|
||
});
|
||
|
||
test("Mountain and Central parks bucket distinctly during the late-evening hour", () => {
|
||
// 2026-07-04 04:30 UTC
|
||
// = 2026-07-03 21:30 MDT (UTC-6) → date 2026-07-03
|
||
// = 2026-07-03 23:30 CDT (UTC-5) → date 2026-07-03
|
||
const d = new Date("2026-07-04T04:30:00Z");
|
||
assert.equal(formatLocalDate(d, "America/Denver"), "2026-07-03");
|
||
assert.equal(formatLocalDate(d, "America/Chicago"), "2026-07-03");
|
||
assert.equal(formatLocalTime(d, "America/Denver"), "22:30");
|
||
assert.equal(formatLocalTime(d, "America/Chicago"), "23:30");
|
||
});
|