DateTime
First and Last Day
Programming
Code Example
Date Manipulation

Getting the first and last day of a month, using a given DateTime object

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Month boundary calculations appear everywhere, including billing cycles, monthly reports, and retention jobs. A small mistake in boundary math can produce missing or duplicated records. A robust implementation starts by defining whether you want date-only boundaries, timestamp boundaries, and inclusive or exclusive end semantics.

Date-Only Month Bounds

For pure dates, use calendar.monthrange to get number of days in month.

python
1import calendar
2from datetime import date
3
4
5def month_bounds_date(d: date):
6    first = d.replace(day=1)
7    last_day = calendar.monthrange(d.year, d.month)[1]
8    last = d.replace(day=last_day)
9    return first, last
10
11print(month_bounds_date(date(2026, 2, 18)))

This handles leap years correctly without manual branching.

Datetime Boundaries with Inclusive End

If you need full datetime range with inclusive end:

python
1from datetime import datetime, timedelta
2
3
4def month_bounds_inclusive(dt: datetime):
5    start = dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
6    next_month = (start.replace(day=28) + timedelta(days=4)).replace(day=1)
7    end = next_month - timedelta(microseconds=1)
8    return start, end
9
10start, end = month_bounds_inclusive(datetime(2026, 2, 18, 14, 30))
11print(start, end)

Inclusive end can be convenient for human-readable logs.

Half-Open Range Preferred for Queries

Databases and APIs usually work better with half-open intervals:

  • Start inclusive.
  • End exclusive at first instant of next month.
python
1def month_range_half_open(dt: datetime):
2    start = dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
3    end = (start.replace(day=28) + timedelta(days=4)).replace(day=1)
4    return start, end

Query pattern:

  • 'created_at >= start'
  • 'created_at < end'

This avoids end-of-day precision bugs.

Timezone Awareness and DST

If your application stores timezone-aware datetimes, boundaries must be calculated in the same zone.

python
1from datetime import datetime
2from zoneinfo import ZoneInfo
3
4tz = ZoneInfo("America/Toronto")
5now = datetime(2026, 3, 15, 10, 0, tzinfo=tz)
6start, end = month_range_half_open(now)
7print(start, end)

Mixing naive and aware datetimes is a common bug source, especially around daylight-saving transitions.

Reusable Utility Design

Centralize month-boundary logic in one module to prevent inconsistent formulas across services.

Recommended functions:

  • 'month_bounds_date for date objects.'
  • 'month_range_half_open for datetime filters.'
  • Optional helper for timezone normalization.

Document return semantics clearly so downstream teams apply filters consistently.

SQL and Data Pipeline Integration

Feed boundary values into parameterized SQL queries.

python
start, end = month_range_half_open(datetime(2026, 3, 15))
params = {"start": start, "end": end}
print(params)

When exporting monthly data, keep the same function for both data extraction and report metadata to prevent mismatch.

Test Cases That Catch Real Bugs

Include automated tests for:

  • Leap-year February.
  • Non-leap February.
  • December to January transition.
  • Timezone-aware datetimes near DST changes.
python
assert month_bounds_date(date(2024, 2, 1))[1].day == 29
assert month_bounds_date(date(2025, 2, 1))[1].day == 28

These tests are small but prevent high-impact defects.

Reporting and Billing Alignment

In many organizations, finance reports, dashboards, and data exports are generated by separate services. If each service computes month boundaries differently, reconciliation becomes difficult and teams lose trust in numbers. To avoid this, publish one shared month-boundary helper package and require all services to use it.

Documentation Expectations

Every month-bound utility should document whether end value is inclusive or exclusive and whether timezone conversion is applied before or after boundary calculation. Teams often skip this note, then rediscover the same edge case during quarter-end processing. Clear docs reduce repeated debugging effort and support smoother handoffs between engineering and analytics teams.## Common Pitfalls

  • Assuming fixed month lengths in date math.
  • Mixing inclusive and exclusive boundaries across services.
  • Using naive datetimes with timezone-aware datasets.
  • Rewriting boundary formulas in multiple code paths.
  • Skipping leap-year and year-boundary tests.

Summary

  • Use calendar-aware utilities for month start and end calculations.
  • Define boundary semantics explicitly for your system.
  • Prefer half-open ranges for query correctness.
  • Keep timezone handling explicit and consistent.
  • Centralize and test month-boundary helpers thoroughly.

Course illustration
Course illustration

All Rights Reserved.