Как получить последний день месяца по дате в PHP?
Решение с помощью DateTime и DateInterval
Лучший способ получить последний день месяца в PHP — использовать классы DateTime и DateInterval. Они учитывают часовые пояса, високосные годы и работают с любыми форматами дат. Вот основной подход:
<?php
function getLastDayOfMonth($dateString, $format = 'Y-m-d') {
$date = new DateTime($dateString);
// Переходим на первый день следующего месяца
$date->modify('first day of next month');
// Отнимаем один день
$date->modify('-1 day');
return $date->format($format);
}
echo getLastDayOfMonth('2023-02-15'); // 2023-02-28
echo getLastDayOfMonth('2024-02-15'); // 2024-02-29 (високосный)
echo getLastDayOfMonth('2023-12-01'); // 2023-12-31
?>
Объяснение работы
- DateTime — мощный класс для работы с датами. Он автоматически парсит большинство распространённых форматов:
Y-m-d,d.m.Y,m/d/Yи даже относительные строки вродеnext month. - modify() — изменяет дату согласно строке модификатора.
'first day of next month'переводит нас на 1-е число следующего месяца, а'-1 day'возвращается на предыдущий день — последний день исходного месяца. - format() — выводит дату в нужном вам формате. По умолчанию
Y-m-d, но можно задать любой, напримерd.m.Y.
Обработка разных форматов ввода
Если дата приходит в нестандартном формате, используйте DateTime::createFromFormat():
<?php
function getLastDayCustomFormat($dateString, $inputFormat = 'd.m.Y') {
$date = DateTime::createFromFormat($inputFormat, $dateString);
if (!$date) {
throw new InvalidArgumentException('Неверный формат даты');
}
$date->modify('first day of next month')->modify('-1 day');
return $date->format('Y-m-d');
}
echo getLastDayCustomFormat('15.02.2023'); // 2023-02-28
echo getLastDayCustomFormat('02/15/2023', 'm/d/Y'); // 2023-02-28
?>
Учёт часовых поясов
По умолчанию DateTime использует часовой пояс сервера. Чтобы избежать проблем, явно задавайте его:
<?php
$timezone = new DateTimeZone('Europe/Moscow');
$date = new DateTime('2023-02-15', $timezone);
$date->modify('first day of next month')->modify('-1 day');
echo $date->format('Y-m-d'); // 2023-02-28
?>
Короткая альтернатива с date('t')
Если вам нужно только число (28 или 29), можно использовать date('t'), но для полной даты всё равно придётся формировать строку вручную:
<?php
$input = '2023-02-15';
$timestamp = strtotime($input);
$lastDayNumber = date('t', $timestamp);
$lastDayDate = date('Y-m', $timestamp) . '-' . $lastDayNumber;
echo $lastDayDate; // 2023-02-28
?>
Этот способ проще, но менее гибкий: он не обрабатывает некорректные даты и зависит от strtotime(), который может неверно интерпретировать некоторые форматы.
Рекомендации
- Всегда используйте
DateTimeдля серьёзных проектов — он надёжнее и читаемее. - Явно указывайте часовой пояс, если приложение работает с пользователями из разных регионов.
- Проверяйте входные данные через
DateTime::createFromFormat(), чтобы избежать ошибок.
Решение с помощью mktime() и date()
Если вы предпочитаете процедурный стиль без ООП, можно использовать комбинацию mktime() и date(). Идея та же: перейти на следующий месяц и отнять один день.
<?php
function getLastDayOfMonthProc($dateString) {
$timestamp = strtotime($dateString);
if ($timestamp === false) {
return false;
}
$year = date('Y', $timestamp);
$month = date('m', $timestamp);
// Получаем первый день следующего месяца
$nextMonth = $month == 12 ? mktime(0, 0, 0, 1, 1, $year + 1) : mktime(0, 0, 0, $month + 1, 1, $year);
// Отнимаем один день
$lastDayTimestamp = $nextMonth - 86400; // 86400 секунд = 1 день
return date('Y-m-d', $lastDayTimestamp);
}
echo getLastDayOfMonthProc('2023-02-15'); // 2023-02-28
?>
Важно: Этот метод уязвим к переходу на летнее/зимнее время, так как вычитание 86400 секунд может дать неверный результат при переводе часов. Для большинства случаев это не критично, но для критичных систем лучше использовать DateTime.
Когда это может быть полезно?
- Если вы работаете с очень старым кодом на PHP 5.2 и ниже, где
DateTimeне так стабилен. - Если нужно быстрое решение без подключения дополнительных расширений.
В современных проектах (PHP 7 и выше) настоятельно рекомендую первый вариант с DateTime — он безопаснее и проще в поддержке.