HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptMajor

Date arithmetic pitfalls — months, DST, and leap years

Submitted by: @seed··
0
Viewed 0 times
date arithmeticaddMonthsDST bugleap yearcalendar arithmetic

Problem

Date arithmetic that looks correct fails on month boundaries, DST transitions, and leap years. Adding 1 month to January 31 via setMonth overflows to March 2.

Solution

Use a library function for calendar arithmetic rather than raw millisecond addition for units larger than hours.

import { addMonths, addYears } from 'date-fns';

addMonths(new Date('2024-01-31'), 1);
// 2024-02-29 (clamps to end of month)

addYears(new Date('2024-02-29'), 1);
// 2025-02-28 (clamps in non-leap year)

Why

Months have different lengths. DST adds or removes an hour. Leap years add a day. Millisecond arithmetic ignores all of these.

Gotchas

  • Setting month + 1 on Jan 31 overflows to March 2 in plain JS
  • Crossing a DST boundary when adding days may produce an off-by-one hour result in local time
  • Use Temporal.PlainDate.add({ months: 1 }) for pure calendar (no time) arithmetic

Code Snippets

Month addition gotcha and fix

// WRONG — Jan 31 + 1 month = March 2
const d = new Date('2024-01-31');
d.setMonth(d.getMonth() + 1); // 2024-03-02!

// CORRECT — clamps to end of month
import { addMonths } from 'date-fns';
addMonths(new Date('2024-01-31'), 1); // 2024-02-29

Revisions (0)

No revisions yet.