Уже есть несколько хороших ответов.Позвольте мне посмотреть, смогу ли я еще придумать что-нибудь элегантное.
Создайте только один список объектов
Во-первых, я предлагаю вам не использовать два списка.Это анти-шаблон, описанный здесь: Анти-шаблон: параллельные коллекции .Поскольку каждый первый день принадлежит соответствующему последнему дню из другого списка, все будет более удобным и менее подверженным ошибкам, когда вы будете хранить их вместе.Для этой цели вы можете использовать какой-нибудь библиотечный класс или разработать свой собственный:
public class DateInterval {
LocalDate firstDay;
LocalDate lastDay;
public DateInterval(LocalDate firstDay, LocalDate lastDay) {
if (lastDay.isBefore(firstDay)) {
throw new IllegalArgumentException("Dates in wrong order");
}
this.firstDay = firstDay;
this.lastDay = lastDay;
}
// getters and other stuff
@Override
public String toString() {
return "" + firstDay + " - " + lastDay;
}
}
С этим классом нам нужен только один список:
LocalDate firstDayOverall = LocalDate.of(2018, Month.DECEMBER, 10);
LocalDate lastDayOverall = LocalDate.of(2019, Month.JANUARY, 6);
if (lastDayOverall.isBefore(firstDayOverall)) {
throw new IllegalStateException("Overall dates in wrong order");
}
LocalDate firstDayOfLastMonth = lastDayOverall.withDayOfMonth(1);
LocalDate currentFirstDay = firstDayOverall;
List<DateInterval> intervals = new ArrayList<>();
while (currentFirstDay.isBefore(firstDayOfLastMonth)) {
intervals.add(new DateInterval(currentFirstDay, currentFirstDay.with(TemporalAdjusters.lastDayOfMonth())));
currentFirstDay = currentFirstDay.withDayOfMonth(1).plusMonths(1);
}
intervals.add(new DateInterval(currentFirstDay, lastDayOverall));
System.out.println("Intervals: " + intervals);
Вывод из приведенного выше фрагмента кода:
Интервалы: [2018-12-10 - 2018-12-31, 2019-01-01 - 2019-01-06]
Особый случай первогои последний день в одном и том же месяце обрабатывается циклом, не повторяющим цикл вообще, и add
после цикла, добавляющим в список один интервал.
Если требуется два списка дат
Если вы настаиваете на двух списках, удобен метод с двумя аргументами datesUntil
:
List<LocalDate> firstDays = new ArrayList<>();
firstDays.add(firstDayOverall);
// first day not to be included in first days
LocalDate endExclusive = lastDayOverall.withDayOfMonth(1).plusMonths(1);
List<LocalDate> remainingFirstDays = firstDayOverall.withDayOfMonth(1)
.plusMonths(1)
.datesUntil(endExclusive, Period.ofMonths(1))
.collect(Collectors.toList());
firstDays.addAll(remainingFirstDays);
System.out.println("First days: " + firstDays);
// Calculate last days as the day before each first day except the first
List<LocalDate> lastDays = remainingFirstDays.stream()
.map(day -> day.minusDays(1))
.collect(Collectors.toCollection(ArrayList::new));
lastDays.add(lastDayOverall);
System.out.println("Last days: " + lastDays);
Вывод:
First days: [2018-12-10, 2019-01-01]
Last days: [2018-12-31, 2019-01-06]
В стороне
Сначала у меня было ощущение, что класс YearMonth
поможет найти элегантное решение.Однако я понял, что это потребовало бы специальной обработки первого и последнего месяца, поэтому я не думаю, что это могло бы привести к созданию более элегантного кода, чем тот, который мы описали выше.Вы можете попробовать, если хотите.