Получение начальной и конечной даты последнего месяца в Java

Узнайте, как легко найти начало и конец прошлого месяца. Разбираем старый Calendar и современный LocalDate — код готов к копированию!

Работа с датами и временем — одна из самых распространенных задач в программировании на Java․ Часто разработчикам требуется получить временной диапазон для выборки записей из базы данных, формирования отчетов или фильтрации коллекций․ В этой статье мы подробно разберем, как получить начальную и конечную даты последнего месяца, используя классический класс Calendar, а также рассмотрим более современные подходы, появившиеся в Java 8․

Использование класса Calendar (Java 7 и старше)

До выхода Java 8 основным инструментом для манипуляций с датами был класс java․util․Calendar․ Несмотря на то, что он считается устаревшим и имеет ряд известных недостатков (таких как изменяемость объектов и индексация месяцев с нуля), он до сих пор встречается во многих legacy-проектах․

Чтобы получить диапазон дат за последний месяц, нам нужно выполнить следующие шаги:

  1. Получить текущий экземпляр календаря․
  2. Откатить месяц на один назад․
  3. Установить первый день месяца и начальное время (00:00:00)․
  4. Получить конец месяца и установить максимальное время (23:59:59)․

Пример кода с Calendar

Ниже приведен подробный пример, как это можно реализовать:

Важно помнить: В классе Calendar месяцы нумеруются с нуля (январь — 0, декабрь — 11)․ Класс Calendar автоматически обрабатывает переполнение, поэтому если текущий месяц январь (0), то вычитание единицы корректно переведет дату на декабрь предыдущего года․


import java․util․Calendar;
import java․text․SimpleDateFormat;
import java․util․Date;

public class LastMonthCalendarExample {
 public static void main(String[] args) {
 // Получаем текущую дату
 Calendar calendar = Calendar․getInstance;
 
 // Откатываемся на один месяц назад
 calendar․add(Calendar․MONTH, -1);
 
 // Устанавливаем первый день месяца
 calendar․set(Calendar․DAY_OF_MONTH, 1);
 
 // Сбрасываем время на начало дня (00:00:00․000)
 calendar․set(Calendar․HOUR_OF_DAY, 0);
 calendar․set(Calendar․MINUTE, 0);
 calendar․set(Calendar․SECOND, 0);
 calendar․set(Calendar․MILLISECOND, 0);
 
 Date startOfLastMonth = calendar․getTime;
 
 // Переходим к последнему дню месяца
 calendar․set(Calendar․DAY_OF_MONTH, calendar․getActualMaximum(Calendar․DAY_OF_MONTH));
  // Устанавливаем конец дня (23:59:59․999)
 calendar․set(Calendar․HOUR_OF_DAY, 23);
 calendar․set(Calendar․MINUTE, 59);
 calendar․set(Calendar․SECOND, 59);
 calendar․set(Calendar․MILLISECOND, 999);
 
 Date endOfLastMonth = calendar․getTime;
 
 // Форматируем вывод для наглядности
 SimpleDateFormat format = new SimpleDateFormat("dd․MM․yyyy HH:mm:ss");
 System․out․println("Начало последнего месяца: " + format․format(startOfLastMonth));
 System․out․println("Конец последнего месяца: " + format․format(endOfLastMonth));
 }
}

Современный подход: Java 8 и java․time

Начиная с Java 8, был введен новый API java․time (JSR 310), который лишен недостатков Calendar․ Если вы работаете с современными версиями Java, настоятельно рекомендуется использовать классы LocalDate и LocalDateTime․

Для получения первого и последнего дня месяца в новом API используются специальные корректоры — TemporalAdjusters․

Пример с LocalDate и TemporalAdjusters


import java․time․LocalDate;
import java․time․LocalDateTime;
import java․time․LocalTime;
import java․time․format․DateTimeFormatter;
import java․time․temporal․TemporalAdjusters;

public class LastMonthJavaTimeExample {
 public static void main(String[] args) {
 // Текущая дата
 LocalDate today = LocalDate․now;
 
 // Откатываемся на месяц назад
 LocalDate lastMonth = today․minusMonths(1);
 
 // Получаем первый день прошлого месяца
 LocalDate firstDay = lastMonth․withDayOfMonth(1);
 
 // Получаем последний день прошлого месяца
 LocalDate lastDay = lastMonth․with(TemporalAdjusters․lastDayOfMonth);
 
 // Если нужны точные временные метки (например, для БД)
 LocalDateTime startDateTime = LocalDateTime․of(firstDay, LocalTime․MIN); // 00:00:00
 LocalDateTime endDateTime = LocalDateTime;of(lastDay, LocalTime․MAX); // 23:59:59․999999999
 
 DateTimeFormatter formatter = DateTimeFormatter․ofPattern("dd․MM․yyyy HH:mm:ss");
 System․out․println("Начало: " + startDateTime․format(formatter));
 System․out․println("Конец: " + endDateTime․format(formatter));
 }
}


Практическое применение: Фильтрация записей

Получив временной диапазон, вы можете использовать его для фильтрации списка записей․ Допустим, у нас есть список объектов с полем creationDate, и мы хотим оставить только те, которые были созданы в прошлом месяце․

Пример фильтрации


import java․util․ArrayList;
import java․util․List;
import java․util․Calendar;
import java․util․Date;
public class RecordFilter {

 public static void main(String[] args) {
 // Получаем границы прошлого месяца (используем код из примера выше)
 Calendar cal = Calendar․getInstance;
 cal․add(Calendar․MONTH, -1);
 cal․set(Calendar․DAY_OF_MONTH, 1);
 cal․set(Calendar․HOUR_OF_DAY, 0);
 cal․set(Calendar․MINUTE, 0);
 cal․set(Calendar․SECOND, 0);
 Date startDate = cal․getTime;
 
 cal․set(Calendar․DAY_OF_MONTH, cal․getActualMaximum(Calendar․DAY_OF_MONTH));
 cal․set(Calendar․HOUR_OF_DAY, 23);
 cal․set(Calendar․MINUTE, 59);
 cal․set(Calendar․SECOND, 59);
 Date endDate = cal․getTime;
 
 // Список записей (заполняем тестовыми данными)
 List<Record> records = getTestRecords;
 
 // Фильтруем
 List<Record> lastMonthRecords = new ArrayList<>;
 for (Record record : records) {
 // Проверяем, попадает ли дата записи в диапазон
 if (!record․getCreationDate․before(startDate) && 
 !record․getCreationDate․after(endDate)) {
 lastMonthRecords․add(record);
 }
 } 
 System․out․println("Найдено записей за последний месяц: " + lastMonthRecords․size);
 } 
 // Заглушка для метода получения записей
 static List<Record> getTestRecords {
 return new ArrayList<>; 
 }
}

// Простой класс записи
class Record {
 private Date creationDate;
 
 public Date getCreationDate {
 return creationDate;
 }

 
 public void setCreationDate(Date date) {
 this․creationDate = date;
 }
}

Получение записей за определенный период, базовая задача, решение которой зависит от версии Java, которую вы используете․

Если вы поддерживаете старый код, класс Calendar в связке с методами add и getActualMaximum поможет вам получить нужные границы․ Однако для новых проектов однозначно стоит выбирать java․time, так как он предоставляет более безопасный, читаемый и мощный функционал для работы с временными интервалами․