Working with TemporalAdjusters in Java: Next Monday, Last Day of Month, and More

Illustration for Working with TemporalAdjusters in Java: Next Monday, Last Day of Month, and More
By Last updated:

Scheduling often requires moving beyond today’s date—finding the next Monday for a meeting, the last day of the month for payroll, or the first day of next year. A common pain point is that developers attempt manual arithmetic (date.plusDays(7)) or write verbose logic to compute such rules, which is error-prone around leap years and month-end boundaries.

The TemporalAdjusters class in the java.time API provides ready-to-use adjusters and the ability to create custom ones, enabling clean and reliable date adjustments in banking, payroll, event scheduling, and compliance reporting.


1. What is a TemporalAdjuster?

  • An interface (TemporalAdjuster) that adjusts a Temporal object (like LocalDate).
  • Implemented by the static utility class TemporalAdjusters.
  • Provides common reusable adjustments.

2. Built-In TemporalAdjusters

Next or Previous Day of Week

LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
LocalDate previousFriday = today.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));

System.out.println("Next Monday: " + nextMonday);
System.out.println("Previous Friday: " + previousFriday);

✅ Simplifies recurring event scheduling.


First and Last Day of Month

LocalDate date = LocalDate.now();
LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());

System.out.println("First day: " + firstDay);
System.out.println("Last day: " + lastDay);

✅ Useful for payroll, billing cycles, and reports.


First and Last Day of Year

LocalDate date = LocalDate.now();
LocalDate firstDayOfYear = date.with(TemporalAdjusters.firstDayOfYear());
LocalDate lastDayOfYear = date.with(TemporalAdjusters.lastDayOfYear());

✅ Handy for financial year-end processing.


Day of Week in Month

LocalDate secondTuesday = LocalDate.now()
    .with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));

System.out.println("Second Tuesday: " + secondTuesday);

✅ For rules like “the second Tuesday of every month.”


3. Custom TemporalAdjusters

Create your own adjuster for domain-specific rules.

TemporalAdjuster nextQuarterStart = temporal -> {
    LocalDate date = LocalDate.from(temporal);
    int currentQuarter = (date.getMonthValue() - 1) / 3 + 1;
    int nextQuarter = currentQuarter == 4 ? 1 : currentQuarter + 1;
    int year = currentQuarter == 4 ? date.getYear() + 1 : date.getYear();
    Month firstMonthOfNextQuarter = Month.of((nextQuarter - 1) * 3 + 1);
    return LocalDate.of(year, firstMonthOfNextQuarter, 1);
};

LocalDate adjusted = LocalDate.now().with(nextQuarterStart);
System.out.println("Next quarter starts: " + adjusted);

✅ Enables flexible business-specific adjustments.


4. Pitfalls and Anti-Patterns

  • ❌ Using plusDays(7) instead of next(DayOfWeek) → fails if meeting day shifts.
  • ❌ Writing verbose month-end logic → built-ins already exist.
  • ❌ Forgetting immutability—with() returns a new object, original unchanged.
  • ❌ Overcomplicating custom adjusters instead of composing built-ins.

5. Best Practices

  • ✅ Prefer built-in adjusters (firstDayOfMonth, next(DayOfWeek)).
  • ✅ Use custom adjusters only for domain-specific rules.
  • ✅ Test custom adjusters against leap years and DST boundaries.
  • ✅ Document business rules clearly in custom logic.

📌 What's New in Java Versions?

  • Java 8: Introduced TemporalAdjusters with core adjusters.
  • Java 11: Minor performance optimizations.
  • Java 17: API stable, no changes.
  • Java 21: Continued stability, no new features.

✅ TemporalAdjusters stable since Java 8.


Real-World Analogy

Think of TemporalAdjusters as calendar shortcuts. Instead of flipping through pages to find “the last day of the month,” you press a shortcut button. Similarly, adjusters provide concise and reusable rules for date adjustments.


Conclusion + Key Takeaways

  • ❌ Avoid manual arithmetic for recurring date rules.
  • ✅ Use TemporalAdjusters for built-in adjustments (next Monday, last day of month).
  • ✅ Create custom adjusters for business-specific logic.
  • ✅ Always remember immutability—store results of adjustments.

With TemporalAdjusters, Java developers can write cleaner, safer, and domain-driven date calculations.


FAQ: Expert-Level Q&A

1. When should I use TemporalAdjusters instead of plusDays()?
When working with recurring rules like next Monday or last day of month.

2. Can TemporalAdjusters handle leap years automatically?
Yes, built-in adjusters respect leap year rules.

3. Are TemporalAdjusters thread-safe?
Yes, since they return immutable LocalDate or ZonedDateTime objects.

4. Can I chain multiple adjusters?
Yes: date.with(nextMonday).with(lastDayOfMonth()).

5. What if I need a biweekly adjustment?
Write a custom adjuster or combine plusWeeks(2).

6. Are custom TemporalAdjusters reusable?
Yes, define them once and apply across multiple calculations.

7. Can TemporalAdjusters adjust time as well as date?
Primarily for dates, but can apply to any Temporal (e.g., ZonedDateTime).

8. What happens if the adjustment is invalid (e.g., 5th Monday in February)?
It throws an exception if invalid—validate business rules.

9. How does performance compare to manual logic?
More efficient and reliable, as built-ins are optimized.

10. Should I use TemporalAdjusters in all scheduling code?
Yes, for recurring or pattern-based rules—it improves clarity and correctness.