How to Subtract Dates in TypeScript (With Examples)

Subtracting dates in TypeScript comes down to one move: call getTime() on both Date objects and subtract the numbers. The result is the gap in milliseconds, which you divide down to seconds, hours or days. Writing end - start directly won’t compile in TypeScript:

const ms = checkOut.getTime() - checkIn.getTime();

In this tutorial, we’ll subtract dates in TypeScript step by step. Every snippet was compiled in strict mode with TypeScript 7.0.2 and Node.js v22.22.2 on a machine set to US Eastern time, and I pasted the console output straight from those runs.

Why can’t you subtract two Date objects in TypeScript?

JavaScript quietly converts both dates to numbers in checkOut - checkIn. TypeScript won’t do that for a Date, so the build stops:

const checkIn = new Date(2026, 6, 3);    // July 3, 2026
const checkOut = new Date(2026, 6, 7);   // July 7, 2026

const gap = checkOut - checkIn;

Compiler output:

sub_minus.ts(4,13): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
sub_minus.ts(4,24): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
Command Prompt output of tsc rejecting subtraction of two Date objects with error TS2362 and TS2363
The compiler flags both sides of the minus sign: each operand must be a number, bigint or enum.

Hand TypeScript a number yourself. All three forms below return the same milliseconds:

const checkIn = new Date(2026, 6, 3);    // July 3, 2026
const checkOut = new Date(2026, 6, 7);   // July 7, 2026

console.log("getTime():", checkOut.getTime() - checkIn.getTime());
console.log("valueOf():", checkOut.valueOf() - checkIn.valueOf());
console.log("unary + :", +checkOut - +checkIn);
console.log("nights:  ", (checkOut.getTime() - checkIn.getTime()) / 86_400_000);

Output:

getTime(): 345600000
valueOf(): 345600000
unary + : 345600000
nights:   4

I prefer getTime() because it reads clearly. If you only need whole days between two dates, my guide to calculating the date difference in days in TypeScript goes deeper on rounding.

Turn a date difference into days, hours and minutes

A raw 206,100,000 means nothing to a user. Divide by 3,600,000 for hours or 60,000 for minutes, or peel off days, hours and minutes with the remainder operator:

const start = new Date(2026, 10, 3, 8, 30);    // Nov 3, 2026, 8:30 AM
const end = new Date(2026, 10, 5, 17, 45);     // Nov 5, 2026, 5:45 PM

const ms = end.getTime() - start.getTime();
console.log("hours:  ", ms / 3_600_000);
console.log("minutes:", ms / 60_000);

function breakdown(ms: number): string {
  const sign = ms < 0 ? "-" : "";
  let rest = Math.abs(ms);
  const days = Math.floor(rest / 86_400_000);
  rest %= 86_400_000;
  const hours = Math.floor(rest / 3_600_000);
  rest %= 3_600_000;
  const minutes = Math.floor(rest / 60_000);
  return `${sign}${days} days ${hours} hours ${minutes} minutes`;
}

console.log("elapsed:", breakdown(ms));
console.log("reverse:", breakdown(start.getTime() - end.getTime()));

Output:

hours:   57.25
minutes: 3435
elapsed: 2 days 9 hours 15 minutes
reverse: -2 days 9 hours 15 minutes
Command Prompt output of a TypeScript function that breaks the difference between two dates into days, hours and minutes
The same gap shown as total hours, total minutes, and a days-hours-minutes label, plus a negative label when the dates are reversed.

Keeping the sign separate stops a past deadline from showing odd negative parts. If timestamps come from an API, read getting the current date in milliseconds to see how epoch values work.

Why is my date subtraction off by an hour?

Daylight saving time is the usual culprit. In 2026, US clocks jump forward on March 8, so the night between March 7 and March 9 is one hour shorter in local time:

const before = new Date(2026, 2, 7, 12, 0);   // Mar 7, 2026, noon
const after = new Date(2026, 2, 9, 12, 0);    // Mar 9, 2026, noon (DST began Mar 8)

const hours = (after.getTime() - before.getTime()) / 3_600_000;
console.log("hours between:", hours);
console.log("Math.floor days:", Math.floor(hours / 24));

const utc = (d: Date) => Date.UTC(d.getFullYear(), d.getMonth(), d.getDate());
console.log("calendar days:", (utc(after) - utc(before)) / 86_400_000);

Output:

hours between: 47
Math.floor days: 1
calendar days: 2

Only 47 hours pass, so Math.floor says one day. Date.UTC() on the calendar parts ignores the clock change. The same idea powers comparing dates without time in TypeScript.

Subtract months or years from a date in TypeScript

Sometimes “subtract dates” means moving one date back, like “one month before March 31.” For days, setDate(getDate() - n) is enough, as shown in subtracting days from a date in TypeScript. Months are trickier because they have different lengths:

