Timezone conversion uses IANA library; no manual offset arithmetic
Why it matters
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.
Severity rationale
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.
Remediation
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.
Detection
-
ID:
iana-timezone-library -
Severity:
high -
What to look for: Count all timezone conversion calls in the codebase. Search
package.jsondependencies 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 by60 * 60 * 1000near a timezone variable. Examine files matchingsrc/lib/*timezone*,src/lib/*date*,src/utils/*time*, and all files importingDate. -
Pass criteria: Count all timezone conversion call sites. At least 1 IANA-aware library must be listed in
package.jsondependencies (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 inpackage.json. -
Do NOT pass when: An IANA library is installed in
package.jsonbut 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"
External references
- cwe · CWE-682 — Incorrect Calculation — manual offset arithmetic produces wrong times during DST transitions
- iso-25010:2011 · functional-correctness — Functional Correctness — timezone conversion must produce accurate results across DST boundaries
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-calendar-availability·automated