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

Every Python developer encounters the problem of calculating how much time separates two dates. A subscription system needs to know how many days remain on a plan. A deployment script needs to log how long a step took. A project manager wants to count the weekdays available before a deadline. Python’s standard library handles all of these cases, and for the more complex calendar math, one additional package fills the remaining gaps.
The built-in datetime module is the foundation. It ships with every Python installation, covers the majority of real-world use cases, and requires zero dependencies. For scenarios involving months, years, and leap years, the dateutil package adds relativedelta, which is the tool I reach for when I need Python to think like a human calendar rather than a raw day counter.
Understanding How Python Represents Dates and Times
Python models dates and times through four main classes in the datetime module. 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, which is what you get when you subtract one date or datetime from another.
These four classes form a coherent system. You can subtract any instance from another of the same class 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, time, datetime, timedelta # date: calendar date only d = date(2026, 4, 15) print(d) # 2026-04-15 # time: time of day only t = time(11, 30, 0) print(t) # 11:30:00 # datetime: date + time combined dt = datetime(2026, 4, 15, 11, 30, 0) print(dt) # 2026-04-15 11:30:00 # timedelta: a duration delta = timedelta(days=10, hours=3) print(delta) # 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 because they are different types, even if the time component of the datetime is midnight.
The Core Pattern: Subtracting Two Dates
The simplest and most common scenario is finding the number of days between two dates. The approach is the same 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 print(type(difference)) #
The subtraction always produces a timedelta regardless of which direction you subtract. If you subtract a later date from an earlier date, you get a negative timedelta. Python handles the sign correctly without throwing an error, which is convenient 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) print((d2 - d1).days) # -104 (negative)
For most applications, you want the absolute value. The abs() function on a timedelta returns the duration as a positive value, which simplifies the 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
Getting Seconds, Minutes, and Hours from a Timedelta
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 to get minutes or by 3600 to get hours.
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}") # 9.2583...
print(f"Minutes: {total_secs / 60}") # 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()) # 186645.0 print(delta.microseconds) # 0
The microseconds attribute holds the fractional second component as microseconds. For most practical applications, .total_seconds() is what you want because it gives you a single floating-point number that represents the complete duration.
Timing Code Execution with datetime
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
total = 0
for i in range(1_000_000):
total += i ** 2
end = datetime.now()
elapsed = end - start
print(f"Computation took: {elapsed}")
print(f"Total seconds: {elapsed.total_seconds():.4f}")
For more accurate performance measurement, use time.perf_counter() instead of datetime.now(). The datetime approach works, but perf_counter is designed specifically for benchmarking and is not affected by system clock adjustments the way datetime can be.
import time
from datetime import timedelta
start = time.perf_counter()
# Actual benchmark
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")
Using dateutil.relativedelta for Calendar-Aware Arithmetic
Subtracting dates with timedelta treats every day as exactly 86400 seconds. That model breaks down when you need calendar-aware arithmetic. A different approach is required for calendar-aware calculations. What does “one month after January 31” mean? What is the age of a person born on February 29, 2000? These are questions that require a calendar-aware library to answer correctly.
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. Install it with pip install python-dateutil.
# pip install python-dateutil
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 makes sense of calendar ambiguity. For a person born on January 31, relativedelta correctly computes their age without trying to assign a fixed number of days to each month. The calculation uses the actual calendar.
from datetime import date
from dateutil.relativedelta import relativedelta
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}")
# Correctly handles the leap year transition
You can also use relativedelta to add calendar-aware durations to a date. Adding one month to January 31 produces February 28 (or 29 in a leap year), which is 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
Counting Business Days Between Two Dates
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. It accepts a weekmask parameter to specify which weekdays count as business days.
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}")
The weekmask parameter uses a binary weekmask 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 (for a 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 days: {business_days}")
For holiday handling, you can 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
]
# Count business days excluding weekends and holidays
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}")
Counting Specific Weekdays Between Two Dates
Project schedules, payroll systems, and recurring event planners often need to know how many times a specific weekday appears in a date range. The example below shows a pure Python approach that requires no external libraries.
from datetime import date, timedelta
def count_weekday_between(start: date, end: date, target_weekday: int) -> int:
"""
Count how many times a weekday appears between two dates (inclusive).
Weekday: 0=Monday, 1=Tuesday, ..., 6=Sunday.
"""
count = 0
current = start
# Advance to the first occurrence of the target weekday
while current.weekday() != target_weekday and current <= end:
current += timedelta(days=1)
# Step through the rest in weekly increments
while current <= end:
count += 1
current += timedelta(days=7)
return count
def count_weekday_practical_example():
"""Demonstrate weekday counting in a real scheduling scenario."""
# Count Mondays in Q2 2026 for a recurring meeting schedule
q2_start = date(2026, 4, 1)
q2_end = date(2026, 6, 30)
mondays = count_weekday_between(q2_start, q2_end, target_weekday=0)
print(f"Standing Monday meetings in Q2 2026: {mondays}")
return mondays
# 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}")
The algorithm advances to the first occurrence of the target weekday, then counts forward in weekly steps. The weekly-stepping pattern 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. For earlier Python versions, the pytz package provides timezone support. The recommended approach 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 in 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}") # 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 EST datetime therefore gives you the correct hour difference without any manual offset calculation.
from datetime import datetime
from zoneinfo import ZoneInfo
utc_now = datetime(2026, 4, 15, 5, 30, tzinfo=ZoneInfo("UTC"))
india_now = datetime(2026, 4, 15, 11, 0, tzinfo=ZoneInfo("Asia/Kolkata"))
# The IST clock shows a later time, but the UTC values are the same instant
diff = india_now - utc_now
print(f"UTC difference: {diff}") # 5:30:00
print(f"Hours: {diff.total_seconds() / 3600}") # 5.0
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:
"""Convert a timedelta to a human-readable string."""
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)
# Test it
print(format_timedelta(timedelta(days=5, hours=3, minutes=30)))
# 5 days 3 hours 30 minutes
print(format_timedelta(timedelta(hours=2)))
# 2 hours
print(format_timedelta(timedelta(seconds=45)))
# 45 seconds
Calculating Deadlines and Due Dates
A practical application of date arithmetic is computing a due date given a start date and a duration. These calculations come up constantly: payment terms (net 30), subscription periods, project timelines, and SLA calculations.
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
def add_business_days(start: datetime, days: int) -> datetime:
"""Add a number of business days to a start date (Mon-Fri)."""
current = start
remaining = days
while remaining > 0:
current += timedelta(days=1)
# Skip weekends
if current.weekday() < 5: # Monday=0, Friday=4
remaining -= 1
return current
def subscription_end_date(start_date: datetime, months: int) -> datetime:
"""Add calendar months to a date (handles end-of-month edge cases)."""
return start_date + relativedelta(months=months)
# Example: invoice with net-30 terms
invoice_date = datetime(2026, 3, 15)
due_date = invoice_date + timedelta(days=30)
print(f"Invoice due: {due_date.strftime('%Y-%m-%d')}")
# Example: 3-month subscription starting today
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')}")
# Correctly handles Jan 31 -> Feb 28 (2026 is not a leap year)
The relativedelta approach for subscription periods handles month-end boundaries correctly. Adding one month to January 31 produces February 28 (or February 29 in a leap year). Adding three months to January 31 produces April 30. Users expect exactly this behavior, unlike naive approaches that simply add 30 days per month.
Common Pitfalls and How to Avoid Them
The most frequent error is attempting to subtract a date from a datetime. Python raises a TypeError because the two types are incompatible. Always convert to matching types before subtracting.
from datetime import date, datetime
d = date(2026, 4, 15)
dt = datetime(2026, 4, 15, 12, 0)
# TypeError
try:
result = dt - d
except TypeError as e:
print(f"Caught: {e}")
# Fix: convert date to datetime at midnight
result = dt - datetime.combine(d, datetime.min.time())
print(f"Correct difference: {result}")
Another pitfall is floating-point precision with .total_seconds(). Because timedelta stores days and seconds separately, converting to total seconds involves a multiplication that can produce floating-point representation artifacts when you print the result. For display purposes, round to a reasonable precision.
from datetime import timedelta
delta = timedelta(days=7, hours=12, minutes=30, seconds=45)
# Raw float may have floating point artifacts
raw_seconds = delta.total_seconds()
print(f"Raw: {raw_seconds}") # 648645.0
# Rounded for display
print(f"Rounded: {round(raw_seconds, 2)} seconds")
Timezone transitions around daylight saving time changes can also produce unexpected results if you use naive datetime objects. A DST transition effectively changes the UTC offset, so a "one-day" period during a spring forward or fall back may not contain exactly 24 hours of clock time. Use timezone-aware datetime objects to get correct results during these transitions.
TLDR
- Subtract
datetimeordateobjects directly to get atimedelta - Read the difference in days using
.days, or in seconds using.total_seconds() - Use
dateutil.relativedeltawhen you need calendar-aware months and years - Use
numpy.busday_countfor business day calculations with holiday support - Always use
zoneinfo.ZoneInfo(Python 3.9+) for timezone-aware arithmetic - Never mix
dateanddatetimein the same subtraction - Store datetimes in UTC internally, convert to local time only at the display layer
Date Difference Methods Comparison
| Method | Best For | Requires | Handles Months/Years | Handles Leap Years |
|---|---|---|---|---|
timedelta |
Simple day and second differences | stdlib | No | Yes |
relativedelta |
Calendar-aware months, years, age calculation | python-dateutil | Yes | Yes |
numpy.busday_count |
Business days, holiday exclusion | numpy | No | Yes |
zoneinfo |
Cross-timezone calculations | Python 3.9+ stdlib | Via relativedelta | Yes |
RankMath 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.