const us = (d: Date) => d.toLocaleDateString("en-US");

const naive = new Date(2026, 2, 31);          // March 31, 2026
naive.setMonth(naive.getMonth() - 1);
console.log("setMonth - 1:", us(naive));

function subtractMonths(date: Date, months: number): Date {
  const d = new Date(date);
  const day = d.getDate();
  d.setDate(1);
  d.setMonth(d.getMonth() - months);
  const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
  d.setDate(Math.min(day, lastDay));
  return d;
}

console.log("clamped:     ", us(subtractMonths(new Date(2026, 2, 31), 1)));
console.log("minus 1 year:", us(subtractMonths(new Date(2028, 1, 29), 12)));

Output:

setMonth - 1: 3/3/2026
clamped:      2/28/2026
minus 1 year: 2/28/2027
Command Prompt output showing setMonth rolling March 31, 2026 minus one month to March 3, and a clamped helper returning February 28
Plain setMonth() overflows into March, while the clamped helper lands on the last day of February, even for a leap day.

February 31 doesn’t exist, so setMonth() rolls forward three days. The helper moves the month from day 1, then restores the original day or the month’s last day, whichever is smaller.

Count full months or years between two dates

Months aren’t a fixed length, so milliseconds can’t count them. Compare the year and month numbers, then subtract one if the end day hasn’t reached the start day. Ages work the same way:

function fullMonthsBetween(start: Date, end: Date): number {
  let months = (end.getFullYear() - start.getFullYear()) * 12 + (end.getMonth() - start.getMonth());
  if (end.getDate() < start.getDate()) months--;   // last month not finished yet
  return months;
}

function age(birth: Date, today: Date): number {
  return Math.floor(fullMonthsBetween(birth, today) / 12);
}

const today = new Date(2026, 9, 27);                     // Oct 27, 2026
console.log("plan months:", fullMonthsBetween(new Date(2026, 0, 31), today));
console.log("age:        ", age(new Date(1990, 6, 4), today));
console.log("age (Nov 9):", age(new Date(1990, 10, 9), today));

Output:

plan months: 8
age:         36
age (Nov 9): 35

A plan that began January 31 has 8 full months on October 27, not 9, since October 31 hasn’t arrived. Before you trust any input date, run it through a check for invalid dates in TypeScript, since an Invalid Date returns NaN here.

Subtract dates with date-fns

If your project already uses date-fns, its typed functions handle these cases. I ran the same shift times through date-fns 4:

import { differenceInHours, formatDuration, intervalToDuration, subMonths } from "date-fns";

const start = new Date(2026, 10, 3, 8, 30);    // Nov 3, 2026, 8:30 AM
const end = new Date(2026, 10, 5, 17, 45);     // Nov 5, 2026, 5:45 PM

console.log("differenceInHours:", differenceInHours(end, start));
console.log("intervalToDuration:", JSON.stringify(intervalToDuration({ start, end })));
console.log("formatDuration:", formatDuration(intervalToDuration({ start, end })));
console.log("subMonths:", subMonths(new Date(2026, 2, 31), 1).toLocaleDateString("en-US"));

Output:

differenceInHours: 57
intervalToDuration: {"days":2,"hours":9,"minutes":15}
formatDuration: 2 days 9 hours 15 minutes
subMonths: 2/28/2026
Command Prompt output of date-fns differenceInHours, intervalToDuration, formatDuration and subMonths in TypeScript
date-fns returns the whole hours, a duration object, a ready-made English label and a clamped one-month-back date.

subMonths clamps to February 28 just like the hand-written helper. intervalToDuration leaves out zero parts, so check for missing keys before you read them. To print the dates themselves, see formatting dates as yyyy-mm-dd in TypeScript.

Which date subtraction method should you use?

TaskUseWatch out for
Gap in ms, hours or minutesend.getTime() - start.getTime()TS2362 if you subtract Date objects
Readable elapsed timeDays/hours/minutes breakdownNegative gaps
Whole calendar daysDate.UTC() on both datesDST makes local days 23 or 25 hours
One date minus N monthsClamped subtractMonths() or subMonthssetMonth() overflow on the 29th-31st
Full months or ageYear and month mathSubtract 1 if the end day is earlier

Date math rarely stops at subtraction, so here are four related TypeScript walkthroughs:

Frequently asked questions

How do I subtract two dates in TypeScript?

Use end.getTime() - start.getTime() for milliseconds, then divide by 86,400,000 for days.

Why does TypeScript give error TS2362 when subtracting dates?

Arithmetic operands must be numbers, bigints or enums, and a Date isn’t. Call getTime() first.

How do I get hours and minutes between two dates?

Subtract the getTime() values, then split the milliseconds with Math.floor and %.

How do I subtract one month from a date without skipping ahead?<