Java TemporalAdjusters
Compute dates relative to others in Java with TemporalAdjusters — firstDayOfMonth, next, previous.
Java TemporalAdjusters
plusDays(7) adds seven days. withDayOfMonth(15) jumps to the 15th. Those two cover the simple cases. A TemporalAdjuster is a function-shaped object you pass to Temporal.with(adjuster) to handle the cases where the new date depends on the current one in a more complicated way: "the first Monday of next month," "the last day of this quarter," "next year's American Thanksgiving."
The interface is one method:
@FunctionalInterface
interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}You don't usually implement it. The class java.time.temporal.TemporalAdjusters (note the s — Adjusters, plural) is a kit of about a dozen built-in adjusters that cover the common cases, and LocalDate.with(adjuster) is how you apply them.
The built-in catalogue
Import statically; the code reads better that way:
import static java.time.temporal.TemporalAdjusters.*;Then:
| Adjuster | Effect |
|---|---|
firstDayOfMonth() | day → first day of the same month |
lastDayOfMonth() | day → last day of the same month |
firstDayOfNextMonth() | day → first day of the next month |
firstDayOfYear() | day → January 1 of the same year |
lastDayOfYear() | day → December 31 of the same year |
firstDayOfNextYear() | day → January 1 of the next year |
next(DayOfWeek dow) | the next dow strictly after the date |
nextOrSame(DayOfWeek dow) | today if it's dow, otherwise next dow |
previous(DayOfWeek dow) | the most recent dow strictly before the date |
previousOrSame(DayOfWeek dow) | today if it's dow, otherwise previous dow |
dayOfWeekInMonth(int ord, DayOfWeek dow) | nth weekday of the month: dayOfWeekInMonth(3, MONDAY) → 3rd Monday |
firstInMonth(DayOfWeek dow) | first dow of the month (equivalent to dayOfWeekInMonth(1, dow)) |
lastInMonth(DayOfWeek dow) | last dow of the month |
The two halves answer "what's the first/last X" (top half) and "what's the next/previous X" (bottom half). Chain them when the question is more complex:
LocalDate firstMondayNextMonth = today.with(firstDayOfNextMonth()).with(nextOrSame(DayOfWeek.MONDAY));That's "the first Monday on or after the first of next month." Read left to right; each with is a step.
Apply with with(adjuster)
LocalDate today = LocalDate.now();
LocalDate eom = today.with(lastDayOfMonth());
LocalDate nextMonday = today.with(next(DayOfWeek.MONDAY));
LocalDate thirdFriday = today.with(dayOfWeekInMonth(3, DayOfWeek.FRIDAY));with exists on every Temporal: LocalDate, LocalDateTime, ZonedDateTime, even OffsetDateTime. The adjuster only touches the date components — applying lastDayOfMonth() to a LocalDateTime leaves the time alone.
Adjusters are total: they always return a result. There's no "what if today isn't in the right month" exception path — lastDayOfMonth() from January 31 is still January 31 (the last day is itself).
Lambda adjusters
Because TemporalAdjuster is a @FunctionalInterface, you can write your own with a lambda:
TemporalAdjuster nextWorkingDay = t -> {
LocalDate d = LocalDate.from(t).plusDays(1);
while (d.getDayOfWeek() == DayOfWeek.SATURDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY) {
d = d.plusDays(1);
}
return d;
};
LocalDate friday = LocalDate.of(2025, 11, 7);
LocalDate monday = friday.with(nextWorkingDay); // 2025-11-10The pattern: convert the incoming Temporal to the date type you want (LocalDate.from(t)), compute the new date, return it. The return type is Temporal (the interface), but the JDK accepts the concrete return.
For one-off use this is overkill — just inline the logic next to the call site. For a calculation you'll use in multiple places (next working day, last day of quarter, observed holiday), bundling it as an adjuster keeps the call sites readable.
ofDateAdjuster for the simple lambda case
If your adjuster works on LocalDate only (the common case), TemporalAdjusters.ofDateAdjuster(UnaryOperator<LocalDate>) is the cleaner factory:
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(d -> {
LocalDate result = d.plusDays(1);
while (result.getDayOfWeek().getValue() > 5) {
result = result.plusDays(1);
}
return result;
});The lambda takes and returns a LocalDate. The factory wraps it as a TemporalAdjuster. This is what you reach for 90% of the time when writing custom adjusters.
A worked example: business dates, holidays, end-of-period
The program below uses adjusters for the bookkeeping calendar: end-of-month for billing, last business day of the month for cash close, the first Monday of next month for a regular planning meeting, a holiday-aware "next working day" adjuster, and end-of-quarter computation.
What to take from the run:
- The built-in adjusters covered the boundary cases (
firstDayOfMonth,lastDayOfMonth,firstDayOfYear, etc.) without any arithmetic on your side. For "this date snapped to the start/end of its month or year," they're the right tool — clearer thanwithDayOfMonth(1)-style by-hand computation, and immune to month-length surprises. next(MONDAY)andprevious(FRIDAY)returned strictly different dates;nextOrSame(TUESDAY)on a Tuesday returned today. Memorise the strict-vs-or-same distinction; it's the source of most "off by one week" bugs when the date you start from happens to fall on the target weekday.- The chained
firstDayOfNextMonth().nextOrSame(MONDAY)expressed "the first Monday on or after the first of next month" in two reads. The chain is one line; the by-hand equivalent is six. Chaining adjusters is the idiomatic way to compose them. NEXT_BUSINESS_DAYskipped the weekend and a holiday in one shot. TheHOLIDAYSset was a synthetic two-element example; a real implementation would load from a service. The adjuster shape is the same — wrap the loop inTemporalAdjusters.ofDateAdjuster(...)and you can drop it intotoday.with(NEXT_BUSINESS_DAY)calls everywhere.END_OF_QUARTERwas a one-line custom adjuster that picked the third month of the date's quarter and appliedlastDayOfMonth(). The point: complex domain operations belong in namedTemporalAdjusterconstants, where the call site readssomeDate.with(END_OF_QUARTER)instead of inlining a six-line arithmetic block. Keep the calendar logic in one place.
What's next
TemporalAdjusters close out the modern java.time API. The last two chapters of this part cover the legacy types you'll meet in older code: Java Legacy Date Class for the original java.util.Date, and Java Calendar Class for java.util.Calendar. Both are still around for compatibility; both have a clean conversion path to java.time.
Practice
You need 'the first Monday of next month' starting from any given date. Which expression returns that value most idiomatically?