Разделить диапазон дат и времени на равные интервалы в Java

В этом руководстве по дате и времени Java мы рассмотрим, как разделить диапазон даты и времени на равные интервалы. Концептуально, разделение диапазона даты и времени на равные интервалы подразумевает разделение всего диапазона на меньшие поддиапазоны одинакового размера. Короче говоря, нам нужно определить начальную и конечную точки каждого поддиапазона.

1. Разделение диапазона дат и времени на N равных интервалов

Давайте начнем с простого примера, где у нас есть начальная и конечная даты, и мы хотим разделить диапазон на N равных интервалов. Каждый интервал — это равноотстоящий временной отрезок с точностью до миллисекунд.

В нашей программе на Java мы использовали LocalDateTime для обозначения начальной и конечной точек, вы можете использовать любой другой класс по вашему выбору.

public static List<LocalDateTime> splitDateTimeRangeInEqualIntervals(LocalDateTime start, LocalDateTime end, int n) {Duration range = Duration.between(start, end);Duration interval = range.dividedBy(n - 1);List<LocalDateTime> listOfDates = new ArrayList<>();LocalDateTime timeline = start;for(int i = 0; i < n - 1; i++) {listOfDates.add(timeline);timeline = timeline.plus(interval);}listOfDates.add(end);return listOfDates;}

Вышеуказанный метод принимает три параметра: start(начальная точка диапазона), end(конечная точка диапазона) и n(количество интервалов для разбиения диапазона). Он возвращает список, содержащий разделенные интервалы даты и времени.

Сначала мы вычисляем общую длительность между начальной и конечной точками с помощью метода Duration.between(), а затем вычисляем длительность интервала, разделив общую длительность диапазона на количество интервалов минус один. Наконец, мы увеличиваем временную шкалу на длительность интервала и захватываем конкретный экземпляр LocalDateTime в этой точке.

Мы можем использовать splitDateTimeRangeInEqualIntervals() следующим образом:

int equalParts = 15;LocalDateTime start = LocalDateTime.of(2021, 10, 1, 0, 0, 0);LocalDateTime end = LocalDateTime.of(2022, 03, 31, 0, 0, 0);List<LocalDateTime> intervals = splitDateTimeRangeInEqualIntervals(start, end, equalParts);

2. Разделение диапазона даты и времени на части даты

Несколько раз нам может понадобиться разделить диапазон даты и времени таким образом, чтобы каждый поддиапазон содержал определенную часть даты, например день, неделю, месяц или год. Это может понадобиться в пакетных приложениях, где вы хотите разделить записи на основе временных интервалов и обрабатывать записи меньшими пакетами.

Независимо от типа диапазона, процесс поиска всех подинтервалов остается тем же. Мы начинаем с начальной точки и постепенно добавляем определенный интервал(день, месяц, год и т. д.), пока не достигнем конечной точки. Мы собираем все эти промежуточные временные метки в списке, который представляет поддиапазоны.

2.1. Разделить диапазон дат и времени на дни

Следующая программа Java использует метод LocalDate.plusDays() для прохода по диапазонам дат, разделенным по дням.

public static List<LocalDate> split_LocalDate_Range_Into_Days(LocalDate startDate, LocalDate endDate) {long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate);return IntStream.iterate(0, i -> i + 1).limit(numOfDaysBetween).mapToObj(i -> startDate.plusDays(i)).collect(Collectors.toList());}

2.2. Разделить диапазон дат и времени на месяцы

Следующая программа Java использует метод LocalDate.plusMonths() для прохода по диапазонам дат, разделенным на месяцы. Мы собираем каждую точку поддиапазона как экземпляр YearMonth, где месяцы находятся в диапазоне от 1 до 12.

Мы намеренно изучаем дни, часы и минуты, поскольку они нам неинтересны.

We are intentionally learning the day, hour or minute parts as we are not interersted in them.public static List<YearMonth> split_LocalDate_Range_Into_Months(LocalDate startDate, LocalDate endDate) {long numOfDaysBetween = ChronoUnit.MONTHS.between(startDate, endDate);return IntStream.iterate(0, i -> i + 1).limit(numOfDaysBetween).mapToObj(i -> YearMonth.from(startDate.plusMonths(i))).collect(Collectors.toList());}

2.3. Разделить диапазон дат и времени на годы

Подобно предыдущей программе, мы можем разбить диапазон даты-времени на годы. Мы собираем диапазон в класс Year и используем метод LocalDate.plusYears() для прохождения по диапазону.

public static List<Year> split_LocalDate_Range_Into_Years(LocalDate startDate, LocalDate endDate) {long numOfDaysBetween = ChronoUnit.YEARS.between(startDate, endDate);return IntStream.iterate(0, i -> i + 1).limit(numOfDaysBetween).mapToObj(i -> Year.from(startDate.plusYears(i))).collect(Collectors.toList());} 

2.4. Разделить диапазон дат и времени на недели

Разделение диапазона дат и времени на недели — непростая задача, поскольку у нас нет точного представления недели, как у месяцев, лет и других периодов времени.

По этой причине мы начинаем с создания новой записи, которая может хранить информацию о неделе и годе, представляющую конкретную неделю конкретного года.

record YearWeek(int year, int week) {//...}

Далее, подобно другим классам JDK, мы добавим метод фабрики, который будет проверять входные данные и возвращать действительный экземпляр записи YearWeek. Кроме того, добавим toString(), который помогает в логировании и отладке.

record YearWeek(int year, int week) {public static YearWeek from(TemporalAccessor temporal) {Objects.requireNonNull(temporal, "temporal");try {if(IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {temporal = LocalDate.from(temporal);}return new YearWeek(temporal.get(YEAR), temporal.get(ALIGNED_WEEK_OF_YEAR));} catch(DateTimeException ex) {throw new DateTimeException(STR."Unable to obtain YearWeek from TemporalAccessor: \{temporal} of type \{temporal.getClass().getName()}", ex);}}@Overridepublic String toString() {return STR."\{year}-\{week}";}}

Далее мы готовы изменить предыдущую логику и использовать метод LocalDate.plusWeeks() для пошагового просмотра недель.

public static List<YearWeek> split_LocalDate_Range_Into_Weeks(LocalDate startDate, LocalDate endDate) {long numOfDaysBetween = ChronoUnit.WEEKS.between(startDate, endDate);return IntStream.iterate(0, i -> i + 1).limit(numOfDaysBetween).mapToObj(i -> YearWeek.from(startDate.plusWeeks(i))).collect(Collectors.toList());}

3. Заключение

В этом коротком руководстве по Java обсуждаются различные методы разделения заданной длительности времени(начальной и конечной дат) на N равноотстоящих интервалов. Мы также научились получать интервалы в виде частей даты, которые обычно полезны при обработке пакетных записей.

Исходный код на Github

Прокрутить вверх