Manual offset arithmetic like date.getTime() + 5 * 3600000 hard-codes a fixed UTC offset and silently breaks twice a year during DST transitions. CWE-682 (Incorrect Calculation) and ISO 25010 functional correctness both flag this: New York is UTC-5 in January and UTC-4 in July — a hard-coded -5 produces a one-hour appointment time error for every booking during EDT. IANA libraries carry the full historical and future timezone rule database; manual arithmetic carries none of it. A single DST-related booking error can generate dozens of no-shows before anyone notices the pattern.
High because manual offset arithmetic silently produces one-hour appointment time errors on every DST transition, affecting all bookings during a roughly five-month EDT window without any visible error.
Replace all manual offset arithmetic with calls to an IANA-aware library. Install date-fns-tz (or luxon) and use its conversion functions exclusively throughout src/lib/.
// Bad — breaks during DST
const local = new Date(utc.getTime() - 5 * 3_600_000) // hardcoded EST offset
// Good — IANA library handles DST automatically
import { toZonedTime, fromZonedTime } from 'date-fns-tz'
const tz = 'America/New_York'
const localTime = toZonedTime(utcDate, tz) // display: UTC → local
const utcTime = fromZonedTime(localDate, tz) // store: local → UTC
Search src/ for the pattern * 3600000 or * 3_600_000 near timezone variables and replace each instance. Confirm date-fns-tz or equivalent appears in package.json dependencies, not just devDependencies.
ID: booking-calendar-availability.availability-logic.iana-timezone-library
Severity: high
What to look for: Count all timezone conversion calls in the codebase. Search package.json dependencies for IANA-aware libraries: date-fns-tz, luxon, moment-timezone, @date-fns/timezone, dayjs-plugin-utc, dayjs-plugin-timezone. Also search all source files for manual offset arithmetic patterns: getTime() + ... * 3600000, getHours() + offset, setHours(hours + tz), or any multiplication by 60 * 60 * 1000 near a timezone variable. Examine files matching src/lib/*timezone*, src/lib/*date*, src/utils/*time*, and all files importing Date.
Pass criteria: Count all timezone conversion call sites. At least 1 IANA-aware library must be listed in package.json dependencies (e.g., date-fns-tz, luxon, moment-timezone). No more than 0 manual offset arithmetic patterns may exist (e.g., getTime() + hours * 3600000). 100% of timezone conversions must use the IANA library. Report: "IANA library: [name]. Manual arithmetic instances: [count]."
Fail criteria: Timezone conversions are done manually with offset arithmetic (e.g., date.getTime() + hours * 3600000), or no IANA-aware timezone library is in package.json.
Do NOT pass when: An IANA library is installed in package.json but manual offset arithmetic also exists elsewhere in the codebase. The presence of the library alone is insufficient — it must be used consistently everywhere.
Skip (N/A) when: Platform does not handle multiple timezones and has no timezone conversion code.
Detail on fail: Example: "Code does: const estTime = new Date(utcTime.getTime() + 5 * 60 * 60 * 1000). This breaks when DST transitions — EST is UTC-5 in winter, EDT is UTC-4 in summer."
Cross-reference: Check utc-storage — times stored in UTC still require IANA library for display conversion.
Cross-reference: Check customer-timezone-display — the IANA library is what powers correct per-viewer timezone conversion.
Cross-reference: Check dst-handling — IANA libraries automatically handle DST; manual arithmetic does not.
Remediation: Use an IANA library:
// Bad: manual offset arithmetic
// const localTime = new Date(utcTime.getTime() - 5 * 60 * 60 * 1000) // Wrong during DST
// Good: use date-fns-tz
import { toZonedTime, fromZonedTime } from 'date-fns-tz'
const timeZone = 'America/New_York'
const utcTime = new Date('2024-02-15T14:00:00Z')
const localTime = toZonedTime(utcTime, timeZone) // Correct: 9:00 AM EST (UTC-5)
// Or use Luxon
import { DateTime } from 'luxon'
const dt = DateTime.fromISO('2024-02-15T14:00:00Z')
.setZone('America/New_York')
.toLocaleString() // Correct: "2/15/2024, 9:00 AM"