Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
125 lines
5.6 KiB
Markdown
125 lines
5.6 KiB
Markdown
---
|
|
phase: 18-auto-timezone-detection-and-ability-to-change-timezone
|
|
plan: "01"
|
|
subsystem: api
|
|
tags: [timezone, iana, drizzle, vitest, tdd]
|
|
|
|
# Dependency graph
|
|
requires:
|
|
- phase: 10-admin-role-settings
|
|
provides: appConfig table (key/value store where household_timezone key lives)
|
|
provides:
|
|
- getHouseholdTimezone(db) — single D-05 accessor for stored household timezone with D-06 fallback chain
|
|
- isValidIanaTimezone(tz) — IANA timezone validator via Intl.DateTimeFormat try/catch
|
|
|
|
affects:
|
|
- 18-02-PLAN (broker rewire — reminderScheduler + outboxWorker import getHouseholdTimezone)
|
|
- 18-03-PLAN (admin API routes — import isValidIanaTimezone for Zod refine)
|
|
- 18-04-PLAN (PWA settings UI — consumes admin timezone API built on top of these)
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- Drizzle single-row PK lookup (.select().from().where(eq()).limit(1)) — same pattern as requireAdmin.ts
|
|
- IANA timezone validation via try/catch on Intl.DateTimeFormat (avoids Intl.supportedValuesOf omitting 'UTC')
|
|
- TZ env save/restore in beforeEach/afterEach to prevent env state leaks between tests
|
|
|
|
key-files:
|
|
created:
|
|
- apps/api/src/lib/householdTimezone.ts
|
|
- apps/api/tests/lib/householdTimezone.test.ts
|
|
modified: []
|
|
|
|
key-decisions:
|
|
- "isValidIanaTimezone uses Intl.DateTimeFormat try/catch — NOT Intl.supportedValuesOf (omits UTC per RESEARCH Pitfall 2)"
|
|
- "D-06 fallback chain verbatim: row?.value ?? process.env.TZ ?? Intl.DateTimeFormat().resolvedOptions().timeZone"
|
|
- "Literal key string 'household_timezone' in the WHERE clause (D-01)"
|
|
|
|
patterns-established:
|
|
- "householdTimezone accessor pattern: getHouseholdTimezone(db) as the single import site for all tz reads in broker workers"
|
|
|
|
requirements-completed: [D-05, D-06]
|
|
|
|
# Metrics
|
|
duration: 2min
|
|
completed: 2026-06-15
|
|
---
|
|
|
|
# Phase 18 Plan 01: Household Timezone Accessor + IANA Validator Summary
|
|
|
|
**Single D-05 accessor `getHouseholdTimezone(db)` reads `household_timezone` from `app_config` with D-06 fallback chain; `isValidIanaTimezone(tz)` validates via `Intl.DateTimeFormat` try/catch (not `supportedValuesOf`)**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** 2 min
|
|
- **Started:** 2026-06-15T02:04:31Z
|
|
- **Completed:** 2026-06-15T02:06:50Z
|
|
- **Tasks:** 2 (TDD RED + GREEN)
|
|
- **Files modified:** 2
|
|
|
|
## Accomplishments
|
|
|
|
- Created `apps/api/src/lib/householdTimezone.ts` exporting `getHouseholdTimezone` and `isValidIanaTimezone`
|
|
- 11 unit tests cover all 6 required behaviors: stored row, no-row + TZ env, no-row + no-TZ, null row, valid zones, invalid zones
|
|
- D-06 fallback chain `row?.value ?? process.env.TZ ?? Intl.DateTimeFormat().resolvedOptions().timeZone` reproduced verbatim
|
|
- TypeScript typecheck (`tsc --noEmit`) clean; full 358-test suite green with no regressions
|
|
- TDD gate compliance: RED commit (`db0077c`) precedes GREEN commit (`eaceff0`)
|
|
|
|
## Task Commits
|
|
|
|
1. **Task 1: RED — failing unit tests** - `db0077c` (test)
|
|
2. **Task 2: GREEN — implement accessor + validator** - `eaceff0` (feat)
|
|
|
|
**Plan metadata:** (see final docs commit below)
|
|
|
|
## Files Created/Modified
|
|
|
|
- `apps/api/src/lib/householdTimezone.ts` — D-05 accessor + IANA validator, exported for broker + admin route consumers
|
|
- `apps/api/tests/lib/householdTimezone.test.ts` — 11 unit tests for fallback chain and validator
|
|
|
|
## Decisions Made
|
|
|
|
- `isValidIanaTimezone` uses `Intl.DateTimeFormat(undefined, { timeZone: tz })` try/catch — `Intl.supportedValuesOf('timeZone')` was explicitly avoided because it omits `'UTC'` in some environments (RESEARCH Pitfall 2).
|
|
- Fallback chain matches the verbatim expression found at `reminderScheduler.ts:247` so the all-day reminder path continues to work unchanged before Plan 02 seeds a stored value.
|
|
- `db` is accepted as a parameter (not imported from `db/client.js`) to enable clean mock-based unit testing without a live MariaDB connection.
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
## Issues Encountered
|
|
|
|
- The vitest global setup requires a MariaDB connection (`global-setup.ts`). Tests need to be run with `DB_HOST=127.0.0.1` when running locally (the `.env` sets `DB_HOST=mariadb` for Docker networking). This is a known dev environment pattern documented in `familysync-dev-stack-setup.md` and has no effect on CI (which uses the service container).
|
|
|
|
## Known Stubs
|
|
|
|
None — this plan creates a pure utility module with no UI stubs or placeholder data.
|
|
|
|
## Threat Flags
|
|
|
|
None — no new network endpoints, auth paths, file access patterns, or schema changes were introduced. The accessor is a read-only DB lookup within the trusted server process (T-18-01 disposition: accept).
|
|
|
|
## TDD Gate Compliance
|
|
|
|
- RED gate: `db0077c` — `test(18-01): add failing tests for household timezone accessor + IANA validator`
|
|
- GREEN gate: `eaceff0` — `feat(18-01): implement household timezone accessor + IANA validator`
|
|
- REFACTOR gate: N/A (implementation was clean on first pass)
|
|
|
|
## Next Phase Readiness
|
|
|
|
- `getHouseholdTimezone` and `isValidIanaTimezone` are the foundation both Plan 18-02 (broker rewire) and Plan 18-03 (admin API + Zod refine) consume.
|
|
- Plan 18-02 can import `getHouseholdTimezone` from `../lib/householdTimezone.js` to replace the bare `process.env.TZ ?? Intl…` lookups in `reminderScheduler.ts:247` and `outboxWorker.ts:501,607`.
|
|
- Plan 18-03 can import `isValidIanaTimezone` for the Zod `.refine()` on `PUT /api/admin/config/timezone`.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `apps/api/src/lib/householdTimezone.ts` — FOUND
|
|
- `apps/api/tests/lib/householdTimezone.test.ts` — FOUND
|
|
- Commit `db0077c` — FOUND
|
|
- Commit `eaceff0` — FOUND
|
|
|
|
---
|
|
*Phase: 18-auto-timezone-detection-and-ability-to-change-timezone*
|
|
*Completed: 2026-06-15*
|