Counting days between dates in TypeScript takes one line: subtract the two getTime() values and divide by 86,400,000, the number of milliseconds in a day. Wrap it in Math.round() so a daylight saving shift can’t leave you with 13.96 days:
const days = Math.round((end.getTime() - start.getTime()) / 86_400_000);
In this tutorial, we’ll count the days between dates in TypeScript the safe way, from a small helper to business days. Every file ran in strict mode on TypeScript 7.0.2 and Node.js v22.22.2, with the clock on New York time.
Calculate days between two dates with getTime()
Here’s the helper I reach for first. It returns a signed number, so a start date that comes after the end date gives a negative count:
const MS_PER_DAY = 1000 * 60 * 60 * 24;
function daysBetween(start: Date, end: Date): number {
return Math.round((end.getTime() - start.getTime()) / MS_PER_DAY);
}
const signed = new Date(2026, 5, 1); // June 1, 2026
const starts = new Date(2026, 6, 15); // July 15, 2026
console.log("June 1 -> July 15:", daysBetween(signed, starts));
console.log("July 15 -> June 1:", daysBetween(starts, signed));
console.log("absolute value: ", Math.abs(daysBetween(starts, signed)));
Output:
June 1 -> July 15: 44
July 15 -> June 1: -44
absolute value: 44

Math.abs().Use Math.abs() when only the size of the gap matters. If you need hours or minutes rather than days, my guide to subtracting dates in TypeScript breaks a gap into days, hours and minutes.
Why does TypeScript reject date2 – date1?
Plain JavaScript turns both dates into numbers when you write checkOut - checkIn. TypeScript in strict mode stops the build instead:
const checkIn = new Date(2026, 10, 20); // Nov 20, 2026
const checkOut = new Date(2026, 10, 27); // Nov 27, 2026
const nights = (checkOut - checkIn) / 86_400_000;
Compiler output:
days_minus.ts(4,17): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
days_minus.ts(4,28): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

tsc stops the build.The fix is the same getTime() call, or +checkOut - +checkIn if you like short code. Both give the epoch milliseconds that getting the current date in milliseconds explains.
Should you use Math.round, Math.floor or Math.ceil for days?
When the times of day differ, the raw result has a fraction. Which rounding you pick depends on the question you’re answering. Take a car rental from 9:00 AM on July 2 to 3:00 PM on July 4:
const MS_PER_DAY = 86_400_000;
const pickup = new Date(2026, 6, 2, 9, 0); // Jul 2, 2026, 9:00 AM
const dropoff = new Date(2026, 6, 4, 15, 0); // Jul 4, 2026, 3:00 PM
const days = (dropoff.getTime() - pickup.getTime()) / MS_PER_DAY;
console.log("exact days:", days);
console.log("floor: ", Math.floor(days));
console.log("round: ", Math.round(days));
console.log("ceil: ", Math.ceil(days));
console.log(`rental: $${(Math.ceil(days) * 49.99).toFixed(2)}`);
Output:
exact days: 2.25
floor: 2
round: 2
ceil: 3
rental: $149.97
Math.floor counts only full days that have passed, Math.round gives the nearest day, and Math.ceil counts every started day. Bills usually want Math.ceil, so you charge three days.
The toFixed(2) call is the quick version of formatting currency to 2 decimals in TypeScript.
Days between dates across daylight saving time
In 2026, US clocks spring forward on March 8. A local-time span from March 1 to March 15 is one hour short of 14 full days, so Math.floor drops a day:
const MS_PER_DAY = 86_400_000;
const start = new Date(2026, 2, 1); // March 1, 2026, local midnight
const end = new Date(2026, 2, 15); // March 15, after DST starts Mar 8
const raw = (end.getTime() - start.getTime()) / MS_PER_DAY;
console.log("time zone:", Intl.DateTimeFormat().resolvedOptions().timeZone);
console.log("raw days: ", raw);
console.log("floor: ", Math.floor(raw));
console.log("round: ", Math.round(raw));
function calendarDays(a: Date, b: Date): number {
const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return (utcB - utcA) / MS_PER_DAY;
}
console.log("Date.UTC: ", calendarDays(start, end));
Output:
time zone: America/New_York
raw days: 13.958333333333334
floor: 13
round: 14
Date.UTC: 14

