Files
2026-06-18 22:21:38 -04:00

5.6 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
18-auto-timezone-detection-and-ability-to-change-timezone 01 api
timezone
iana
drizzle
vitest
tdd
phase provides
10-admin-role-settings appConfig table (key/value store where household_timezone key lives)
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
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)
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
created modified
apps/api/src/lib/householdTimezone.ts
apps/api/tests/lib/householdTimezone.test.ts
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)
householdTimezone accessor pattern: getHouseholdTimezone(db) as the single import site for all tz reads in broker workers
D-05
D-06
2min 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: db0077ctest(18-01): add failing tests for household timezone accessor + IANA validator
  • GREEN gate: eaceff0feat(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