Question
How can I find the last calendar day of the month for a date in PHP?
For example:
$a_date = "2009-11-23";
The result should be 2009-11-30.
And for:
$a_date = "2009-12-23";
The result should be 2009-12-31.
Short Answer
You will learn how to parse a date and move it to the last day of its month in PHP. You will also see why DateTimeImmutable is a reliable choice, how leap years are handled automatically, and how to format the result.
Concept
PHP's date and time classes understand the calendar, including months with 28, 29, 30, or 31 days. Instead of manually listing the number of days in each month, create a date object and ask PHP to modify it to the last day of this month.
DateTimeImmutable is especially useful because operations return a new object instead of changing the original date. This makes code easier to reason about and reduces accidental changes to shared values.
The expression last day of this month is a relative date-time instruction recognized by PHP's date parser. It works correctly for:
- February in regular years: 28 days
- February in leap years: 29 days
- Months with 30 days
- Months with 31 days
For example, PHP knows that the last day of February 2024 is February 29, while the last day of February 2023 is February 28.
Mental Model
Think of a date as a page on a calendar. Starting from any page in November, the instruction last day of this month means: “Stay in this month, then turn forward until you reach its final page.”
You do not need to know how many pages the month has. PHP checks the calendar rules for you.
Syntax and Examples
Create a DateTimeImmutable object from the input, call modify(), then format the result:
<?php
$aDate = "2009-11-23";
$date = new DateTimeImmutable($aDate);
$lastDay = $date->modify('last day of this month');
echo $lastDay->format('Y-m-d'); // 2009-11-30
format('Y-m-d') produces a date in ISO-style form:
Y— four-digit year, such as2009m— two-digit month, such as11d— two-digit day, such as30
The same code works for December:
<?php
$aDate = ;
= ( ())
->();
->();
Step by Step Execution
Consider this code:
<?php
$input = '2024-02-10';
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $input);
$lastDay = $date->modify('last day of this month');
$result = $lastDay->format('Y-m-d');
echo $result;
Step by step:
$inputstores the text date2024-02-10.createFromFormat('!Y-m-d', $input)parses that text as a year-month-day date at00:00:00.modify('last day of this month')uses February 2024 from the parsed date and finds that month's final day.- Since 2024 is a leap year, the final day is February 29.
format('Y-m-d')converts the date object back to the string2024-02-29.echoprints2024-02-29.
Real World Use Cases
Finding month-end dates is common in applications that work with periods and reports:
- Billing systems: calculate the end of a billing month.
- Financial reports: define a report range from the first to the last day of a selected month.
- Subscriptions: determine when a monthly plan period ends.
- Dashboards: query data such as signups or sales for the current month.
- Scheduled jobs: run a month-end reconciliation task on the correct date.
- Form validation: ensure a selected date falls within a month-based reporting period.
Real Codebase Usage
In production code, developers usually put date calculations in a small function so that parsing, validation, formatting, and time-zone choices are consistent.
A reusable function can return null for invalid input instead of silently continuing:
<?php
function lastDayOfMonth(string $input, DateTimeZone $timezone): ?string
{
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $input, $timezone);
$errors = DateTimeImmutable::getLastErrors();
if ($date === false || ($errors !== false && ($errors['warning_count'] > 0 || $errors['error_count'] > 0))) {
return null;
}
return $date
->modify('last day of this month')
->format();
}
Common Mistakes
Manually hard-coding month lengths
This approach fails for February and is easy to maintain incorrectly:
// Fragile: February does not always have 28 days.
$daysInMonth = [1 => 31, 2 => 28, 3 => 31];
Use PHP's calendar-aware date handling instead:
$lastDay = (new DateTimeImmutable('2024-02-10'))
->modify('last day of this month');
Assuming February always has 28 days
This is incorrect in leap years:
// Incorrect for 2024.
$lastDay = '2024-02-28';
PHP correctly calculates 2024-02-29 when using modify().
Using an ambiguous input format
A string such as 03/04/2024 can mean different dates depending on regional convention. Prefer Y-m-d for data interchange:
Comparisons
| Approach | Best use | Important behavior |
|---|---|---|
DateTimeImmutable->modify('last day of this month') | Most application code | Returns a new date object; original stays unchanged. |
DateTime->modify('last day of this month') | Code intentionally mutating one object | Changes the existing object. |
cal_days_in_month() | You only need the number of days | Returns an integer, not a date. |
| Manual month-length array | Avoid | Easy to mishandle February and leap years. |
If you only need a day count, cal_days_in_month() can be appropriate:
<?php
$days = cal_days_in_month(CAL_GREGORIAN, 2, 2024);
;
Cheat Sheet
// Recommended: input date to last date of its month
$lastDay = (new DateTimeImmutable('2024-11-23'))
->modify('last day of this month')
->format('Y-m-d');
// 2024-11-30
// Parse a known YYYY-MM-DD format explicitly
$date = DateTimeImmutable::createFromFormat('!Y-m-d', '2024-02-10');
$lastDay = $date->modify('last day of this month');
| Need | Use |
|---|---|
| Last calendar date | modify('last day of this month') |
| First calendar date | modify('first day of this month') |
| Number of days |
FAQ
How do I get the last day of the current month in PHP?
$lastDay = (new DateTimeImmutable('now'))
->modify('last day of this month')
->format('Y-m-d');
Does PHP handle leap years when finding the last day of February?
Yes. modify('last day of this month') uses PHP's calendar rules, so February 2024 ends on 2024-02-29 and February 2023 ends on 2023-02-28.
What is the difference between DateTime and DateTimeImmutable?
DateTime changes its own value when modified. DateTimeImmutable returns a new object and keeps the original unchanged.
Can I get the number of days in a month instead of a date?
Yes:
$days = cal_days_in_month(CAL_GREGORIAN, 11, 2009);
// 30
Why should I use dates?
Mini Project
Description
Build a small PHP function that accepts a YYYY-MM-DD date and returns the inclusive start and end dates of that month. This is useful when filtering monthly reports, invoices, or database records.
Goal
Create a validated monthly date-range helper that correctly handles every month, including leap-year February.
Requirements
Create a function that accepts a date string in YYYY-MM-DD format.
Validate the input before performing date calculations.
Return the first and last day of the input date's month.
Format both returned dates as YYYY-MM-DD.
Test the function with February 2024 and November 2009.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.