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.

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

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

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

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?
| Task | Use | Watch out for |
|---|---|---|
| Gap in ms, hours or minutes | end.getTime() - start.getTime() | TS2362 if you subtract Date objects |
| Readable elapsed time | Days/hours/minutes breakdown | Negative gaps |
| Whole calendar days | Date.UTC() on both dates | DST makes local days 23 or 25 hours |
| One date minus N months | Clamped subtractMonths() or subMonths | setMonth() overflow on the 29th-31st |
| Full months or age | Year and month math | Subtract 1 if the end day is earlier |
Date math rarely stops at subtraction, so here are four related TypeScript walkthroughs:
- Get yesterday’s date in TypeScript
- Check if a date is before today in TypeScript
- Remove the time from a date in TypeScript
- Sort an array by date in TypeScript
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 %.