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...
}

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

 J.Mengelle06 июл. 2016 г., 11:31
Я не думаю, что есть другой способ, кроме итерации.
 Fildor06 июл. 2016 г., 11:32
Я мог предположить, что мог бы быть некоторый причудливый поток Java8 - способ получить список тех дат ..., но я не слишком знаком с этим новым API.
 Thermech07 дек. 2016 г., 16:39
 Fildor06 июл. 2016 г., 11:30
Вы также можете увеличить начальную дату, если она меньше конечной даты. (Предполагая, что начало всегда меньше конца для начала.)

Ответы на вопрос(9)

Вы могли бы использоватьСпектр функциональность в Googleгуайява библиотека. После определенияDiscreteDomain над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);
Решение Вопроса

Предполагая, что вы в основном хотите выполнять итерации по диапазону дат, имеет смысл создать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, возможно, иметь статический конструктор factory + private, чтобы соответствовать стилю кодирования Java time API и т. Д.

 assylias26 сент. 2018 г., 14:35
@Holger - это комментарий к производительности или читабельность?
 Holger26 сент. 2018 г., 15:04
Немного и того и другого, хотя читабельность субъективна. Но он создает размерный поток, который может иметь небольшие преимущества при итерации, но существенные с такими операциями, какtoArray, count() (очевидно) и с параллельными потоками. И это ближе к оригинальной петле ОП.
 assylias07 июл. 2016 г., 11:44
@ Flown Быть проверенным ;-)
 Patrick06 июл. 2016 г., 16:18
Это чистый подход, чтобы иметь логику в своем классе. Спасибо, что указали мне в этом направлении!
 Dalton19 мар. 2019 г., 14:16
Это лучший ответ для этого случая, потому что вам не придется иметь дело с конечными переменными, как если бы вы использовали потоковый подход.
 Holger26 сент. 2018 г., 14:02
LongStream.rangeClosed(0, ChronoUnit.DAYS.between(startDate, endDate)) .mapToObj(startDate::plusDays)
 assylias07 июл. 2016 г., 10:08
@ Флоун, я мог (см. Комментарий к методу toList). Это выдержка из более крупного класса, и я думаю, что причина не в производительности (не уверен, если это будет реально, если честно).
 Flown07 июл. 2016 г., 10:23
@assylias Я увидел комментарий позже и удалил свой комментарий. Вы действительно думаете, что производительность является проблемой в этом случае?

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

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

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

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

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

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

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

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

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

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

Java 9

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

List<LocalDate> dates = startDate.datesUntil(endDate).collect(Collectors.toList());
 membersound26 июн. 2019 г., 16:20
Следует отметить, чтоdatesUntil является эксклюзивным.

Сначала вы можете использоватьTemporalAdjuster чтобы получить последний день месяца. ДалееStream API предлагает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);
 riccardo.cardin24 нояб. 2017 г., 14:58
Вау! Я смог заменить огромныйdo...while с этим трюком. Спасибо большое.
 Patrick06 июл. 2016 г., 11:45
Спасибо за ваше очень хорошее решение. Будет использовать его, когда мне все еще нужен список.
 user40593530 июл. 2017 г., 10:53
Так должно быть.limit(ChronoUnit.DAYS.between(start, end) + 1) включить последний день каждого месяца.
 Flown30 июл. 2017 г., 20:33
@ user405935 Этот вопрос больше года и должен найти решение. Пользователь оставляет за собой право адаптировать этот фрагмент в соответствии с требованиями.
 Flown06 июл. 2016 г., 11:48
@ Патрик, ты не уточнил, как должны выглядеть результаты, это было только предположение. Вам также не нужно хранить результаты, вместо этого вы можете использоватьStream::forEach обработать результаты.

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

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

Пример здесь

 N.J.Dawson06 июл. 2016 г., 14:39
Не волнуйтесь, первым делом я скопировал его в мою среду IDE и добавил несколько строк / отступов. Я не верил, что форматирование представленных комментариев было ожидаемым результатом.
 Holger06 июл. 2016 г., 13:15
Почему неfor цикл? У вас есть начальный оператор, условие и операция приращения, которая точно соответствует сценарию использованияfor петля, т.е.for(LocalDate startDate = LocalDate.now(), endDate = startDate.plusMonths(1); !startDate.isAfter(endDate); startDate = startDate.plusDays(1)) { System.out.println(startDate); }
 N.J.Dawson06 июл. 2016 г., 13:24
@ Хольгер, я задам тебе противоположный вопрос. Почемуfor цикл? Что вы получаете отfor цикл? Производительность или предпочтение?
 Holger06 июл. 2016 г., 14:24
Что ж, отсутствие форматирования в комментариях затрудняет чтение каждого нетривиального фрагмента кода.
 N.J.Dawson06 июл. 2016 г., 13:34
@Holger Лично я не согласен с утверждением «делает код понятным», мне было сложнее понять сегмент кода, который вы предоставили, потому что мой стиль программирования - возможно, это ваш случай, но определенно не единое утверждение. Хотя я не согласен с вами по этой теме - вы высказали очень хорошие замечания, если к этому я привык, но я редко вижу, чтобы люди писали цикл таким образом (ссылаясь на учебные пособия и, как правило, на других пользователей), так что ваша реализация кажется очень чуждым для меня.
 Holger06 июл. 2016 г., 13:28
Как сказано,for цикл является предполагаемой синтаксической конструкцией для этого варианта использования. Наличие четко определенного шаблона для начального значения, условия и приращения делает код понятным. Эти три взаимосвязанные вещи также всегда вместе, независимо от размера тела цикла. Вwhile В цикле нет способа различить операцию приращения цикла, обычно находящуюся где-то в конце, и фактическое действие в теле цикла, кроме того, переменные цикла должны быть объявлены вне цикла, что приводит к большей области действия, чем предполагалось.
ТЛ; др

Расширение на хорошееОтвет Сингхом, используя поток из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 поток

Начиная с 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 фреймворк встроен в Java 8 и выше. Эти классы вытесняют хлопотное староенаследие классы даты и времени, такие какjava.util.Date, Calendar&SimpleDateFormat.

Joda времени проект, сейчас вРежим технического обслуживанияПосоветует миграцию на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Более поздние версии реализации пакета Androidjava.time классы.Для более ранней версии Android (<26)ThreeTenABP проект адаптируетсяThreeTen-Backport (упомянутое выше). УвидетьКак использовать ThreeTenABP ....

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

Ваш ответ на вопрос