Pandas DateOffset: Calendar Arithmetic and Schedules

Add months, years and days, replace date components and compare calendar rules with elapsed durations.

Create the original sample 🔝

pd.DateOffset() performs calendar arithmetic on a Timestamp, DatetimeIndex or datetime Series. It is not a DataFrame method. Retain the original Ravi, Raju and Alex synthetic dates; later examples leave this base unchanged.
import pandas as pd
df = pd.DataFrame({"NAME": ["Ravi", "Raju", "Alex"],
    "dt_start": pd.to_datetime(["2020-01-31", "2020-02-29", "2019-02-28"])})
print(df)
assert df.shape == (3, 2)

Expected output

   NAME   dt_start
0  Ravi 2020-01-31
1  Raju 2020-02-29
2  Alex 2019-02-28

Add or subtract days 🔝

days=365 is not necessarily a calendar year. Also retain the original subtract-30-days example.
changed = df.copy()
changed["dt_end"] = df["dt_start"] + pd.DateOffset(days=365)
changed["earlier"] = df["dt_start"] - pd.DateOffset(days=30)
print(changed)
assert "dt_end" not in df.columns

Expected output

   NAME   dt_start     dt_end    earlier
0  Ravi 2020-01-31 2021-01-30 2020-01-01
1  Raju 2020-02-29 2021-02-28 2020-01-30
2  Alex 2019-02-28 2020-02-28 2019-01-29

Add or subtract calendar years 🔝

Leap days adjust when the destination year lacks February 29. Also subtract one year.
changed = df.copy()
changed["dt_end"] = df["dt_start"] + pd.DateOffset(years=2)
changed["earlier"] = df["dt_start"] - pd.DateOffset(years=1)
print(changed)
assert "dt_end" not in df.columns

Expected output

   NAME   dt_start     dt_end    earlier
0  Ravi 2020-01-31 2022-01-31 2019-01-31
1  Raju 2020-02-29 2022-02-28 2019-02-28
2  Alex 2019-02-28 2021-02-28 2018-02-28

Add or subtract calendar months 🔝

A destination month may have fewer days. Also subtract two months.
changed = df.copy()
changed["dt_end"] = df["dt_start"] + pd.DateOffset(months=3)
changed["earlier"] = df["dt_start"] - pd.DateOffset(months=2)
print(changed)
assert "dt_end" not in df.columns

Expected output

   NAME   dt_start     dt_end    earlier
0  Ravi 2020-01-31 2020-04-30 2019-11-30
1  Raju 2020-02-29 2020-05-29 2019-12-29
2  Alex 2019-02-28 2019-05-28 2018-12-28

Check month-end behaviour 🔝

Calendar months are not fixed 30-day intervals. Directly adding two months differs from applying a one-month offset twice after a date has been clipped.
dt = pd.Timestamp("2026-01-31")
one = dt + pd.DateOffset(months=1)
two = dt + pd.DateOffset(months=2)
repeated = one + pd.DateOffset(months=1)
print(one)
print(two)
print(repeated)
assert str(two.date()) == "2026-03-31" and str(repeated.date()) == "2026-03-28"

Expected output

2026-02-28 00:00:00
2026-03-31 00:00:00
2026-03-28 00:00:00

Combine date and time components 🔝

Retain the original combined years/months/days operation. Hours, minutes and seconds also support relative arithmetic.
changed = df.assign(dt_end=df["dt_start"] + pd.DateOffset(years=2, months=3, days=13))
print(changed)
clock = pd.Timestamp("2026-09-05 10:20:30")
print(clock + pd.DateOffset(hours=2, minutes=15, seconds=10))
assert changed["dt_end"].iloc[0] == pd.Timestamp("2022-05-13")

Expected output

   NAME   dt_start     dt_end
0  Ravi 2020-01-31 2022-05-13
1  Raju 2020-02-29 2022-06-11
2  Alex 2019-02-28 2021-06-10
2026-09-05 12:35:40

Plural adds; singular replaces 🔝

Plural years/months/days/weeks/hours/minutes/seconds/milliseconds/microseconds/nanoseconds describe relative changes. Singular year/month/day/hour/minute/second/microsecond/nanosecond replace components. Use valid destination values. Replacement operations can be slower on a Series.
base = pd.Timestamp("2026-01-15 10:20:30")
print(base + pd.DateOffset(months=6))
print(base + pd.DateOffset(month=6))
print(base + pd.DateOffset(year=2027, month=6, day=1))
assert (base + pd.DateOffset(month=6)).month == 6

