# Booking confirmation and calendar agree on displayed appointment time

- **Pattern:** `ab-000531` (`booking-calendar-availability.availability-logic.confirmation-calendar-parity`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000531
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000531)

## Why it matters

When the booking confirmation email says "2 PM EST" and the calendar block says "7 PM UTC", the customer has no reliable signal about when to show up. This ISO 25010 functional-correctness failure typically stems from one component reading the raw UTC value while another converts it — two code paths, two different timezone assumptions, same appointment. The damage is a no-show or a wrong-time arrival, followed by a refund request and a support ticket. Confirmation-calendar parity is not cosmetic: it is the contract the customer relies on to plan their day.

## Severity rationale

Low because the mismatch does not corrupt stored data, but it causes customers to appear at the wrong time, producing no-shows and direct refund liability.

## Remediation

Extract time formatting into a single shared utility function and call it from both the confirmation display and the calendar view — never format the same appointment time twice from different code paths.

```ts
// src/lib/format-appointment-time.ts
import { toZonedTime, format } from 'date-fns-tz'

export function formatAppointmentTime(utcDate: Date, tz: string): string {
  return format(toZonedTime(utcDate, tz), 'MMM d, yyyy h:mm a zzz', { timeZone: tz })
}
```

In `src/components/BookingConfirmation.tsx` and your confirmation email template in `src/lib/email.ts`, import and call `formatAppointmentTime(appointment.start_time, appointment.customer_timezone)`. Remove any inline `new Date().toLocaleString()` calls that bypass the shared formatter.

## Detection

- **ID:** `confirmation-calendar-parity`
- **Severity:** `low`
- **What to look for:** Count all booking confirmation display locations: confirmation modal, confirmation page, confirmation email template. For each, compare the time formatting function used against the calendar's time formatting. Both must use the same timezone conversion (same IANA library call, same timezone source) and the same date format string. Examine files matching `src/components/*Confirmation*`, `src/components/*BookingSuccess*`, `src/lib/*email*`, `src/app/api/*confirm*`.
- **Pass criteria:** Count all confirmation display locations. 100% of confirmation displays must use the same time formatting function and timezone source as the calendar. At least 1 confirmation mechanism must exist (modal, page, or email). The formatted time strings must be identical between calendar and confirmation for the same appointment. Report: "X of Y confirmation displays match calendar timezone and format."
- **Fail criteria:** Confirmation shows one time and calendar shows a different time (e.g., confirmation shows local time, calendar shows UTC), or different formatting functions are used.
- **Skip (N/A) when:** Platform does not send or display booking confirmations.
- **Detail on fail:** Example: `"Confirmation email says '2 PM EST', but calendar shows '7 PM UTC'. Customer books thinking it's 2 PM but the appointment is actually 7 PM UTC (2 PM EST), causing confusion."`
- **Cross-reference:** Check `customer-timezone-display` — both calendar and confirmation must convert to the viewer's local timezone.
- **Cross-reference:** Check `utc-storage` — parity issues typically stem from one component reading raw UTC while the other converts.
- **Cross-reference:** Check `live-availability-update` — after booking, the updated calendar slot time must match the confirmation.
- **Remediation:** Ensure both confirmation and calendar use the same time source:

  ```tsx
  async function showConfirmation(appointment) {
    const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone
    const displayTime = toZonedTime(appointment.start_time, userTz)
    const formattedTime = format(displayTime, 'MMM d, yyyy h:mm a zzz')

    // Display in modal
    showModal(`Confirmed: ${formattedTime}`)

    // Send same data to confirmation email
    await sendConfirmationEmail(appointment.customer_email, {
      appointmentTime: formattedTime,
      appointmentUTC: format(appointment.start_time, 'yyyy-MM-dd HH:mm:ss zzz'),
    })
  }
  ```

## External references

- iso-25010 functional-correctness

Taxons: data-integrity

HTML version: https://auditbuffet.com/patterns/ab-000531
