
Я терпеть не могу работать с датами в Java, потому что существует слишком много разных классов, которые я никак не могу запомнить и различить. Есть Календари, есть Даты, есть Локальные даты, в старых версиях Джавы использовалось одно, теперь – другое, и везде у объектов разные методы, а ещё они легко между собой не совмещаются…
Но сегодня мне пришлось решать небольшую задачку по нахождению списка дат между двумя заданными датами. На работе поставили задачу реализовать сохранение ежедневных заявок «оптом». Эти заявки по содержанию часто оказываются одинаковыми, и пользователю нет смысла делать одно и то же десяток раз – во фронте я добавил поле для выбора интервала дат, и в бэке у меня теперь есть date1 (от) и date2 (до). Но как получить все даты между ними?
Получаем список дат между двумя датами с помощью java.util.Calendar
Самое простое решение, пожалуй, будет выглядеть таким образом:
List<Date> dates = new ArrayList<Date>();
Calendar calendar = new GregorianCalendar();
calendar.setTime(date1);
while (calendar.getTime().before(date2) || calendar.getTime().equals(date2))
{
Date temp = calendar.getTime();
dates.add(temp);
calendar.add(Calendar.DATE, 1);
}
Здесь всё между двумя объектами java.util.Date – date1 и date2 добавляется в коллекцию dates. Мы устанавливаем календарь на начальную дату, и прибавляем по одному дню, пока новая дата меньше или равна конечной (потому что мне нужен промежуток, вместе с конечной датой).
Меня лично Календарь всегда почему-то раздражал, поэтому я решил покопаться и найти другие решения этой проблемы.
Получаем список дат между двумя датами c помощью java.time.LocalDate
Чтобы добыть все даты, можно ещё воспользоваться классами из пакета java.time package. Для этого нам нужны две даты в формате LocalDate: назову их start и end. Тогда алгоритм прост:
List<LocalDate> dates = new ArrayList<>();
while (!start.isAfter(end)) {
dates.add(start);
start = start.plusDays(1);
}
В этом коде мы создаём список для хранения всех найденных дат, после чего в цикле день за днём двигаемся вперёд по календарю (наверно, не очень удачно я употребил это слово, потому что объекта типа Calendar тут нет). Когда полученная дата оказывается после конечной даты (end), цикл прекращается.
Есть и немного более элегантное решение, с помощью стрима.
Stream<LocalDate> datesStream = start.datesUntil(end.plusDays(1));
List<LocalDate> dates = datesStream.collect(Collectors.toList());
Вроде бы всё легко, но проблема в том, что мои даты (начальная и конечная) в формате Date (java.util.Date). То есть мне нужно как-то их преобразовать их этого формата в LocalDate, а потом преобразовать обратно, потому что каждую из дат необходимо сделать значением поля Дата объекта Заявка (исторически так сложилось, что там тоже используется java.util.date).
Как преобразовать java.util.Date в java.time.LocalDate и наоборот?
Чтобы преобразовать дату java.util.Date (переменная date) в java.time.LocalDate, нужен следующий код:
LocalDate localDate = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()
Можно также преобразовать java.util.Date в java.time.LocalDateTime:
LocalDateTime localDateTime = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
Чтобы сделать наоборот (у нас есть переменная localDate типа LocalDate) , необходим код:
Date date = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
Если же в наличии объект типа LocalDateTime, то код следующий:
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
На этом всё. На сегодня хватит манипуляций с датами.