Date Arithmetic & Timedelta
Subtract dates to get Timedelta durations, add days or months to dates, find gaps between matches with diff(), and convert durations to days, hours or seconds.
"How many days of rest did a team get between two matches?" "When does a 14-day injury layoff end?" "How long did the whole IPL season last?" These are date arithmetic questions. pandas answers them with one idea: subtracting two moments gives a duration, and adding a duration to a moment gives a new moment.
Definition
A
Timedeltais an exact duration — a length of time such as 3 days 4 hours — not a point on the calendar. Subtracting two datetimes gives a Timedelta, and adding or subtracting a Timedelta to/from a datetime gives a new datetime. A column of durations has dtypetimedelta64. For calendar-aware steps (one month, one year — whose length varies) pandas usespd.DateOffset.
- Exact duration — measured in fixed units (days, hours, minutes, seconds).
1 dayis always 24 hours. - Not a point on the calendar —
65 dayshas no date of its own; it only measures a gap. - Calendar-aware — "add 1 month" from 31 January cannot be a fixed number of days;
DateOffset(months=1)follows the calendar.
Syntax
python
later - earlier # -> Timedelta
date + pd.Timedelta(days=3) # -> new date
pd.Timedelta(days=0, hours=0, minutes=0, seconds=0, weeks=0)
pd.Timedelta("1 day 2 hours") # from text
pd.to_timedelta(values, unit="D") # a whole column of durations
date + pd.DateOffset(months=1) # calendar step
s.diff() # difference with the previous row
td.dt.days td.dt.total_seconds() # parts of a timedelta column| Parameter / attribute | Meaning |
|---|---|
days, hours, minutes, seconds, weeks | building blocks of a Timedelta |
unit (to_timedelta) | unit of numeric input: "D", "h", "min", "s" |
months, years (DateOffset) | calendar months / years to add |
.days / .dt.days | whole days in a duration |
.total_seconds() / .dt.total_seconds() | full length in seconds (float) |
.dt.components | DataFrame of days, hours, minutes, … |
Example — subtracting two dates
python
opening = pd.Timestamp("2024-03-22")
final = pd.Timestamp("2024-05-26")
length = final - opening
print(length)
print(type(length).__name__)
print(length.days, "days")
print(length.total_seconds() / 3600, "hours")text
65 days 00:00:00
Timedelta
65 days
1560.0 hoursHow it works: subtracting two Timestamps returns a Timedelta. .days gives the whole-day count and .total_seconds() the exact length in seconds.
Example — adding days
python
injury = pd.Timestamp("2024-04-10")
print("Back on:", injury + pd.Timedelta(days=14))
print("Two weeks earlier:", injury - pd.Timedelta(weeks=2))
print(injury + pd.Timedelta("1 day 6 hours"))text
Back on: 2024-04-24 00:00:00
Two weeks earlier: 2024-03-27 00:00:00
2024-04-11 06:00:00Example — whole columns: gaps between matches with diff()
python
mi = pd.DataFrame({
"opponent": ["GT", "SRH", "RR", "DC", "RCB"],
"date": pd.to_datetime(["2024-03-24", "2024-03-27", "2024-04-01",
"2024-04-07", "2024-04-11"]),
})
mi["gap"] = mi["date"].diff()
mi["rest_days"] = mi["gap"].dt.days
mi["since_opener"] = mi["date"] - mi["date"].iloc[0]
print(mi)
print(mi.dtypes)
print("Average rest:", mi["gap"].mean())text
opponent date gap rest_days since_opener
0 GT 2024-03-24 NaT NaN 0 days
1 SRH 2024-03-27 3 days 3.0 3 days
2 RR 2024-04-01 5 days 5.0 8 days
3 DC 2024-04-07 6 days 6.0 14 days
4 RCB 2024-04-11 4 days 4.0 18 days
opponent str
date datetime64[us]
gap timedelta64[us]
rest_days float64
since_opener timedelta64[us]
dtype: object
Average rest: 4 days 12:00:00How it works:
diff()subtracts the previous row from each row, giving atimedelta64column. The first row has no previous match →NaT..dt.days(the.dtaccessor also works on timedelta columns) converts to plain numbers —float64because of theNaN.date - date.iloc[0]broadcasts one Timestamp against the whole column..mean()on a timedelta column gives an average duration.
Example — a column of durations and comparisons
python
squad = pd.DataFrame({
"player": ["Virat", "Rohit", "Bumrah"],
"dob": pd.to_datetime(["1988-11-05", "1987-04-30", "1993-12-06"]),
})
season_start = pd.Timestamp("2024-03-22")
squad["age_days"] = (season_start - squad["dob"]).dt.days
squad["age_years"] = (squad["age_days"] / 365.25).round(1)
squad["layoff_ends"] = season_start + pd.to_timedelta([0, 7, 21], unit="D")
print(squad)
print(squad[squad["layoff_ends"] > "2024-04-01"]["player"].tolist())text
player dob age_days age_years layoff_ends
0 Virat 1988-11-05 12921 35.4 2024-03-22
1 Rohit 1987-04-30 13476 36.9 2024-03-29
2 Bumrah 1993-12-06 11064 30.3 2024-04-12
['Bumrah']How it works: pd.to_timedelta([0, 7, 21], unit="D") builds a duration for every row, and adding it to a Timestamp gives one date per player. Datetime columns compare with date strings directly (> "2024-04-01").
Example — Timedelta vs DateOffset (months)
python
d = pd.Timestamp("2024-01-31")
print("+30 days :", d + pd.Timedelta(days=30))
print("+1 month :", d + pd.DateOffset(months=1))
print("+1 year :", pd.Timestamp("2024-02-29") + pd.DateOffset(years=1))text
+30 days : 2024-03-01 00:00:00
+1 month : 2024-02-29 00:00:00
+1 year : 2025-02-28 00:00:00How it works: a month is not a fixed length. Timedelta(days=30) lands on 1 March, while DateOffset(months=1) moves to the same day next month, clamped to the month's last day (29 Feb in a leap year).
Comparison — Timestamp, Timedelta, DateOffset
| Object | Represents | Example | Result of + with a date |
|---|---|---|---|
Timestamp | a moment | 2024-03-22 19:30 | — (date + date is not allowed) |
Timedelta | exact duration | 3 days 04:00:00 | date shifted by exact time |
DateOffset | calendar step | months=1 | same day next month |
| date − date | — | — | a Timedelta |
Common mistakes
- ❌
date1 + date2→TypeError. ✅ Onlydate − datemakes sense; add aTimedeltainstead. - ❌ Subtracting text dates (
"2024-05-26" - "2024-03-22"). ✅ Convert withpd.to_datetimefirst. - ❌
pd.Timedelta(months=1)→ error (months have no fixed length). ✅pd.DateOffset(months=1). - ❌ Calling
.dayson a column →AttributeError. ✅ Use.dt.dayson a Series;.daysis for a single Timedelta. - ❌ Using
.dt.secondsfor the total length — it is only the seconds part (0–86399). ✅.dt.total_seconds().
Interview tip
Q: How do you find the number of days between consecutive orders/matches per customer/team?
A: Sort by date, then df.groupby("team")["date"].diff().dt.days.
Key points to remember
- Subtracting two datetimes gives a Timedelta (an exact duration); a column of them has dtype timedelta64.
- Add or subtract pd.Timedelta(days=…, hours=…, weeks=…) to shift dates by exact amounts.
- Use pd.DateOffset(months=1) for calendar steps — months and years have no fixed length.
- s.diff() gives the gap to the previous row; the first row becomes NaT.
- On a timedelta column use .dt.days and .dt.total_seconds(); on a single Timedelta use .days and .total_seconds().
- Datetime columns compare directly with date strings: df[df['date'] > '2024-04-01'].
Try it yourself
Season length and injury return date
Rest days between MI matches
Practice: player ages on opening day
Revision Flashcards
Tap a card to flip it and see the answer.