Math.floor says 13, and the Date.UTC() version says 14.Date.UTC() builds a timestamp from the year, month and day only, so time zones and clock changes don’t touch it. It’s the same trick behind comparing dates without time in TypeScript.
Count calendar days between two dates
Sometimes the clock doesn’t matter at all. An order placed at 11:30 PM that ships at 12:15 AM shipped the next day, even though only 45 minutes passed:
const MS_PER_DAY = 86_400_000;
function calendarDays(a: Date, b: Date): number {
const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return (utcB - utcA) / MS_PER_DAY;
}
const ordered = new Date(2026, 10, 25, 23, 30); // Nov 25, 11:30 PM
const shipped = new Date(2026, 10, 26, 0, 15); // Nov 26, 12:15 AM
const elapsed = (shipped.getTime() - ordered.getTime()) / MS_PER_DAY;
console.log("elapsed days: ", elapsed.toFixed(4));
console.log("floor elapsed:", Math.floor(elapsed));
console.log("calendar days:", calendarDays(ordered, shipped));
const tripStart = new Date(2026, 6, 1); // Jul 1, 2026
const tripEnd = new Date(2026, 6, 10); // Jul 10, 2026
console.log("trip, both days counted:", calendarDays(tripStart, tripEnd) + 1);
Output:
elapsed days: 0.0313
floor elapsed: 0
calendar days: 1
trip, both days counted: 10
The elapsed count is 0, but the calendar count is 1. If you only store the date part, removing the time from a date in TypeScript shows how. When both ends count, like a July 1 to July 10 trip, add 1.
How do you count days between date strings?
Dates from your form or API often arrive as "2026-06-01". A date-only string like that is read as UTC midnight, which is still May 31 in New York. Parsing the parts yourself avoids the shift and catches impossible dates:
const MS_PER_DAY = 86_400_000;
// "2026-06-01" alone is read as UTC midnight, which is May 31 in New York
console.log("new Date():", new Date("2026-06-01").toLocaleDateString("en-US"));
function parseDay(iso: string): number {
const [y, m, d] = iso.split("-").map(Number);
const utc = Date.UTC(y, m - 1, d);
if (new Date(utc).getUTCDate() !== d) throw new Error(`bad date: ${iso}`);
return utc;
}
function daysBetween(from: string, to: string): number {
return (parseDay(to) - parseDay(from)) / MS_PER_DAY;
}
console.log("days:", daysBetween("2026-06-01", "2026-07-15"));
try {
daysBetween("2026-02-30", "2026-03-15");
} catch (e) {
console.log((e as Error).message);
}
Output:
new Date(): 5/31/2026
days: 44
bad date: 2026-02-30
The round-trip check rejects February 30, which Date.UTC() would quietly roll into March. For other kinds of bad input, see how to check for invalid dates in TypeScript.
Get the difference in days with date-fns
If you already use date-fns, it ships both kinds of day count. I ran an order placed November 25 at 11:30 PM and shipped November 27 at 8:00 AM through date-fns 4:
import { differenceInCalendarDays, differenceInDays } from "date-fns";
const ordered = new Date(2026, 10, 25, 23, 30); // Nov 25, 11:30 PM
const shipped = new Date(2026, 10, 27, 8, 0); // Nov 27, 8:00 AM
console.log("differenceInDays: ", differenceInDays(shipped, ordered));
console.log("differenceInCalendarDays:", differenceInCalendarDays(shipped, ordered));
Output:
differenceInDays: 1
differenceInCalendarDays: 2
differenceInDays counts full 24-hour days, so 32.5 hours is 1. differenceInCalendarDays compares the dates only and returns 2. Both take the later date first. When you display these dates for users in other zones, read formatting a date with a time zone in TypeScript.
Practical examples: days until, days late and business days
You’ll hit these three questions in almost every app. Here they are with today’s date, October 27, 2026:
const MS_PER_DAY = 86_400_000;
function calendarDays(a: Date, b: Date): number {
const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return (utcB - utcA) / MS_PER_DAY;
}
function businessDays(a: Date, b: Date): number {
let count = 0;
const day = new Date(a.getFullYear(), a.getMonth(), a.getDate());
while (day < b) {
const dow = day.getDay();
if (dow !== 0 && dow !== 6) count++;
day.setDate(day.getDate() + 1);
}
return count;
}
const today = new Date(2026, 9, 27); // Oct 27, 2026
const thanksgiving = new Date(2026, 10, 26); // Nov 26, 2026
console.log("days until Thanksgiving:", calendarDays(today, thanksgiving));
const invoiceDue = new Date(2026, 8, 30); // Sep 30, 2026
const late = calendarDays(invoiceDue, today);
console.log(`invoice ${late} days late, fee $${(late * 1.5).toFixed(2)}`);
const review = new Date(2026, 10, 10); // Nov 10, 2026
console.log("business days to Nov 10:", businessDays(today, review));
Output:
days until Thanksgiving: 30
invoice 27 days late, fee $40.50
business days to Nov 10: 10

The business-day loop skips Saturdays and Sund