YearMonth.atEndOfMonth returns the actual final LocalDate of a month, including leap-year February.
Java YearMonth.atEndOfMonth: calculate a month boundary without guessed days
Compute the boundary from the calendar
A billing period ending in February does not have a constant day count. YearMonth carries the year and month without inventing a day; atEndOfMonth resolves the final date. The fixture prints February in a leap year and a non-leap year.
For a half-open timestamp range, use the first day of the next month as the exclusive end, then resolve that local boundary in the customer's zone. Exclusive date ranges avoid a fabricated last instant with guessed precision.
Keep policy separate from representation
A calendar month is not thirty elapsed days. If a contract renews on the last day, recompute from YearMonth each cycle. Repeated plusMonths can drift after a short month.
Working program
import java.time.YearMonth;
public class InvoiceMonthBoundary {
public static void main(String[] args) {
System.out.println(YearMonth.of(2024, 2).atEndOfMonth());
System.out.println(YearMonth.of(2025, 2).atEndOfMonth());
System.out.println(YearMonth.of(2024, 2).plusMonths(1).atDay(1));
}
}Output
2024-02-29
2025-02-28
2024-03-01Cost and ownership
YearMonth and LocalDate are immutable fixed-size values. The operation is constant application work and storage; the larger risk is using a guessed day count and billing a different range from the one stated to users.
Common Mistakes
- Do not hard-code February 28 as its last day.
- Do not model a calendar month as a fixed Duration.
- Do not append 23:59:59 as a substitute for an exclusive upper bound.
Read next
localdate exclusive range, period month end arithmetic, Java Duration versus Period across a daylight-saving change, Java date and time: local schedules versus instants.
