wulf-pulse/.planning/phases/07.1-user-timezone-fix-inserted-urgent/07.1-04-AUDIT.md
lorentz dfd0a9f2b2 docs(07.1-04): codebase-wide tz audit + Plan 05 dispatch
- 81 leak callsites across 39 files identified — Plan 05 closes them
- 47 number-format callsites (not dates) excluded
- 7 server-side LLM prompt callsites out of scope
- 1 deliberate-UTC callsite (engagement sparkline) leave as-is
- 5 explicit-zone callsites (Plan 04 already migrated)
- Plan 05 dispatch: NEEDED — file paths enumerated for Plan 05's files_modified
2026-05-07 07:56:32 -04:00

15 KiB

Phase 7.1 — Codebase-wide tz audit (Plan 04 Task 3)

Discovery date: 2026-05-07 Excluded: app/mobile/finance/page.tsx, app/mobile/tickets/[id]/page.tsx, components/ui/calendar.tsx, app/admin/data-browser/time-entries/page.tsx.backup (orphaned backup file), node_modules/**

Discovery grep:

grep -rEn "Intl\.DateTimeFormat|\.toLocaleDateString\(|\.toLocaleTimeString\(|\.toLocaleString\(" \
  app components lib/hooks

Leak callsites (must migrate via Plan 05)

Each row is a Date.toLocale* callsite with NO timeZone: option in the same call. These render in the browser's local zone — the bug TZ-02 is patching.

File Line Snippet Notes
app/engagement/profile/page.tsx 141 d.toLocaleDateString('en-US', { month: 'short' }) sparkline label
app/engagement/profile/page.tsx 264 new Date(m + '-02').toLocaleDateString('en-US', { month: 'short', year: 'numeric' }) month label
app/engagement/page.tsx 842 new Date(user.lastActivity).toLocaleDateString() last activity
app/engagement/page.tsx 1010 new Date(snap.last_activity_date).toLocaleDateString() snapshot last activity
app/engagement/page.tsx 1106 d.toLocaleDateString() + d.toLocaleTimeString([], …) call timestamp (2 calls on one line)
app/engagement/page.tsx 1225 new Date(call.startTime).toLocaleDateString() call start
app/engagement/page.tsx 1253 d.toLocaleDateString() + d.toLocaleTimeString([], …) call timestamp (2 calls on one line)
app/admin/sync/duo/page.tsx 117 new Date(d).toLocaleString() sync timestamp
app/admin/sync/datto-rmm/page.tsx 23 new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }) sync timestamp
app/admin/sync/veeam/page.tsx 26 new Date(d).toLocaleString(undefined, { … }) sync timestamp
app/admin/sync/itglue/page.tsx 24 new Date(d).toLocaleString(undefined, { … }) sync timestamp
app/admin/sync/mimecast/page.tsx 28 new Date(d).toLocaleString(undefined, { … }) sync timestamp
app/admin/sync/mimecast/page.tsx 651 new Date(message.dateReceived).toLocaleString() message timestamp
app/admin/sync/mimecast/page.tsx 924 new Date(m.dateReceived).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) message timestamp
app/admin/sync/mimecast/page.tsx 1239 new Date(message.received).toLocaleString() message timestamp
app/admin/sync/mimecast/page.tsx 1405 new Date(m.receivedDateTime).toLocaleString(undefined, { … }) message timestamp
app/admin/sync/mimecast/page.tsx 1840 new Date(m.received).toLocaleString(undefined, { … }) message timestamp
app/admin/sync/sentinelone/page.tsx 15 new Date(d).toLocaleString() sync timestamp
app/admin/zabbix-wan/page.tsx 212 new Date(ts).toLocaleString() last synced
app/admin/zabbix-wan/page.tsx 213 new Date(ts).toLocaleString() last synced
app/admin/rmm-overshell/page.tsx 142 new Date(settings.discoveredAt).toLocaleString() discovered timestamp
app/admin/rmm-overshell/page.tsx 199 new Date(settings.logliftDiscoveredAt).toLocaleString() discovered timestamp
app/admin/rmm-overshell/page.tsx 265 new Date(e.queuedAt).toLocaleString() queued timestamp
app/admin/itglue-writes/page.tsx 144 new Date(w.performed_at).toLocaleString() performed timestamp
app/admin/data-browser/tasks/page.tsx 100 new Date(value).toLocaleDateString() DataTable column render
app/admin/data-browser/projects/page.tsx 88 new Date(value).toLocaleDateString() DataTable column render
app/admin/data-browser/ticket-notes/page.tsx 86 new Date(v).toLocaleDateString() DataTable column render
app/admin/data-browser/contracts/page.tsx 88 new Date(value).toLocaleDateString() DataTable column render
app/admin/data-browser/contracts/page.tsx 96 new Date(value).toLocaleDateString() DataTable column render
app/admin/data-browser/tickets/page.tsx 103 new Date(value).toLocaleDateString() DataTable column render
app/admin/data-browser/time-entries/page.tsx 296 new Date(value).toLocaleDateString() DataTable column render
app/admin/ticket-digest/page.tsx 410 new Date(report.generated_at).toLocaleString() report timestamp
app/admin/device-link-conflicts/page.tsx 223 new Date(r.xref.lastSeenAt).toLocaleString() last seen
app/admin/workflow/history/page.tsx 192 new Date(exec.created_at).toLocaleString() execution timestamp
app/admin/workflow/pipelines/[id]/page.tsx 582 new Date(exec.started_at).toLocaleString() execution timestamp
app/analyzer/itglue/applications/page.tsx 129 new Date(r.latestAudit.generatedAt).toLocaleDateString() audit timestamp
app/analyzer/itglue/applications/[id]/page.tsx 411 new Date(audit.generated_at).toLocaleString() audit timestamp
app/analyzer/itglue/applications/[id]/page.tsx 720 new Date(w.performed_at).toLocaleString() write performed
app/analyzer/itglue/applications/[id]/page.tsx 779 new Date(h.generated_at).toLocaleString() history timestamp
app/analyzer/itglue/configurations/page.tsx 130 new Date(r.latestAudit.generatedAt).toLocaleDateString() audit timestamp
app/analyzer/itglue/configurations/[id]/page.tsx 393 new Date(audit.generated_at).toLocaleString() audit timestamp
app/analyzer/itglue/configurations/[id]/page.tsx 688 new Date(w.performed_at).toLocaleString() write performed
app/analyzer/itglue/configurations/[id]/page.tsx 741 new Date(h.generated_at).toLocaleString() history timestamp
app/analyzer/itglue/sites/[companyId]/page.tsx 212 new Date(e.queuedAt).toLocaleString() queued timestamp
app/analyzer/queue/page.tsx 103 new Date(a.triggeredAt).toLocaleString() triggered timestamp
app/analyzer/ticket/[ticketNumber]/page.tsx 143 new Date(a.triggeredAt).toLocaleString() triggered timestamp
app/analyzer/tickets/page.tsx 112 d.toLocaleDateString() helper
app/analyzer/reports/page.tsx 136 new Date(r.generatedAt).toLocaleString() report timestamp
app/analyzer/reports/[id]/page.tsx 186 new Date(report.generatedAt).toLocaleString() report timestamp
app/analyzer/reports/[id]/page.tsx 199 new Date(report.dateRangeActual.earliest).toLocaleDateString() range earliest
app/analyzer/reports/[id]/page.tsx 201 new Date(report.dateRangeActual.latest).toLocaleDateString() range latest
app/dashboard/page.tsx 152 new Date().toLocaleDateString(undefined, { … }) header date
app/quotes/page.tsx 107 new Date(dateString).toLocaleDateString('en-US', { … }) helper
app/veeam-analysis/page.tsx 671 new Date(summary.generated_at).toLocaleString() generated timestamp
components/admin/DetailModal.tsx 160 d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }) detail row
components/admin/DetailModal.tsx 610 new Date(entry.entry_date).toLocaleDateString(undefined, { … }) time-entry date
components/admin/DetailModal.tsx 656 new Date(note.create_date_time).toLocaleDateString(undefined, { … }) note date
components/admin/DetailModal.tsx 658 new Date(note.create_date_time).toLocaleTimeString(undefined, { … }) note time
components/admin/IntegrationStatusTabs.tsx 36 new Date(d).toLocaleString(undefined, { … }) helper
components/admin/SyncScheduler.tsx 230 new Date(dateString).toLocaleString() scheduler
components/admin/audit/audit-log-table.tsx 186 new Date(log.timestamp).toLocaleString() audit log row
components/admin/users/user-table.tsx 205 new Date(user.created_at).toLocaleDateString() user row
components/admin/users/user-sessions.tsx 161 new Date(session.created_at).toLocaleString() session row
components/admin/users/user-sessions.tsx 164 new Date(session.expires_at).toLocaleString() session row
components/analyzer/analysis-view.tsx 115 new Date(a.triggeredAt).toLocaleString() analysis header
components/analyzer/analysis-view.tsx 222 new Date(event.timestamp).toLocaleString() event timestamp
components/analyzer/analysis-view.tsx 310 new Date(ts).toLocaleString() event timestamp
components/analyzer/analysis-view.tsx 401 new Date(a.timeline[expandedEvent].timestamp).toLocaleString() event timestamp
components/settings/active-sessions.tsx 118 new Date(session.created_at).toLocaleDateString() session row
components/dashboard/resolution-trend.tsx 27 new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) chart axis label
components/dashboard/volume-trend.tsx 28 new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) chart axis label
components/quotes/ticket-detail-modal.tsx 231 new Date(dateString).toLocaleString('en-US', { … }) helper
components/analytics/TimelineView.tsx 133 new Date(groupKey + ':00:00').toLocaleString('en-US', { … }) hour bucket
components/analytics/TimelineView.tsx 140 new Date(groupKey).toLocaleDateString('en-US', { … }) day bucket
components/analytics/TimelineView.tsx 148 new Date(groupKey + '-01').toLocaleDateString('en-US', { … }) month bucket
components/analytics/TimelineView.tsx 303 new Date(event.timestamp).toLocaleTimeString() event time
components/analytics/ScoreCard.tsx 486 analysis.dateRange.latest.toLocaleDateString() range latest
components/configuration-items/auvik-tab.tsx 26 new Date(dateString).toLocaleString() helper
components/configuration-items/addigy-tab.tsx 107 new Date(device['Last Check In']).toLocaleString() device check-in
components/configuration-items/addigy-tab.tsx 327 new Date(device['Warranty Expiration Date']).toLocaleDateString() warranty date
components/status/activity-sparkline.tsx 30 new Date(iso).toLocaleTimeString(undefined, { … }) tooltip label
components/backup/compliance-detail-table.tsx 170 new Date(contract.start_date).toLocaleDateString() contract start
components/backup/compliance-detail-table.tsx 173 new Date(contract.end_date).toLocaleDateString() contract end
components/backup/company-backup-detail.tsx 48 new Date(dateStr).toLocaleString() helper

Explicit-zone callsites (no migration)

File Line Snippet
app/mobile/finance/page.tsx 44, 77, 375 already migrated by Plan 04 Task 2 (excluded from this audit)
app/mobile/tickets/[id]/page.tsx 50 already migrated by Plan 04 Task 2 (excluded from this audit)
lib/hooks/use-user-timezone.ts 60 helper threading caller-supplied timeZone

Number-format callsites (not date — ignore)

These are Number.prototype.toLocaleString() calls — thousands separators on numeric values, NOT date callsites.

Count: 47

Examples (representative, not exhaustive):

  • app/admin/page.tsx:131counts.linkConflicts.toLocaleString()
  • app/admin/sync/page.tsx:344,348,360,378,396,400,404 — sync count formatters
  • app/admin/sync/duo/page.tsx:281value.toLocaleString()
  • app/admin/sync/veeam/page.tsx:162,177 — record counts
  • app/admin/sync/itglue/page.tsx:116-125,150,194 — stat counts
  • app/admin/sync/mimecast/page.tsx:35,866,901,903,905 — count formatters
  • app/admin/sync/sentinelone/page.tsx:100,123,137,165 — record counts
  • app/admin/data-browser/ticket-notes/page.tsx:180,181 — total counts
  • app/admin/data-browser/time-entries/page.tsx:494,496 — total counts
  • app/admin/qbo/page.tsx:43n.toLocaleString() helper
  • app/analyzer/tickets/page.tsx:744total.toLocaleString()
  • app/dashboard/page.tsx:426,427 — stat counts
  • app/sentinelone/coverage/page.tsx:96Number(value).toLocaleString()
  • components/admin/SyncDashboard.tsx:126,132,138 — record counts
  • components/admin/EntitySyncProgress.tsx:219,239 — total records
  • components/admin/ChunkedSyncProgress.tsx:117,131,166 — chunk counts
  • components/analyzer/analysis-view.tsx:118,119 — token counts
  • components/dashboard/kpi-card.tsx:60value.toLocaleString()
  • components/mobile/KpiCardMobile.tsx:29value.toLocaleString() (number branch)

Server-side callsites (out of scope)

These run in Node (api routes), not React components. Out of scope for the client hook. Today the only such callsites are the analyzer prompt builders — deliberately locale-only (LLM input).

File Line Reason
app/api/veeam/rpo-analyze/route.ts 78, 108, 109 LLM prompt builder — locale-only by design
app/api/veeam/ticket-analysis/run/route.ts 78, 90, 97, 98 LLM prompt builder — locale-only by design

Deliberate UTC callsites

Files that already pass { timeZone: 'UTC' } for a specific reason. Leave as-is.

File Line Reason
components/mobile/EngagementHoursSparkline.tsx 39 UTC pin for sparkline shape (not a clock) — data is daily UTC buckets

Summary

  • Leak count: 81 (rows in the leak table above; 81 distinct callsites across 39 files)
  • Explicit-zone count: 5 (3 finance + 1 tickets + 1 hook helper)
  • Server-side count: 7 (3 in rpo-analyze route + 4 in ticket-analysis route)
  • Deliberate-UTC count: 1 (EngagementHoursSparkline)
  • Number-format count: 47 (ignored — not date callsites)

Plan 05 dispatch

  • Leak count > 0: Plan 05 (sibling, depends_on 07.1-04) IS NEEDED.
  • Plan 05's files_modified is the unique set of leak file paths above (39 files).
  • Plan 05 must:
    1. Add useUserTimezone() (or its server equivalent for non-client files) to each of the 39 files.
    2. Thread tz into every leak callsite listed above so the count of timeZone: ≥ count of toLocaleDateString( + toLocaleString( + toLocaleTimeString( callsites in each file.
    3. Skip the four file groups documented as out-of-scope (Number-format / Server-side / Deliberate-UTC / already-migrated).
    4. Verify with the same positive-assertion grep approach Plan 04 used.

Note: The app/admin/data-browser/time-entries/page.tsx.backup file is an orphaned backup (no .backup suffix is part of the active app). It contains a leak at line 166 but is unreachable. Plan 05 should delete the file (not migrate it) — confirm by running git log -- app/admin/data-browser/time-entries/page.tsx.backup before deletion.