Files
SixFlagsSuperCalendar/tests/timezone-bucketing.test.ts
josh 4f838d99c1
Build and Deploy / Build & Push (push) Successful in 3m7s
feat: add per-ride history charts with wait time and uptime tracking
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>
2026-05-29 23:35:27 -04:00

111 lines
5.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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:0003: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");
});