Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
Can be used to obtain the equivalent date range of most Temporal objects.
For example, `LocalDateRange.from(YearMonth.of(2024, 6))` will return the range 2024-06-01 to 2024-06-30 (inclusive).
</action>
<action dev="jodastephen" type="add" issue="371">
Add `LocalDateRange.withEndInclusive(TemporalAdjuster)`.
Allows a range to be adjusted based on the inclusive end date.
Comment thread
jodastephen marked this conversation as resolved.
</action>
</release>
<release version="1.10.0" date="2026-06-16" description="v1.10.0">
<action dev="jodastephen" type="add" issue="379">
Expand Down
33 changes: 29 additions & 4 deletions src/main/java/org/threeten/extra/LocalDateRange.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public static LocalDateRange of(LocalDate startInclusive, LocalDate endExclusive
* <p>
* The range includes the start date and the end date.
* The end date must be equal to or after the start date.
* Note that an empty range cannot be created with this method.
* <p>
* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used
* to indicate an unbounded far-past or far-future. In addition, an end date of
Expand Down Expand Up @@ -171,7 +172,7 @@ public static LocalDateRange ofClosed(LocalDate startInclusive, LocalDate endInc
* Obtains an instance of {@code LocalDateRange} from the start and a period.
* <p>
* The end date is calculated as the start plus the duration.
* The period must not be negative.
* The period must not be negative, but may be zero.
* <p>
* The constant {@code LocalDate.MIN} can be used to indicate an unbounded far-past.
* <p>
Expand All @@ -188,7 +189,7 @@ public static LocalDateRange of(LocalDate startInclusive, Period period) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(period, "period");
if (period.isNegative()) {
throw new DateTimeException("Period must not be zero or negative");
throw new DateTimeException("Period must not be negative");
}
return new LocalDateRange(startInclusive, startInclusive.plus(period));
}
Expand Down Expand Up @@ -469,7 +470,7 @@ public LocalDateRange withStart(TemporalAdjuster adjuster) {
}

/**
* Returns a copy of this range with the end date adjusted.
* Returns a copy of this range with the exclusive end date adjusted.
* <p>
* This returns a new instance with the exclusive end date altered.
* Since {@code LocalDate} implements {@code TemporalAdjuster} any
Expand All @@ -481,13 +482,37 @@ public LocalDateRange withStart(TemporalAdjuster adjuster) {
* </pre>
*
* @param adjuster the adjuster to use, not null
* @return a copy of this range with the end date adjusted
* @return a copy of this range with the exclusive end date adjusted
* @throws DateTimeException if the new end date is before the current start date
*/
public LocalDateRange withEnd(TemporalAdjuster adjuster) {
return LocalDateRange.of(start, end.with(adjuster));
}

/**
* Returns a copy of this range with the inclusive end date adjusted.
* <p>
* This returns a new instance with the inclusive end date altered.
* Since {@code LocalDate} implements {@code TemporalAdjuster} any
* local date can simply be passed in.
* <p>
* For example, to adjust the end to one week later:
* <pre>
* range = range.withEndInclusive(date -&gt; date.plus(1, ChronoUnit.WEEKS));
* </pre>
* <p>
* It is not possible to create an empty range with this method,
* as the inclusive end date must always be on or after the start date.
*
* @param adjuster the adjuster to use, not null
* @return a copy of this range with the inclusive end date adjusted
* @throws DateTimeException if the new inclusive end date is before the start date
* @since 1.11.0
*/
public LocalDateRange withEndInclusive(TemporalAdjuster adjuster) {
return LocalDateRange.ofClosed(start, getEndInclusive().with(adjuster));
}

//-----------------------------------------------------------------------
/**
* Checks if this range contains the specified date.
Expand Down
48 changes: 48 additions & 0 deletions src/test/java/org/threeten/extra/TestLocalDateRange.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ public void test_ofClosed() {
assertEquals("2012-07-28/2012-07-31", test.toString());
}

@Test
public void test_ofClosed_empty() {
assertThrows(DateTimeException.class, () -> LocalDateRange.ofClosed(DATE_2012_07_30, DATE_2012_07_29));
}

@Test
public void test_ofClosed_MIN() {
LocalDateRange test = LocalDateRange.ofClosed(LocalDate.MIN, DATE_2012_07_30);
Expand Down Expand Up @@ -701,6 +706,49 @@ public void test_withEnd_invalid() {
assertThrows(DateTimeException.class, () -> base.withEnd(DATE_2012_07_27));
}

//-----------------------------------------------------------------------
@Test
public void test_withEndInclusive() {
LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31);
LocalDateRange test = base.withEndInclusive(DATE_2012_07_31);
assertEquals(DATE_2012_07_28, test.getStart());
assertEquals(DATE_2012_07_31, test.getEndInclusive());
assertEquals(DATE_2012_08_01, test.getEnd());
}

@Test
public void test_withEndInclusive_adjuster() {
LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31);
LocalDateRange test = base.withEndInclusive(date -> {
assertEquals(DATE_2012_07_30, date);
return date.plus(1, ChronoUnit.WEEKS);
});
assertEquals(DATE_2012_07_28, test.getStart());
assertEquals(DATE_2012_07_30.plusWeeks(1), test.getEndInclusive());
assertEquals(DATE_2012_07_31.plusWeeks(1), test.getEnd());
}

@Test
public void test_withEndInclusive_max() {
LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31);
LocalDateRange test = base.withEndInclusive(LocalDate.MAX);
assertEquals(DATE_2012_07_28, test.getStart());
assertEquals(LocalDate.MAX, test.getEndInclusive());
assertEquals(LocalDate.MAX, test.getEnd());
}

@Test
public void test_withEndInclusive_empty() {
LocalDateRange base = LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_31);
assertThrows(DateTimeException.class, () -> base.withEndInclusive(DATE_2012_07_29));
}

@Test
public void test_withEndInclusive_invalid() {
LocalDateRange base = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_31);
assertThrows(DateTimeException.class, () -> base.withEndInclusive(DATE_2012_07_27));
}

//-----------------------------------------------------------------------
@Test
public void test_contains() {
Expand Down
Loading