Pandas - Add/Subtract Days from Date
The most straightforward approach to adding or subtracting days uses `pd.Timedelta`. This method works with both individual datetime objects and entire Series.
Key Insights
- Pandas provides
pd.Timedeltaandpd.DateOffsetfor date arithmetic, with Timedelta being more precise for exact durations and DateOffset handling calendar-aware operations like month-end adjustments - The
+and-operators work directly on datetime columns, enabling vectorized operations across entire DataFrames without explicit loops - Business day calculations require
pd.offsets.BDay()to automatically skip weekends and optionally handle custom holiday calendars
Basic Date Arithmetic with Timedelta
The most straightforward approach to adding or subtracting days uses pd.Timedelta. This method works with both individual datetime objects and entire Series.
import pandas as pd
from datetime import datetime
# Single date manipulation
date = pd.to_datetime('2024-01-15')
future_date = date + pd.Timedelta(days=10)
past_date = date - pd.Timedelta(days=5)
print(f"Original: {date}")
print(f"Plus 10 days: {future_date}")
print(f"Minus 5 days: {past_date}")
Output:
Original: 2024-01-15 00:00:00
Plus 10 days: 2024-01-25 00:00:00
Minus 5 days: 2024-01-10 00:00:00
For DataFrame operations, the same syntax applies to entire columns:
df = pd.DataFrame({
'order_date': pd.to_datetime(['2024-01-15', '2024-02-20', '2024-03-10']),
'delivery_days': [3, 5, 2]
})
# Add fixed number of days
df['estimated_delivery'] = df['order_date'] + pd.Timedelta(days=7)
# Add variable days based on column values
df['actual_delivery'] = df['order_date'] + pd.to_timedelta(df['delivery_days'], unit='D')
print(df)
Output:
order_date delivery_days estimated_delivery actual_delivery
0 2024-01-15 3 2024-01-22 2024-01-18
1 2024-02-20 5 2024-02-27 2024-02-25
2 2024-03-10 2 2024-03-17 2024-03-12
Working with DateOffset for Calendar-Aware Operations
While Timedelta adds exact time periods, pd.DateOffset handles calendar-specific logic like month boundaries and year transitions.
from pandas.tseries.offsets import DateOffset
df = pd.DataFrame({
'start_date': pd.to_datetime(['2024-01-31', '2024-02-29', '2024-03-15'])
})
# Add 1 month using DateOffset (calendar-aware)
df['plus_1_month'] = df['start_date'] + DateOffset(months=1)
# Add 30 days using Timedelta (exact duration)
df['plus_30_days'] = df['start_date'] + pd.Timedelta(days=30)
# Combine days and months
df['complex_offset'] = df['start_date'] + DateOffset(months=2, days=15)
print(df)
Output:
start_date plus_1_month plus_30_days complex_offset
0 2024-01-31 2024-02-29 2024-03-01 2024-04-15
1 2024-02-29 2024-03-29 2024-03-30 2024-05-13
2 2024-03-15 2024-04-15 2024-04-14 2024-05-30
Notice how DateOffset(months=1) intelligently handles month-end dates, while Timedelta(days=30) simply adds 30 days.
Business Day Calculations
Real-world applications often need to exclude weekends and holidays. Pandas provides BDay (business day) offset for this purpose.
from pandas.tseries.offsets import BDay
df = pd.DataFrame({
'task_start': pd.to_datetime(['2024-01-12', '2024-01-19', '2024-01-26']) # All Fridays
})
# Add 3 business days
df['task_end'] = df['task_start'] + BDay(3)
# Subtract 5 business days
df['review_start'] = df['task_start'] - BDay(5)
print(df)
Output:
task_start task_end review_start
0 2024-01-12 2024-01-17 2024-01-05
1 2024-01-19 2024-01-24 2024-01-12
2 2024-01-26 2024-01-31 2024-01-19
Starting from Friday, adding 3 business days lands on Wednesday (skipping Saturday and Sunday).
Custom Holiday Calendars
For organization-specific holidays, create custom business day calendars:
from pandas.tseries.holiday import USFederalHolidayCalendar, Holiday
from pandas.tseries.offsets import CustomBusinessDay
# Use US Federal holidays
us_bd = CustomBusinessDay(calendar=USFederalHolidayCalendar())
df = pd.DataFrame({
'project_start': pd.to_datetime(['2024-07-03', '2024-12-23'])
})
df['milestone_5bd'] = df['project_start'] + 5 * us_bd
print(df)
This accounts for Independence Day (July 4th) and Christmas (December 25th) as non-business days.
For custom holidays:
from pandas.tseries.holiday import AbstractHolidayCalendar
class CompanyCalendar(AbstractHolidayCalendar):
rules = [
Holiday('Company Foundation Day', month=3, day=15),
Holiday('Summer Shutdown Start', month=7, day=15),
]
company_bd = CustomBusinessDay(calendar=CompanyCalendar())
df = pd.DataFrame({
'deadline': pd.to_datetime(['2024-03-13', '2024-07-12'])
})
df['extended_deadline'] = df['deadline'] + 3 * company_bd
print(df)
Vectorized Operations for Performance
When working with large datasets, avoid loops. Pandas operations are vectorized and significantly faster:
import numpy as np
# Generate large dataset
n = 100000
df = pd.DataFrame({
'transaction_date': pd.date_range('2023-01-01', periods=n, freq='h'),
'days_to_add': np.random.randint(1, 30, n)
})
# Efficient vectorized approach
df['future_date'] = df['transaction_date'] + pd.to_timedelta(df['days_to_add'], unit='D')
# Avoid this loop-based approach
# for idx, row in df.iterrows(): # SLOW!
# df.at[idx, 'future_date'] = row['transaction_date'] + pd.Timedelta(days=row['days_to_add'])
The vectorized approach runs 100x+ faster on large datasets.
Conditional Date Arithmetic
Apply different offsets based on conditions using np.where or apply:
df = pd.DataFrame({
'order_date': pd.to_datetime(['2024-01-15', '2024-02-20', '2024-03-10']),
'priority': ['standard', 'express', 'standard']
})
# Express orders: +2 days, Standard: +5 days
df['delivery_date'] = df['order_date'] + pd.to_timedelta(
np.where(df['priority'] == 'express', 2, 5), unit='D'
)
print(df)
Output:
order_date priority delivery_date
0 2024-01-15 standard 2024-01-20
1 2024-02-20 express 2024-02-22
2 2024-03-10 standard 2024-03-15
For complex conditions:
def calculate_delivery(row):
if row['priority'] == 'express':
return row['order_date'] + BDay(1)
elif row['priority'] == 'standard':
return row['order_date'] + BDay(3)
else:
return row['order_date'] + BDay(7)
df['delivery_date'] = df.apply(calculate_delivery, axis=1)
Handling Edge Cases
Always validate datetime conversions and handle potential errors:
df = pd.DataFrame({
'date_string': ['2024-01-15', '2024-02-30', 'invalid', None]
})
# Convert with error handling
df['parsed_date'] = pd.to_datetime(df['date_string'], errors='coerce')
# Only add days to valid dates
df['future_date'] = df['parsed_date'] + pd.Timedelta(days=10)
print(df)
Output:
date_string parsed_date future_date
0 2024-01-15 2024-01-15 2024-01-25
1 2024-02-30 NaT NaT
2 invalid NaT NaT
3 None NaT NaT
The errors='coerce' parameter converts invalid dates to NaT (Not a Time), preventing exceptions.
Time Components in Date Arithmetic
When adding days to datetime objects with time components, the time is preserved:
df = pd.DataFrame({
'timestamp': pd.to_datetime(['2024-01-15 14:30:00', '2024-02-20 09:15:00'])
})
df['plus_3_days'] = df['timestamp'] + pd.Timedelta(days=3)
df['plus_72_hours'] = df['timestamp'] + pd.Timedelta(hours=72)
print(df)
Output:
timestamp plus_3_days plus_72_hours
0 2024-01-15 14:30:00 2024-01-18 14:30:00 2024-01-18 14:30:00
1 2024-02-20 09:15:00 2024-02-23 09:15:00 2024-02-23 09:15:00
Both operations yield identical results since 3 days equals 72 hours, and both preserve the time component.
Performance Comparison
For critical applications, choose the right tool:
- Timedelta: Fastest for fixed-day operations, exact time calculations
- DateOffset: Required for month/year arithmetic, slightly slower
- BDay/CustomBusinessDay: Necessary for business logic, slowest due to calendar lookups
Use Timedelta when possible, upgrade to DateOffset or business day offsets only when calendar awareness is required.