Java - создать группу дат по месяцам из 2-х дат - PullRequest
0 голосов
/ 25 октября 2018

Используя Java 8

Цель

Из двух дат (например: firstDay 2018-09-01 и lastDay 2018-11-10), я хотел бысоздать два массива firstDay и lastDay, созданных по месяцам.Например:

List<LocalDate> firstDays = [2018-09-01,2018-10-01,2018-11-01]
List<LocalDate> lastDays = [2018-09-30, 2018-10-31,2018-11-10]

В конце концов, я бы хотел, чтобы этот метод применялся также и к годам (например: firstDay 2018-12-10 и lastDay 2019-01-06).

Issue

Теперь я не знаю, что использовать для достижения этой цели.Я все еще ищу.Не могли бы вы мне помочь, пожалуйста?

Ответы [ 3 ]

0 голосов
/ 25 октября 2018

В итеративном стиле и обработки крайних случаев:

LocalDate startDate = LocalDate.of(2018, 9, 1);
LocalDate endDate = LocalDate.of(2018, 11, 10);

List<LocalDate> firstDays = new ArrayList<>();
List<LocalDate> lastDays = new ArrayList<>();

LocalDate firstOfMonth = startDate.withDayOfMonth(1);
LocalDate lastOfMonth = startDate.withDayOfMonth(startDate.lengthOfMonth());

while (firstOfMonth.isBefore(endDate)) {
    firstDays.add(firstOfMonth.isBefore(startDate) ? startDate : firstOfMonth);
    lastDays.add(endDate.isBefore(lastOfMonth) ? endDate : lastOfMonth);

    firstOfMonth = firstOfMonth.plus(1, ChronoUnit.MONTHS);
    lastOfMonth = firstOfMonth.withDayOfMonth(firstOfMonth.lengthOfMonth());
}

System.out.println(firstDays);
System.out.println(lastDays);

Вывод:

[2018-09-01, 2018-10-01, 2018-11-01]
[2018-09-30, 2018-10-31, 2018-11-10]
0 голосов
/ 25 октября 2018

Уже есть несколько хороших ответов.Позвольте мне посмотреть, смогу ли я еще придумать что-нибудь элегантное.

Создайте только один список объектов

Во-первых, я предлагаю вам не использовать два списка.Это анти-шаблон, описанный здесь: Анти-шаблон: параллельные коллекции .Поскольку каждый первый день принадлежит соответствующему последнему дню из другого списка, все будет более удобным и менее подверженным ошибкам, когда вы будете хранить их вместе.Для этой цели вы можете использовать какой-нибудь библиотечный класс или разработать свой собственный:

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 поможет найти элегантное решение.Однако я понял, что это потребовало бы специальной обработки первого и последнего месяца, поэтому я не думаю, что это могло бы привести к созданию более элегантного кода, чем тот, который мы описали выше.Вы можете попробовать, если хотите.

0 голосов
/ 25 октября 2018

Вы можете использовать plusMonth примерно так:

LocalDate firstDay = LocalDate.parse("2018-09-01");
LocalDate lastDay = LocalDate.parse("2018-11-10");

Long timeBetween = ChronoUnit.MONTHS.between(firstDay, lastDay);

// First day of firstDays is not changeable so add it like it is
List<LocalDate> firstDays = new ArrayList<>(Arrays.asList(firstDay));

// Generate the dates between the start and end date
firstDays.addAll(LongStream.rangeClosed(1, timeBetween)
        .mapToObj(f -> firstDay.withDayOfMonth(1).plusMonths(f))
        .collect(Collectors.toList()));

// For the lastDays, generate the dates
List<LocalDate> lastDays = LongStream.range(0, timeBetween)
        .mapToObj(f -> {
            LocalDate newDate = firstDay.plusMonths(f);
            return newDate.withDayOfMonth(newDate.lengthOfMonth());
        }).collect(Collectors.toList());
// Last day of lastDays is not changeable so add it like it is
lastDays.add(lastDay);

Выход

[2018-09-01, 2018-10-01, 2018-11-01]
[2018-09-30, 2018-10-31, 2018-11-10]
...