Math with Dates in Python
Performing math with dates — adding days, subtracting weeks, calculating differences, or projecting future dates — is one of the most essential skills in data science. Whether you’re building rolling windows, calculating customer lifetime, measuring freshness, or creating time-based features, Python’s timedelta and relativedelta make date arithmetic clean, accurate, and powerful.
TL;DR — Key Tools for Date Math
timedelta→ days, hours, minutes, secondsdateutil.relativedelta→ months, years, weeks- pandas
.dtaccessor for vectorized operations - Always work with timezone-aware datetimes
1. Basic Date Arithmetic with timedelta
from datetime import date, timedelta
from zoneinfo import ZoneInfo
today = date.today()
# Add time
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
future = today + timedelta(days=30, hours=6)
# Subtract time
last_week = today - timedelta(weeks=1)
print(f"Last week: {last_week}")
2. Advanced Math with relativedelta
from dateutil.relativedelta import relativedelta
# Add months or years (timedelta cannot do this accurately)
next_month = date.today() + relativedelta(months=1)
next_year = date.today() + relativedelta(years=1)
# End of month handling
end_of_month = date(2026, 3, 31) + relativedelta(months=1) # correctly becomes April 30
3. Real-World Data Science Examples
import pandas as pd
# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
"order_id": range(1001, 1001 + n),
"order_date": pd.date_range("2026-01-01", periods=n, freq="3D"),
"customer_id": rng.integers(101, 121, n),
"customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
"product_id": rng.integers(1, 9, n),
"region": rng.choice(["North", "South", "East", "West"], n),
"category": rng.choice(["Electronics", "Computers", "Books", "Home"], n),
"payment_method": rng.choice(["card", "cash", "wallet"], n),
"segment": rng.choice(["consumer", "business"], n),
"quantity": rng.integers(1, 6, n),
"amount": rng.uniform(50, 2500, n).round(2),
"discount": rng.choice([0.0, 0.05, 0.1, 0.2], n),
}).assign(
price=lambda d: (d["amount"] / d["quantity"]).round(2),
profit=lambda d: (d["amount"] * rng.uniform(0.05, 0.3, n)).round(2),
).assign(
ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)
df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])
# Current time
now = pd.Timestamp.now(tz="UTC")
df["order_date"] = df["order_date"].dt.tz_localize("UTC") # demo dates are naive; make them UTC-aware to match now
# Math with dates in pandas
df["days_since_order"] = (now - df["order_date"]).dt.days
df["projected_delivery"] = df["order_date"] + pd.Timedelta(days=14)
df["one_month_ago"] = df["order_date"] - pd.Timedelta(days=30)
# Rolling window example
df["sales_7_days_ago"] = df["amount"].shift(7)
4. Best Practices in 2026
- Use
timedeltafor day/hour/minute arithmetic - Use
relativedeltafor month/year calculations - Prefer pandas
Timedeltaand.dtfor large DataFrames - Always perform calculations on timezone-aware datetimes
- Store original dates and derived math columns separately
Date Arithmetic Rules
Adding a timedelta to a date moves it by a fixed number of days. Only the days part matters for a date: hours and minutes in the timedelta are ignored, because a date has no time.
Months and years have different lengths, so timedelta cannot express "one month later". dateutil.relativedelta and pandas DateOffset handle calendar arithmetic and clip to the end of shorter months.
Subtracting tz-aware and naive timestamps raises an error, so make both sides the same kind before doing math.
Worked Example: Fixed Days versus Calendar Months
We compare adding 30 days with adding one calendar month.
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta
import pandas as pd
start = date(2026, 1, 31)
print(start + timedelta(days=30))
print(start + relativedelta(months=1))
print(date(2026, 3, 1) + timedelta(hours=30))
s = pd.Series(pd.to_datetime(["2026-01-31", "2026-02-15"]))
print((s + pd.DateOffset(months=1)).dt.date.tolist())
Thirty days after 31 January is 2 March, while one calendar month later is 28 February. Adding 30 hours to a date moves it by only one day. pandas DateOffset clips to month end in the same way.
Practical Tips
Always test date arithmetic around month ends and leap days, such as 31 January and 29 February.
These edge cases are where most date bugs hide.
Common Mistakes
- Using 30 days to mean one month.
- Expecting hours to affect a date.
- Mixing naive and aware timestamps.
- Forgetting leap years in manual calculations.
Quick Summary
- timedelta adds fixed durations.
- relativedelta and DateOffset add calendar units.
- Dates ignore time parts.
- Keep time zone awareness consistent.
Conclusion
Math with dates is a core skill for any data scientist. In 2026, the combination of timedelta, relativedelta, and pandas vectorized operations lets you perform accurate time-based feature engineering, projections, and freshness analysis with clean, readable code.
Next steps:
- Add time-delta features (days since order, projected delivery, etc.) to one of your current datasets