Expected output

2026-07-15 10:20:30
2026-06-15 10:20:30
2027-06-01 10:20:30

Compare calendar years and fixed durations 🔝

DateOffset(years=1) means one calendar year. Timedelta is an elapsed duration and cannot represent a fixed-length calendar month. NumPy month/year timedelta units are not substitutes for calendar arithmetic.
leap_year = pd.Timestamp("2020-01-31")
print(leap_year + pd.DateOffset(days=365))
print(leap_year + pd.DateOffset(years=1))
print(pd.Timestamp("2026-01-15") + pd.Timedelta(days=10))
assert leap_year + pd.DateOffset(days=365) != leap_year + pd.DateOffset(years=1)

Expected output

2021-01-30 00:00:00
2021-01-31 00:00:00
2026-01-25 00:00:00

Compare calendar days across daylight saving 🔝

For this valid local noon, DateOffset(days=1) preserves the wall-clock hour; an exact 24-hour Timedelta does not. For ambiguous or nonexistent destination wall times, resolve the timezone policy explicitly.
local = pd.Timestamp("2026-03-07 12:00", tz="US/Eastern")
calendar_day = local + pd.DateOffset(days=1)
elapsed_day = local + pd.Timedelta(hours=24)
print(calendar_day)
print(elapsed_day)
assert calendar_day.hour == 12 and elapsed_day.hour == 13
assert (calendar_day - local) / pd.Timedelta(hours=1) == 23

Expected output

2026-03-08 12:00:00-04:00
2026-03-08 13:00:00-04:00

Use MonthEnd, MonthBegin and rolling 🔝

Specialised offsets target calendar boundaries. rollforward/rollback leave an already-valid boundary unchanged; adding MonthEnd() at month end advances to the next month end.
mid = pd.Timestamp("2026-09-15")
offset = pd.offsets.MonthEnd()
print(mid + offset)
print(mid + pd.offsets.MonthBegin())
print(offset.rollforward(mid))
print(offset.rollback(mid))
end = pd.Timestamp("2026-09-30")
assert offset.rollforward(end) == end
assert end + offset == pd.Timestamp("2026-10-31")

Expected output

2026-09-30 00:00:00
2026-10-01 00:00:00
2026-09-30 00:00:00
2026-08-31 00:00:00

Advance by business days 🔝

BusinessDay skips weekends. Public holidays require an explicit custom calendar. The original Friday-to-Monday example is retained.
friday = pd.Timestamp("2026-09-04")
next_day = friday + pd.offsets.BusinessDay()
print(next_day)
assert next_day == pd.Timestamp("2026-09-07")

Expected output

2026-09-07 00:00:00

Build a monthly schedule from an anchor 🔝

For this synthetic policy, each due date is measured from the original January 31 anchor. Do not repeatedly add a month if the policy is to retain the original day when valid.
anchor = pd.Timestamp("2026-01-31")
schedule = pd.DatetimeIndex([anchor + pd.DateOffset(months=n) for n in range(1, 4)])
print(schedule)
assert schedule.strftime("%Y-%m-%d").tolist() == ["2026-02-28", "2026-03-31", "2026-04-30"]

Expected output

DatetimeIndex(['2026-02-28', '2026-03-31', '2026-04-30'], dtype='datetime64[us]', freq=None)

Common mistakes 🔝

Use pd.DateOffset(), not DataFrame.DateOffset(). month=6 sets June; months=6 adds six months. Calendar years may cross leap years; month clipping can prevent addition/subtraction from being reversible. Do not treat a calendar month as a fixed duration. For parsing see to_datetime(); compare with date differences and date ranges.

Exercise: next month end 🔝

For 2026-09-30, compare rollforward with adding MonthEnd(). Predict which operation stays on the same date. More practice: Date and Time exercises.

Exercise solution 🔝

An already-valid boundary rolls to itself; adding an anchored offset advances.
date = pd.Timestamp("2026-09-30")
end_offset = pd.offsets.MonthEnd()
print(end_offset.rollforward(date))
print(date + end_offset)
assert end_offset.rollforward(date) == date
assert date + end_offset == pd.Timestamp("2026-10-31")

Expected output

2026-09-30 00:00:00
2026-10-31 00:00:00

Practice in Google Colab 🔝

Open in Google Colab View on GitHub

Run examples in order, inspect formatted dates and try the exercise. Save a copy to keep edits.

Reference: Pandas DateOffset() documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer