feat(backend): pin server timezone to UTC with startup assertion

Belt-and-suspenders, not a bug fix: storage (timestamptz) and timer math are already tz-independent. Add SERVER_TZ env (default UTC) via getServerTimezone(); db/client.js pins the DB session timezone (reads env directly to avoid an import cycle); server.js pins process.env.TZ and asserts at boot that the DB session matches (logs [tz] or a loud warning). Keeps any future date_trunc/::date reporting deterministic and surfaces a misconfigured server early. Documented in backend/CLAUDE.md + .env.example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 22:27:16 +08:00
parent 495eb98787
commit 529a38ae3f
5 changed files with 58 additions and 2 deletions

View File

@@ -114,6 +114,20 @@ export const getMitraHeartbeatCadenceSeconds = () => {
return Number.isFinite(parsed) && parsed >= 5 ? parsed : 30
}
// Server timezone. Defaults to UTC and should essentially never be changed —
// see backend/CLAUDE.md "Timezone" note. timestamptz storage and all of our
// instant-based timer math are timezone-INDEPENDENT, so this is belt-and-
// suspenders: it pins the DB session + Node process to one zone so that any
// FUTURE session-tz-dependent SQL (date_trunc / ::date / CURRENT_DATE) and any
// stray local-time Date formatting stay deterministic across deploys.
// NOTE: db/client.js reads `process.env.SERVER_TZ || 'UTC'` directly (it cannot
// import this module without a cycle); keep the default in sync.
export const getServerTimezone = () => {
const raw = process.env.SERVER_TZ
if (!raw || raw.trim() === '') return 'UTC'
return raw.trim()
}
// --- Valkey availability mirror — env-driven cadences ---
//
// Per requirement/valkey-online-mirror-plan.md. All three are operational