A half-open date range includes its start and excludes its end, so adjacent ranges meet without overlap.
Java LocalDate half-open ranges: define an exclusive end
Use one boundary convention
A monthly report can represent February as [February 1, March 1). The next report starts exactly where the first ends. No last-second adjustment is needed, and leap years are handled by calendar arithmetic rather than manual day counts.
The fixture checks the final day of February and the next day's exclusion. YearMonth can supply the start of the next month; a timestamp query must convert each local boundary using an explicit zone and its transition policy.
Keep date ranges and instant ranges distinct
A LocalDate range answers which civil dates belong to a report. An Instant range answers which events fall on a timeline. The conversion between them requires the user's zone and may span 23 or 25 hours around daylight-saving changes; calendar versus elapsed time explains that difference.
Working program
import java.time.LocalDate;
import java.time.YearMonth;
public class InvoiceDateWindow {
static boolean includes(LocalDate candidate, LocalDate start, LocalDate endExclusive) {
if (!start.isBefore(endExclusive)) throw new IllegalArgumentException("empty window");
return !candidate.isBefore(start) && candidate.isBefore(endExclusive);
}
public static void main(String[] args) {
YearMonth period = YearMonth.of(2024, 2);
LocalDate start = period.atDay(1);
LocalDate endExclusive = period.plusMonths(1).atDay(1);
System.out.println(includes(LocalDate.of(2024, 2, 29), start, endExclusive));
System.out.println(includes(LocalDate.of(2024, 3, 1), start, endExclusive));
}
}Output
true
falseCost and ownership
Each membership test is constant work and storage. A database query should use the same inclusive lower and exclusive upper bound. If its columns are timestamps, resolve the local dates to instants before querying rather than comparing formatted strings.
Common Mistakes
- Do not use an inclusive 23:59:59 upper timestamp; it misses finer precision.
- Do not combine local dates with UTC timestamps without a zone rule.
- Do not accept reversed or empty bounds silently.
Read next
Java YearMonth.atEndOfMonth: calculate a month boundary without guessed days, Java Duration versus Period across a daylight-saving change, Java date and time: local schedules versus instants, Java daylight-saving transitions: reject gaps and choose overlaps.
