Skip to content

fix(mp): correct timezone handling on MP datetime writes#15

Merged
chriskehayias merged 1 commit into
mainfrom
fix/mp-datetime-handling
May 21, 2026
Merged

fix(mp): correct timezone handling on MP datetime writes#15
chriskehayias merged 1 commit into
mainfrom
fix/mp-datetime-handling

Conversation

@chriskehayias
Copy link
Copy Markdown
Contributor

Summary

  • MP stores datetimes as wall-clock in the domain's time zone, not UTC. Existing write paths tagged values as UTC (${date}T00:00:00Z, new Date().toISOString()) which caused saved records to drift by the offset between the Node server's local zone and MP's domain zone — and edits compounded the drift on each save.
  • Adds DomainTimezoneService (singleton, Windows→IANA mapping, toMpSqlDatetime / parseMpDatetime), a getMpTimezone() shared server action, fixes the offending writes in GroupService and FamilyService, and ships a reference doc + CLAUDE.md rule so future MP date work doesn't re-introduce the pattern.

Changes

  • New: src/services/domainTimezoneService.ts — singleton boundary service; only path that reads MPHelper.getDomainInfo().TimeZoneName.
  • New: src/services/domainTimezoneService.test.ts — 16 tests covering Windows→IANA mapping, caching, concurrent dedupe, wall-clock vs instant inputs, and a server-TZ-independent regression.
  • New: src/components/shared-actions/domain.tsgetMpTimezone() server action for client-side Intl.DateTimeFormat rendering.
  • Fix: src/services/groupService.tsStart_Date / End_Date / Promotion_Date now route through toMpSqlDatetime.
  • Fix: src/services/familyService.tsParticipant_Start_Date and donor Setup_Date writes use MP-TZ wall-clock instead of new Date().toISOString().
  • Test: src/services/groupService.test.ts — mocks getDomainInfo, resets both singletons, asserts MP-SQL format, and adds a 3-save round-trip regression proving no drift.
  • Docs: .claude/references/ministryplatform.datetimehandling.md — full reference (recipes, anti-patterns, Windows↔IANA table, testing guidance).
  • Docs: CLAUDE.md — Key Development Practice chore(deps): security audit updates + mjml fix #12 + Reference Documents entry pointing to the new doc.

Test plan

  • npx vitest run src/services/domainTimezoneService.test.ts — 16/16 pass
  • npx vitest run (full suite) — 664/664 pass
  • TZ=UTC npx vitest run — 664/664 pass
  • TZ=America/Los_Angeles npx vitest run — 664/664 pass
  • npm run lint — clean
  • npx tsc --noEmit — no new errors (pre-existing errors in helper.test.ts, provider.test.ts, word-document.test.ts, logger.test.ts are untouched and unrelated)
  • Manual spot-check: save a group with Start_Date = today, then re-save without changing fields, confirm date does not shift

🤖 Generated with Claude Code

MP stores datetimes as wall-clock values in the domain's configured time
zone, not UTC. Existing write paths in GroupService and FamilyService tagged
values as UTC (via `${date}T00:00:00Z` or `new Date().toISOString()`),
which caused saved records to drift by the offset between the Node server's
local zone and MP's domain zone. Edits compounded the drift on each save.

- Add DomainTimezoneService — singleton wrapping getDomainInfo() with
  Windows->IANA mapping and SQL datetime conversion (toMpSqlDatetime,
  parseMpDatetime).
- Add shared server action getMpTimezone() for client-side display.
- Fix GroupService.createGroup/updateGroup to route Start_Date / End_Date /
  Promotion_Date through the service.
- Fix FamilyService Participant_Start_Date and Donor Setup_Date writes to
  use MP-TZ wall-clock instead of new Date().toISOString().
- Update groupService.test.ts: mock getDomainInfo, reset both singletons in
  beforeEach, assert MP-SQL format, add round-trip regression proving three
  consecutive saves of the same date do not shift.
- Add reference doc .claude/references/ministryplatform.datetimehandling.md.
- Update CLAUDE.md with Key Development Practice #12 + reference link.

Tests: 16 new in domainTimezoneService.test.ts, 1 round-trip regression in
groupService.test.ts. Full suite passes (664/664) under TZ=UTC and
TZ=America/Los_Angeles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chriskehayias chriskehayias merged commit 6034a7f into main May 21, 2026
2 checks passed
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 94.50549% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/services/domainTimezoneService.ts 94.11% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

@chriskehayias chriskehayias deleted the fix/mp-datetime-handling branch May 21, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant