it-swarm.com.ru

Java 8 LocalDate - Как мне получить все даты между двумя датами?

Есть ли возможность получить все даты между двумя датами в новом Java.time API?

Допустим, у меня есть эта часть кода:

@Test
public void testGenerateChartCalendarData() {
    LocalDate startDate = LocalDate.now();

    LocalDate endDate = startDate.plusMonths(1);
    endDate = endDate.withDayOfMonth(endDate.lengthOfMonth());
}

Теперь мне нужны все даты между startDate и endDate.

Я думал получить daysBetween двух дат и повторить:

long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

for(int i = 0; i <= daysBetween; i++){
    startDate.plusDays(i); //...do the stuff with the new date...
}

Есть ли лучший способ получить даты?

43
Patrick

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

for (LocalDate d : DateRange.between(startDate, endDate)) ...

Что-то вроде:

public class DateRange implements Iterable<LocalDate> {

  private final LocalDate startDate;
  private final LocalDate endDate;

  public DateRange(LocalDate startDate, LocalDate endDate) {
    //check that range is valid (null, start < end)
    this.startDate = startDate;
    this.endDate = endDate;
  }

  @Override
  public Iterator<LocalDate> iterator() {
    return stream().iterator();
  }

  public Stream<LocalDate> stream() {
    return Stream.iterate(startDate, d -> d.plusDays(1))
                 .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1);
  }

  public List<LocalDate> toList() { //could also be built from the stream() method
    List<LocalDate> dates = new ArrayList<> ();
    for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
      dates.add(d);
    }
    return dates;
  }
}

Было бы целесообразно добавить методы equals & hashcode, getters, возможно, иметь статическую фабрику + приватный конструктор для соответствия стилю кодирования Java time API и т.д.

58
assylias

Сначала вы можете использовать TemporalAdjuster, чтобы получить последний день месяца. Затем API Stream предлагает Stream::iterate , который является подходящим инструментом для вашей проблемы.

LocalDate start = LocalDate.now();
LocalDate end = LocalDate.now().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
List<LocalDate> dates = Stream.iterate(start, date -> date.plusDays(1))
    .limit(ChronoUnit.DAYS.between(start, end))
    .collect(Collectors.toList());
System.out.println(dates.size());
System.out.println(dates);
58
Flown

Java 9

В Java 9 класс LocalDate был улучшен с помощью метода LocalDate.datesUntil (LocalDate endExclusive) , который возвращает все даты в диапазоне дат в виде Stream<LocalDate>.

List<LocalDate> dates = startDate.datesUntil(endDate).collect(Collectors.toList());
15
Durgpal Singh

Вы можете использовать .isAfter и .plusDays, чтобы сделать это через цикл. Я бы не сказал лучше, поскольку я не провел большого количества исследований по этой теме, но могу с уверенностью сказать, что он использует API Java 8 и является альтернативой small.

LocalDate startDate = LocalDate.now();
LocalDate endDate = startDate.plusMonths(1);
while (!startDate.isAfter(endDate)) {
 System.out.println(startDate);
 startDate = startDate.plusDays(1);
}

Результат

2016-07-05
2016-07-06
...
2016-08-04
2016-08-05

Пример здесь

8
N.J.Dawson

Вы можете создать stream из LocalDate объектов. У меня тоже была эта проблема, и я опубликовал свое решение как Java-timestream на github.

Используя ваш пример ...

LocalDateStream
    .from(LocalDate.now())
    .to(1, ChronoUnit.MONTHS)
    .stream()
    .collect(Collectors.toList());

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

6
Todd

Библиотека ThreeTen-Extra имеет класс LocalDateRange , который может выполнять именно то, что вы запрашиваете:

LocalDateRange.ofClosed(startDate, endDate).stream()
        .forEach(/* ...do the stuff with the new date... */);
2
M. Justin

тЛ; др

Расширение на хороший Ответ Сингх , используя поток из datesUntil в Java 9 и позже.

today                                 // Determine your beginning `LocalDate` object.
.datesUntil(                          // Generate stream of `LocalDate` objects.
    today.plusMonths( 1 )             // Calculate your ending date, and ask for a stream of dates till then.
)                                     // Returns the stream.
.collect( Collectors.toList() )       // Collect your resulting dates in a `List`. 
.toString()                           // Generate text representing your found dates.

[2018-09-20, 2018-09-21, 2018-09-22, 2018-09-23, 2018-09-24, 2018-09-25, 2018-09-26, 2018-09-27, 2018 -09-28, 2018-09-29, 2018-09-30, 2018-10-01, 2018-10-02, 2018-10-03, 2018-10-04, 2018-10-05, 2018-10 -06, 2018-10-07, 2018-10-08, 2018-10-09, 2018-10-10, 2018-10-11, 2018-10-12, 2018-10-13, 2018-10-14 , 2018-10-15, 2018-10-16, 2018-10-17, 2018-10-18, 2018-10-19]

LocalDate::datesUntil stream

Начиная с Java 9, вы можете запросить поток дат. Позвонить LocalDate::datesUntil .

Начните с определения сегодняшней даты. Это требует часового пояса. В любой момент времени дата меняется по всему земному шару в зависимости от зоны.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
LocalDate today = LocalDate.now( z ) ;

Определите дату окончания.

LocalDate stop = today.plusMonths( 1 ) ;

Запросите поток дат от начала до конца.

Stream< LocalDate > stream = today.datesUntil( today.plusMonths( 1 ) );

Вытащите даты из этого потока, собрав их в List

List< LocalDate > datesForMonthFromToday = stream.collect( Collectors.toList() );

Распечатайте наш список дат, генерируя текст в стандартном ISO 8601 формате.

System.out.println( datesForMonthFromToday );

О Java.time

Java.time framework встроен в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые унаследованные классы даты и времени, такие как Java.util.Date , Calendar , & SimpleDateFormat .

Проект Joda-Time , теперь находящийся в режиме обслуживания , рекомендует выполнить переход к Java.time классам.

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация это JSR 310 .

Вы можете обмениваться объектами Java.time непосредственно с вашей базой данных. Используйте драйвер JDBC совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в классах Java.sql.*.

Где взять классы Java.time? 

  • Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11 и более поздние версии - Часть стандартного Java API с комплексной реализацией .
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности Java.time перенесена в Java 6 & 7 в ThreeTen-Backport .
  • Android

Проект ThreeTen-Extra расширяет Java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к Java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval , YearWeek , YearQuarter , и more .

1
Basil Bourque

В моей временной библиотеке Time4J я написал оптимизированный сплитератор для создания потока календарных дат с хорошими характеристиками распараллеливания. Адаптировано к вашему варианту использования:

LocalDate start = ...;
LocalDate end = ...;

Stream<LocalDate> stream = 
  DateInterval.between(start, end) // closed interval, else use .withOpenEnd()
    .streamDaily()
    .map(PlainDate::toTemporalAccessor);

Этот короткий подход может быть интересной отправной точкой, если вы также заинтересованы в связанных функциях, таких как тактовые интервалы на календарную дату (разделенные потоки) или других функциях интервалов и хотите избежать неуклюжего рукописного кода, см. Также API DateInterval .

1
Meno Hochschild

Вы можете использовать функциональность Range в библиотеке Google Guava . После определения DiscreteDomain over LocalDate вы можете получить ContiguousSet всех дат в диапазоне.

LocalDate d1 = LocalDate.parse("2017-12-25");
LocalDate d2 = LocalDate.parse("2018-01-05");

DiscreteDomain<LocalDate> localDateDomain = new DiscreteDomain<LocalDate>() {
    public LocalDate next(LocalDate value) { return value.plusDays(1); }
    public LocalDate previous(LocalDate value) { return value.minusDays(1); }
    public long distance(LocalDate start, LocalDate end) { return start.until(end, ChronoUnit.DAYS); }
    public LocalDate minValue() { return LocalDate.MIN; }
    public LocalDate maxValue() { return LocalDate.MAX; }
};

Set<LocalDate> datesInRange = ContiguousSet.create(Range.closed(d1, d2), localDateDomain);
0
M. Justin