python
datetime
previous month
date manipulation
programming

python date of the previous month

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Calculating dates for the previous month is a common requirement in billing, reporting, and analytics pipelines. The tricky part is handling month boundaries, leap years, and day overflow cleanly. Python provides reliable patterns in both the standard library and third-party utilities.

First and Last Day of the Previous Month

A robust standard-library method is to jump to the first day of the current month, then subtract one day.

python
1from datetime import date, timedelta
2
3
4def previous_month_range(today: date) -> tuple[date, date]:
5    first_this_month = today.replace(day=1)
6    last_prev_month = first_this_month - timedelta(days=1)
7    first_prev_month = last_prev_month.replace(day=1)
8    return first_prev_month, last_prev_month
9
10
11if __name__ == "__main__":
12    start, end = previous_month_range(date(2026, 3, 4))
13    print(start, end)  # 2026-02-01 2026-02-28

This pattern is dependable because it does not guess month lengths.

Same Day in Previous Month with dateutil

If you need the same calendar day one month earlier, relativedelta handles month math well.

python
1from datetime import date
2from dateutil.relativedelta import relativedelta
3
4
5def same_day_previous_month(d: date) -> date:
6    return d - relativedelta(months=1)
7
8print(same_day_previous_month(date(2026, 3, 31)))  # 2026-02-28
9print(same_day_previous_month(date(2024, 3, 31)))  # 2024-02-29

Notice how end-of-month values are clamped to valid dates automatically.

Time Zone-Aware Datetimes

If your job schedules are timezone-sensitive, compute previous month boundaries in the target timezone before converting to UTC storage values.

python
1from datetime import datetime, timedelta
2from zoneinfo import ZoneInfo
3
4
5def previous_month_start_local(now: datetime) -> datetime:
6    first_this = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
7    last_prev = first_this - timedelta(days=1)
8    return last_prev.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
9
10now = datetime.now(ZoneInfo("America/Toronto"))
11start_prev = previous_month_start_local(now)
12print(start_prev.isoformat())

Working in local timezone first helps avoid off-by-one-day errors around daylight saving transitions.

Designing Reusable Date Utilities

Centralize date range helpers in one module so reporting code remains consistent. A shared utility function should define:

  • Input type expectations.
  • Whether output is date or datetime.
  • Timezone policy.
  • Inclusive or exclusive end semantics.

A good convention for monthly reports is inclusive start with exclusive next-month boundary. That makes SQL range filters and partition queries simpler.

For example, query conditions can use created_at >= start and created_at < next_start. This avoids handling variable last-second values.

SQL and Reporting Integration

Monthly date helpers are often used to build SQL filters and partition paths. For warehouse jobs, generate both a human-readable label and machine-safe boundaries from the same helper so dashboards and extract jobs stay aligned.

A useful pattern is returning a small data object containing start date, end date, and next-start boundary. Downstream code can choose inclusive or exclusive interpretation without recalculating dates in multiple places. This reduces duplicated logic and makes reporting pipelines easier to audit.

Validation and Testing Strategy

Include tests for edge cases:

  • January to December rollover.
  • Leap year February.
  • End-of-month dates such as day thirty-one.
  • Timezone transitions in scheduled jobs.
python
1from datetime import date
2
3
4def test_prev_month_january_rollover():
5    start, end = previous_month_range(date(2026, 1, 15))
6    assert str(start) == "2025-12-01"
7    assert str(end) == "2025-12-31"

Small focused tests prevent reporting regressions that can be expensive to detect after data exports.

Common Pitfalls

A common mistake is subtracting a fixed number of days such as thirty to represent one month. Month lengths vary, so this causes drift and incorrect ranges.

Another issue is mixing naive datetimes and timezone-aware datetimes in one pipeline. Comparisons can fail or produce confusing boundaries in production.

Developers also forget to define inclusive versus exclusive end boundaries. Ambiguous range semantics often cause double-counted or missed records when chaining monthly jobs.

Finally, not testing leap-year and January rollover behavior can leave hidden bugs that appear only a few times per year.

Summary

  • Use first-day and minus-one-day logic for reliable previous-month ranges.
  • Use relativedelta for same-day previous-month calculations.
  • Apply timezone logic before converting to storage timezone.
  • Standardize range semantics in shared utility helpers.
  • Add edge-case tests for rollover, leap years, and end-of-month dates.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.