Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java LocalDate half-open ranges: define an exclusive end

Last updated: 7 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

A half-open date range includes its start and excludes its end, so adjacent ranges meet without overlap.

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

Java
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

Output
true
false

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

java
time
localdate-exclusive-range
Storage details