Skip to content
JavaScript js dates 4 min read

Date Arithmetic & Comparison

Once you can create and read dates, the next thing you’ll inevitably need is to do math with them: add a week to a due date, count the days until a deadline, or check whether one event happens before another. JavaScript has no dedicated arithmetic methods, so all of this builds on a single idea — a Date is a number of milliseconds — plus the roll-over behavior of its setters. Get those two tools right and most of the calendar gymnastics you’ll meet become straightforward.

Adding and subtracting time

There are two reliable techniques. For whole calendar units like days, months, and years, use the setters, which automatically normalize out-of-range values (adding 5 days to the 30th rolls into the next month). For fixed durations like hours, minutes, and seconds, do millisecond arithmetic on the timestamp.

// Calendar-aware: add days via the setter (handles month rollover)
function addDays(date, days) {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}

const start = new Date("2026-01-30T00:00:00Z");
console.log(addDays(start, 5).toISOString());

Output:

2026-02-04T00:00:00.000Z

The same pattern works for months and years using setMonth and setFullYear. Because the setters mutate in place, always clone the input (new Date(date)) so you don’t surprise the caller.

For fixed-length durations, defining millisecond constants keeps the intent obvious:

const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;

const meeting = new Date("2026-06-01T09:00:00Z");
const reminder = new Date(meeting.getTime() - 30 * MINUTE);
console.log(reminder.toISOString()); // 30 minutes earlier

Don’t use DAY = 24 * HOUR to add calendar days across a daylight-saving boundary. A “day” can be 23 or 25 hours long in many time zones, so millisecond math will land you on the wrong wall-clock time. Use setDate for calendar days and reserve millisecond math for true elapsed durations.

Computing differences

To find how far apart two dates are, subtract their timestamps. Subtraction coerces both operands with unary +, so you can subtract the Date objects directly and get a millisecond gap, then divide down to the unit you want.

function diffInDays(a, b) {
  const ms = Math.abs(a - b); // coercion → milliseconds
  return Math.floor(ms / (24 * 60 * 60 * 1000));
}

const checkout = new Date("2026-06-10");
const checkin = new Date("2026-06-01");
console.log(diffInDays(checkout, checkin)); // 9

Output:

9

Choose your rounding deliberately: Math.floor for completed whole units, Math.round for “nearest”, Math.ceil for “any part counts”. Here’s how the common units divide down:

UnitDivide milliseconds byExpression
Seconds1000ms / 1000
Minutes60_000ms / (60 * 1000)
Hours3_600_000ms / (60 * 60 * 1000)
Days86_400_000ms / (24 * 60 * 60 * 1000)

For calendar-aware differences (whole months or years), timestamp division lies — months have different lengths. Compute those from the components instead:

function diffInMonths(from, to) {
  return (
    (to.getFullYear() - from.getFullYear()) * 12 +
    (to.getMonth() - from.getMonth())
  );
}

console.log(diffInMonths(new Date("2026-01-15"), new Date("2026-06-15"))); // 5

Comparing dates

Relational operators (<, >, <=, >=) work directly on Date objects because they coerce to timestamps. Equality is the trap: == and === compare object references, not moments, so two distinct Date instances are never strictly equal even when they point at the same instant. Compare getTime() values instead.

const a = new Date("2026-06-01T12:00:00Z");
const b = new Date("2026-06-01T12:00:00Z");

console.log(a < b);                  // false
console.log(a > b);                  // false
console.log(a === b);                // false — different objects!
console.log(a.getTime() === b.getTime()); // true — same instant

A small comparator is handy for sorting:

const events = [
  { name: "Launch", at: new Date("2026-06-03") },
  { name: "Kickoff", at: new Date("2026-06-01") },
  { name: "Review", at: new Date("2026-06-02") },
];

events.sort((x, y) => x.at - y.at); // ascending by date
console.log(events.map((e) => e.name));

Output:

[ 'Kickoff', 'Review', 'Launch' ]

To test whether two dates fall on the same calendar day (ignoring the time of day), compare the year, month, and date components rather than the timestamps:

function isSameDay(a, b) {
  return (
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate()
  );
}

Edge cases: DST and time zones

The local setters operate in the host’s time zone, which means a “day” added across a daylight-saving transition still yields the correct wall-clock time even though the elapsed hours differ. That’s usually what you want for calendars. Pure millisecond arithmetic, by contrast, preserves elapsed time but can shift the visible hour.

// In a US zone observing DST, "spring forward" is March 8, 2026.
const before = new Date(2026, 2, 7, 12, 0, 0); // Mar 7, noon local
const next = new Date(before);
next.setDate(next.getDate() + 1);              // Mar 8, still noon local
console.log(next.getHours()); // 12 — wall-clock preserved

For correctness-critical cross-zone math, prefer the modern Temporal API or a maintained library, which model zones and durations explicitly instead of leaning on these implicit behaviors.

Best Practices

  • Add calendar days/months/years with the setters (setDate, setMonth); they roll over correctly across boundaries.
  • Reserve millisecond arithmetic for true elapsed durations, not calendar units, to avoid DST surprises.
  • Clone with new Date(original) before mutating so helper functions stay side-effect free.
  • Subtract Date objects directly (a - b) to get a millisecond gap, then divide and round deliberately.
  • Compare moments with getTime() (or unary +), never ==/===; use </> only for ordering.
  • Compute month and year differences from components — never by dividing milliseconds.
  • For anything spanning time zones or DST, reach for Temporal or a dedicated date library.
Last updated June 1, 2026
Was this helpful?