Skip to main content

At most one date library

ab-000229 · ai-slop-code-drift.tooling-stack-drift.dual-date-library
Severity: mediumactive

Why it matters

Two date libraries mean two timezone-handling strategies, two parsing APIs, and two bundle payloads. date-fns treats dates as immutable UTC-first while dayjs mirrors Moment's mutable-looking API — code that mixes both produces DST-transition bugs that only fire twice a year. Formatting output drifts between components (one says "Jan 5" the other says "1/5"), and locale handling requires loading the right locale pack into the right library or date strings silently fall back to English.

Severity rationale

Medium because DST and timezone bugs are real correctness issues, but they're bounded to date-display and scheduling features.

Remediation

Wrap your chosen library in src/utils/dates.ts and import only that wrapper from the rest of the codebase.

// src/utils/dates.ts
import { format, parseISO } from 'date-fns'
export const formatDate = (d: Date) => format(d, 'yyyy-MM-dd')
export const parseDate = (s: string) => parseISO(s)

Rewrite every dayjs call site, then npm uninstall dayjs. Add an ESLint rule forbidding direct imports of the removed library anywhere outside the wrapper module to prevent regression.

Detection

  • ID: ai-slop-code-drift.tooling-stack-drift.dual-date-library

  • Severity: medium

  • What to look for: Apply the three-condition rule against this exact date library allowlist: date-fns, dayjs, luxon, moment, @js-joda/core, temporal-polyfill, js-time-ago, time-fns. Note: native Date and the upcoming Temporal API are NOT packages — do not count them. Count all REMAINING libraries that meet all three conditions (at least 1 non-escape-hatch importing file).

  • Pass criteria: 0 or 1 date libraries from the allowlist actively used. Report even on pass: "Canonical date library: [name or 'native Date'] ([N] importing files)."

  • Fail criteria: 2 or more date libraries from the allowlist meet all three conditions (at least 1 non-escape-hatch importing file).

  • Skip (N/A) when: 0 date libraries from the allowlist appear in RUNTIME_DEPS.

  • Detail on fail: "2 active date libraries: 'date-fns' (14 files) AND 'dayjs' (6 files). Pick one for date handling."

  • Remediation: Two date libraries means two timezone behaviors, two formatting APIs, two parsing strategies — and bugs that only show up at DST transitions. Pick one and migrate:

    // Bad: src/utils/format-date.ts uses date-fns, src/utils/parse-date.ts uses dayjs
    // Good: a single src/utils/dates.ts that wraps your chosen library
    
    // src/utils/dates.ts
    import { format, parseISO } from 'date-fns'
    export const formatDate = (d: Date) => format(d, 'yyyy-MM-dd')
    

Taxons

History