< prev index next >

src/java.base/share/classes/java/time/LocalDate.java

Print this page

        

*** 98,107 **** --- 98,109 ---- import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.zone.ZoneOffsetTransition; import java.time.zone.ZoneRules; import java.util.Objects; + import java.util.stream.LongStream; + import java.util.stream.Stream; /** * A date without a time-zone in the ISO-8601 calendar system, * such as {@code 2007-12-03}. * <p>
*** 1714,1723 **** --- 1716,1808 ---- int months = (int) (totalMonths % 12); // safe return Period.of(Math.toIntExact(years), months, days); } /** + * Returns a sequential ordered stream of dates. The returned stream starts from this date + * (inclusive) and goes to {@code endExclusive} (exclusive) by an incremental step of 1 day. + * <p> + * This method is equivalent to {@code datesUntil(endExclusive, Period.ofDays(1))}. + * + * @param endExclusive the end date, exclusive, not null + * @return a sequential {@code Stream} for the range of {@code LocalDate} values + * @throws IllegalArgumentException if end date is before this date + * @since 9 + */ + public Stream<LocalDate> datesUntil(LocalDate endExclusive) { + long end = endExclusive.toEpochDay(); + long start = toEpochDay(); + if (end < start) { + throw new IllegalArgumentException(endExclusive + " < " + this); + } + return LongStream.range(start, end).mapToObj(LocalDate::ofEpochDay); + } + + /** + * Returns a sequential ordered stream of dates by given incremental step. The returned stream + * starts from this date (inclusive) and goes to {@code endExclusive} (exclusive). + * <p> + * The n-th date which appears in the stream is equal to {@code this.plus(step.multipliedBy(n))} + * (but the result of step multiplication never overflows). For example, if this date is + * {@code 2015-01-31}, the end date is {@code 2015-05-01} and the step is 1 month, then the + * stream contains {@code 2015-01-31}, {@code 2015-02-28}, {@code 2015-03-31}, and + * {@code 2015-04-30}. + * + * @param endExclusive the end date, exclusive, not null + * @param step the non-zero, non-negative {@code Period} which represents the step. + * @return a sequential {@code Stream} for the range of {@code LocalDate} values + * @throws IllegalArgumentException if step is zero, or {@code step.getDays()} and + * {@code step.toTotalMonths()} have opposite sign, or end date is before this date + * and step is positive, or end date is after this date and step is negative + * @since 9 + */ + public Stream<LocalDate> datesUntil(LocalDate endExclusive, Period step) { + if (step.isZero()) { + throw new IllegalArgumentException("step is zero"); + } + long end = endExclusive.toEpochDay(); + long start = toEpochDay(); + long until = end - start; + long months = step.toTotalMonths(); + long days = step.getDays(); + if ((months < 0 && days > 0) || (months > 0 && days < 0)) { + throw new IllegalArgumentException("period months and days are of opposite sign"); + } + if (until == 0) { + return Stream.empty(); + } + int sign = months > 0 || days > 0 ? 1 : -1; + if (sign < 0 ^ until < 0) { + throw new IllegalArgumentException(endExclusive + (sign < 0 ? " > " : " < ") + this); + } + if (months == 0) { + long steps = (until - sign) / days; // non-negative + return LongStream.rangeClosed(0, steps).mapToObj( + n -> LocalDate.ofEpochDay(start + n * days)); + } + // 48699/1600 = 365.2425/12, no overflow, non-negative result + long steps = until * 1600 / (months * 48699 + days * 1600) + 1; + long addMonths = months * steps; + long addDays = days * steps; + long maxAddMonths = months > 0 ? MAX.getProlepticMonth() - getProlepticMonth() + : getProlepticMonth() - MIN.getProlepticMonth(); + // adjust steps estimation + if (addMonths * sign > maxAddMonths + || (plusMonths(addMonths).toEpochDay() + addDays) * sign >= end * sign) { + steps--; + addMonths -= months; + addDays -= days; + if (addMonths * sign > maxAddMonths + || (plusMonths(addMonths).toEpochDay() + addDays) * sign >= end * sign) { + steps--; + } + } + return LongStream.rangeClosed(0, steps).mapToObj( + n -> this.plusMonths(months * n).plusDays(days * n)); + } + + /** * Formats this date using the specified formatter. * <p> * This date will be passed to the formatter to produce a string. * * @param formatter the formatter to use, not null
< prev index next >