🚀 Supercharge your YouTube channel's growth with AI.
Try YTGrowAI FreeDifference Between Two Dates in Python: Complete Guide

I needed to figure out how many days were left on a subscription the other day, and I realized I had to calculate the exact gap between today’s date and the renewal date. The same problem shows up everywhere: a project manager counting weekdays before a deadline, a DevOps engineer timing how long a deployment step took, an analyst measuring the duration between two market events.
Python’s standard library handles all of these cases cleanly. The built-in datetime module covers the majority of date arithmetic scenarios with zero dependencies. For more complex calendar math — like adding months or computing age across leap years — the dateutil package fills the remaining gaps. This article covers the four datetime classes, how to subtract dates for days and seconds, how to work with calendars using relativedelta, and how to handle timezones and business days.
TLDR
- Subtract
dateordatetimeobjects directly to get atimedelta, then read.daysor.total_seconds() - Use
dateutil.relativedeltawhen you need calendar-aware months, years, or age calculation - Use
numpy.busday_countfor business day calculations with holiday support - Use
zoneinfo.ZoneInfo(Python 3.9+) for timezone-aware arithmetic - Store datetimes in UTC internally, convert to local time only at the display layer
Python’s Four Main Date and Time Classes
Python’s datetime module defines four classes that form the foundation of all date and time handling. The date class represents a calendar date with year, month, and day. The time class represents a time of day with hour, minute, second, and microsecond. The datetime class combines both a date and a time in a single object. The timedelta class represents a duration — this is what you get when you subtract one date or datetime from another, as covered in the Python timedelta guide.
These four classes are designed to work together. You can subtract any instance from another of the same type and get a timedelta. You can add or subtract a timedelta from a date or datetime to get a new date or datetime. The relationship between these types makes the API predictable and composable.
from datetime import date, datetime, timedelta
# date: calendar date only
d = date(2026, 4, 15)
print(d) # 2026-04-15
# datetime: date and time combined
dt = datetime(2026, 4, 15, 11, 30, 0)
print(dt) # 2026-04-15 11:30:00
# timedelta: a duration between two points in time
delta = timedelta(days=10, hours=3)
print(delta) # 10 days, 3:00:00
2026-04-15
2026-04-15 11:30:00
10 days, 3:00:00
The key distinction for date arithmetic is that date and datetime are point-in-time values, while timedelta is a duration. You cannot directly subtract a datetime from a date — Python raises a TypeError because they are different types. Always convert to matching types before subtracting.
Finding the Number of Days Between Two Dates
The simplest and most common scenario is finding how many days separate two dates. The approach is identical whether you use date objects or datetime objects: subtract the earlier date from the later date and read the .days attribute from the resulting timedelta.
from datetime import date
start = date(2026, 1, 1)
end = date(2026, 4, 15)
difference = end - start
print(difference) # 104 days, 0:00:00
print(difference.days) # 104
104 days, 0:00:00
104
The subtraction always produces a timedelta regardless of which direction you subtract. If you subtract a later date from an earlier one, you get a negative timedelta. Python handles the sign correctly without throwing an error, which is useful when you are not sure which date is earlier.
from datetime import date
d1 = date(2026, 4, 15)
d2 = date(2026, 1, 1)
# Reversing the order gives a negative timedelta
diff = d1 - d2
print(diff.days) # 104 (positive, end - start)
print((d2 - d1).days) # -104 (negative, start - end)
104
-104
For most applications, you want the absolute value. The abs() function on a timedelta returns the duration as a positive value, which simplifies logic when the order of subtraction is not guaranteed.
from datetime import date
def days_between(d1: date, d2: date) -> int:
return abs((d2 - d1).days)
print(days_between(date(2026, 4, 15), date(2026, 1, 1))) # 104
print(days_between(date(2026, 1, 1), date(2026, 4, 15))) # 104
104
104
Working with Timedelta: Seconds, Minutes, Hours, Code Timing, and Deadlines
The .days attribute gives you whole days, but sometimes you need finer granularity. A timedelta internally stores duration as a number of days and seconds. The .total_seconds() method converts the entire duration to floating-point seconds, which you can then divide by 60 for minutes or by 3600 for hours, as demonstrated in the datetime module examples.
from datetime import datetime
start = datetime(2026, 4, 15, 8, 30, 0)
end = datetime(2026, 4, 15, 17, 45, 30)
diff = end - start
total_secs = diff.total_seconds()
print(f"Total seconds: {total_secs}") # 33330.0
print(f"Hours: {total_secs / 3600:.2f}") # 9.26
print(f"Minutes: {total_secs / 60:.1f}") # 555.5
Total seconds: 33330.0
Hours: 9.26
Minutes: 555.5
If you need the seconds component separately from days, the .seconds attribute gives you the remainder after removing full days. The .seconds attribute differs from .total_seconds() because it only counts the portion of the duration that does not make up a full day.
from datetime import timedelta
delta = timedelta(days=2, hours=5, minutes=30, seconds=45)
print(delta.days) # 2
print(delta.seconds) # 19845 (5*3600 + 30*60 + 45)
print(delta.total_seconds()) # 192645.0
print(delta.microseconds) # 0
2
19845
192645.0
0
A common use case is measuring how long a piece of code takes to run. You capture the current timestamp before the code, capture it again after, and subtract to get the elapsed time. The datetime.now() function returns the current local date and time as a datetime object.
from datetime import datetime
start = datetime.now()
# Simulated work
data = list(range(100_000))
squared = [x ** 2 for x in data]
end = datetime.now()
elapsed = end - start
print(f"Computation took: {elapsed}")
print(f"Total seconds: {elapsed.total_seconds():.4f}")
Computation took: 0:00:00.004831
Total seconds: 0.0048
For more accurate performance measurement, use time.perf_counter() instead of datetime.now(). The perf_counter function is designed specifically for benchmarking and is not affected by system clock adjustments the way datetime can be, as covered in the Python timedelta article.
import time
from datetime import timedelta
start = time.perf_counter()
data = list(range(100_000))
squared = [x ** 2 for x in data]
end = time.perf_counter()
elapsed = timedelta(seconds=end - start)
print(f"Elapsed: {elapsed.total_seconds():.6f} seconds")
Elapsed: 0.004831 seconds
Calendar-Aware Arithmetic with dateutil
Subtracting dates with timedelta treats every day as exactly 86400 seconds. That model breaks down when you need calendar-aware arithmetic. What does “one month after January 31” mean? What is the age of a person born on February 29, 2000? These questions require a calendar-aware library to answer correctly, as explained in the datetime module examples.
The dateutil package provides relativedelta, which represents the difference between two dates in terms of years, months, and days as a human would interpret them.
from datetime import date
from dateutil.relativedelta import relativedelta
birthday = date(1993, 8, 15)
today = date(2026, 4, 15)
age = relativedelta(today, birthday)
print(f"Age: {age.years} years, {age.months} months, {age.days} days")
Age: 32 years, 8 months, 0 days
The relativedelta object handles calendar ambiguity correctly. For a person born on January 31, it computes their age without trying to assign a fixed number of days to each month.
from datetime import date
from dateutil.relativedelta import relativedelta
# February 29 in a leap year
feb29 = date(2000, 2, 29)
one_year_later = date(2001, 2, 28)
delta = relativedelta(one_year_later, feb29)
print(f"Years: {delta.years}, Months: {delta.months}, Days: {delta.days}")
Years: 1, Months: 0, Days: 0
You can also use relativedelta to add calendar-aware durations to a date. Adding one month to January 31 produces February 28 (or February 29 in a leap year) — the logically correct result rather than an error or an overflow.
from datetime import date
from dateutil.relativedelta import relativedelta
start = date(2026, 1, 31)
plus_one_month = start + relativedelta(months=1)
plus_one_year = start + relativedelta(years=1)
print(plus_one_month) # 2026-02-28
print(plus_one_year) # 2027-01-31
2026-02-28
2027-01-31
Business Days, Weekdays, and Holidays
Business day calculations require excluding weekends and sometimes holidays. The numpy library provides busday_count, which counts the number of business days between two dates using a configurable weekmask, as shown in the datetime module examples.
import numpy as np
from datetime import date
project_start = date(2026, 4, 1)
project_end = date(2026, 4, 30)
business_days = np.busday_count(
project_start.strftime('%Y-%m-%d'),
project_end.strftime('%Y-%m-%d')
)
print(f"Business days in April 2026: {business_days}")
Business days in April 2026: 21
The weekmask parameter uses a binary string where each character represents a day of the week starting from Monday. A 1 means that day is a business day, and 0 means it is not. The default is 1111100, which makes Monday through Friday business days.
import numpy as np
from datetime import date
start = date(2026, 4, 1)
end = date(2026, 4, 30)
# Sunday and Tuesday off (4-day work week)
weekmask = "1001100"
business_days = np.busday_count(
start.strftime('%Y-%m-%d'),
end.strftime('%Y-%m-%d'),
weekmask=weekmask
)
print(f"4-day work week: {business_days}")
4-day work week: 12
For holiday handling, pass a list of holiday dates as the holidays parameter. The busday_offset function is also useful for moving forward or backward by a specific number of business days.
import numpy as np
from datetime import date
holidays = [
date(2026, 4, 3), # Good Friday
date(2026, 4, 10), # Company holiday
]
start = date(2026, 4, 1)
end = date(2026, 4, 30)
business_days = np.busday_count(
start.strftime('%Y-%m-%d'),
(end + np.timedelta64(1, 'D')).strftime('%Y-%m-%d'),
holidays=[h.strftime('%Y-%m-%d') for h in holidays]
)
print(f"Business days minus holidays: {business_days}")
Business days minus holidays: 19
Project schedules, payroll systems, and recurring event planners often need to know how many times a specific weekday appears in a date range. The function below counts weekday occurrences without any external libraries.
from datetime import date, timedelta
def count_weekday_between(start: date, end: date, target_weekday: int) -> int:
count = 0
current = start
while current.weekday() != target_weekday and current <= end:
current += timedelta(days=1)
while current <= end:
count += 1
current += timedelta(days=7)
return count
# How many Mondays in April 2026?
mondays = count_weekday_between(
date(2026, 4, 1),
date(2026, 4, 30),
target_weekday=0
)
print(f"Mondays in April 2026: {mondays}")
# How many Fridays?
fridays = count_weekday_between(
date(2026, 4, 1),
date(2026, 4, 30),
target_weekday=4
)
print(f"Fridays in April 2026: {fridays}")
Mondays in April 2026: 4
Fridays in April 2026: 4
The algorithm advances to the first occurrence of the target weekday, then counts forward in weekly steps. This runs in O(n) weeks rather than O(n) days, which matters when you are processing very large date ranges.
Timezone-Aware Date Arithmetic
Naive datetime objects carry no timezone information. When you subtract two naive datetime objects, you get the elapsed time according to the local clock, but if those datetimes came from different timezone contexts, the result can be misleading. Timezone-aware datetime objects eliminate this ambiguity.
Python 3.9 introduced zoneinfo, which is part of the standard library. The recommended approach for timezone handling going forward is zoneinfo.
from datetime import datetime
from zoneinfo import ZoneInfo
# A meeting scheduled in UTC
meeting_utc = datetime(2026, 4, 15, 10, 0, tzinfo=ZoneInfo("UTC"))
# Same moment converted to India Standard Time
meeting_ist = meeting_utc.astimezone(ZoneInfo("Asia/Kolkata"))
print(f"UTC: {meeting_utc}")
print(f"IST: {meeting_ist}")
print(f"Same moment: {meeting_utc == meeting_ist}")
UTC: 2026-04-15 10:00:00+00:00
IST: 2026-04-15 15:30:00+05:30
Same moment: True
When you subtract two timezone-aware datetime objects, the result is a timedelta that reflects the actual elapsed time. The timezone offset is accounted for automatically — subtracting a UTC datetime from an IST datetime gives you the correct hour difference without any manual offset calculation.
Formatting Timedelta for Display
Raw timedelta objects print as something like 15 days, 3:24:00, which is not always what your users or logs need. You often want a human-readable sentence or a specific unit. The function below converts a timedelta into a readable string without requiring any external libraries.
from datetime import timedelta
def format_timedelta(delta: timedelta) -> str:
total_seconds = int(delta.total_seconds())
if total_seconds < 0:
return "in the past"
days = total_seconds // 86400
hours = (total_seconds % 86400) // 3600
minutes = (total_seconds % 3600) // 60
seconds = total_seconds % 60
parts = []
if days > 0:
unit = "day" if days == 1 else "days"
parts.append(f"{days} {unit}")
if hours > 0:
unit = "hour" if hours == 1 else "hours"
parts.append(f"{hours} {unit}")
if minutes > 0:
unit = "minute" if minutes == 1 else "minutes"
parts.append(f"{minutes} {unit}")
if seconds > 0 and days == 0:
unit = "second" if seconds == 1 else "seconds"
parts.append(f"{seconds} {unit}")
if not parts:
return "less than a second"
return " ".join(parts)
print(format_timedelta(timedelta(days=5, hours=3, minutes=30)))
print(format_timedelta(timedelta(hours=2)))
print(format_timedelta(timedelta(seconds=45)))
5 days 3 hours 30 minutes
2 hours
45 seconds
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
def add_business_days(start: datetime, days: int) -> datetime:
current = start
remaining = days
while remaining > 0:
current += timedelta(days=1)
if current.weekday() < 5:
remaining -= 1
return current
def subscription_end_date(start_date: datetime, months: int) -> datetime:
return start_date + relativedelta(months=months)
# Net-30 invoice
invoice_date = datetime(2026, 3, 15)
due_date = invoice_date + timedelta(days=30)
print(f"Invoice due: {due_date.strftime('%Y-%m-%d')}")
# 3-month subscription ending on the last day of the month
sub_start = datetime(2026, 1, 31)
sub_end = subscription_end_date(sub_start, months=3)
print(f"Subscription ends: {sub_end.strftime('%Y-%m-%d')}")
Invoice due: 2026-04-14
Subscription ends: 2026-04-30
The relativedelta approach for subscription periods handles month-end boundaries correctly. Adding three months to January 31 produces April 30 — users expect exactly this behavior, unlike naive approaches that simply add 90 days per month.
FAQ
How do I calculate the difference between two dates in Python?
Subtract one datetime or date object from another. The result is a timedelta object. Access the difference in days with .days or in total seconds with .total_seconds().
How do I count months and years between two dates?
Use dateutil.relativedelta. It computes the difference in calendar years, months, and days rather than treating everything as a raw number of days. Install it with pip install python-dateutil.
How do I calculate business days between two dates in Python?
Use numpy.busday_count(). Pass the start and end dates as strings in YYYY-MM-DD format. Use the weekmask parameter for custom work weeks and the holidays parameter to exclude specific dates.
How do I handle timezones when computing date differences?
Attach a timezone using zoneinfo.ZoneInfo (Python 3.9+) or pytz. Always store datetimes in UTC and convert to local time only at the display layer.
How do I avoid errors when subtracting date and datetime objects?
You cannot subtract a date from a datetime directly. Convert the date to a datetime using datetime.combine(date_obj, datetime.min.time()) before subtracting.
How accurate is timedelta for measuring code execution time?
For accurate performance measurement, use time.perf_counter() or time.time() rather than datetime.now(). The time module functions are designed for benchmarking and are not affected by system clock adjustments.
How do I add one month to a date in Python?
Use dateutil.relativedelta(months=1) added to the date. The library handles end-of-month edge cases correctly. Naive approaches like adding 30 days fail for months with fewer than 30 days.


