Index

Temporal Adjusters

Java Date and Time

7.1 Using Built-in Temporal Adjusters

Java’s Date-Time API includes a powerful utility class called TemporalAdjusters that simplifies complex date manipulations. Instead of manually calculating dates such as “the next Monday” or “the last day of the month,” you can use these pre-built adjusters to write clearer, more readable code.

What Are Temporal Adjusters?

A Temporal Adjuster is an interface that adjusts a temporal object, such as LocalDate or LocalDateTime, according to some rule. The TemporalAdjusters class provides many built-in implementations of this interface for common calendar calculations.

This approach replaces cumbersome manual calculations with expressive method calls, reducing errors and improving maintainability.

Common Built-in Temporal Adjusters and Examples

Finding the Next or Previous Day of the Week

To find the date of the next Monday after a given date:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class NextMondayExample {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

        System.out.println("Today: " + today);
        System.out.println("Next Monday: " + nextMonday);
    }
}

If today is Monday, next() will return the Monday of the following week.

Finding the Previous Day of the Week

Similarly, you can find the previous Friday:

LocalDate previousFriday = today.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));

Finding the Next or Same Day

If you need to get the next Monday but return today if it is already Monday, use:

LocalDate nextOrSameMonday = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));

First and Last Day of Month or Year

You can easily jump to boundary dates, such as the first day of the current month:

LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());

Or the last day of the year:

LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());

Why Use TemporalAdjusters?

Summary

The TemporalAdjusters class offers an elegant, expressive way to perform common date calculations. Whether you need to find the next occurrence of a weekday, the first day of a month, or the last day of a year, these built-in adjusters simplify your code and reduce bugs. Leveraging them is a best practice for any Java developer working with date and time.

Index

7.2 Creating Custom Temporal Adjusters

While Java’s built-in TemporalAdjusters cover many common date manipulation scenarios, there are times when you need custom logic tailored to your application’s requirements. For example, adjusting to the “next working day” (skipping weekends) or moving to a company-specific event date.

This section shows how to create your own custom TemporalAdjuster by implementing the adjustInto() method.

Implementing a Custom Temporal Adjuster

A custom TemporalAdjuster is simply a class or lambda that implements the TemporalAdjuster interface, which defines a single method:

Temporal adjustInto(Temporal temporal);

This method receives a temporal object (such as a LocalDate) and returns a new, adjusted temporal instance. Because Java date-time types are immutable, you always return a new adjusted object instead of modifying the input.

Example 1: Next Working Day Adjuster (Skipping Weekends)

Suppose you want to move a date forward to the next weekday, skipping Saturdays and Sundays:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class NextWorkingDayAdjuster implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate date = LocalDate.from(temporal);
        DayOfWeek day = date.getDayOfWeek();

        switch (day) {
            case FRIDAY:
                return date.plusDays(3); // skip Saturday & Sunday
            case SATURDAY:
                return date.plusDays(2); // skip Sunday
            default:
                return date.plusDays(1); // next day
        }
    }
}

Usage:

LocalDate today = LocalDate.of(2025, 5, 30); // Friday
LocalDate nextWorkingDay = today.with(new NextWorkingDayAdjuster());

System.out.println("Next working day after " + today + " is " + nextWorkingDay);
// Output: Next working day after 2025-05-30 is 2025-06-02

This custom adjuster moves the date forward, skipping weekends.

Click to view full runnable Code

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class NextWorkingDayExample {

    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2025, 5, 30); // Friday
        LocalDate nextWorkingDay = today.with(new NextWorkingDayAdjuster());

        System.out.println("Next working day after " + today + " is " + nextWorkingDay);
        // Output: Next working day after 2025-05-30 is 2025-06-02
    }

    // Custom TemporalAdjuster to skip weekends
    static class NextWorkingDayAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            DayOfWeek day = date.getDayOfWeek();

            switch (day) {
                case FRIDAY:
                    return date.plusDays(3); // skip Saturday & Sunday
                case SATURDAY:
                    return date.plusDays(2); // skip Sunday
                default:
                    return date.plusDays(1); // next day
            }
        }
    }
}

Example 2: Company-Specific Event Adjuster

Imagine a company has quarterly review meetings on the 15th of March, June, September, and December. You want to adjust any date to the next such event date.

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class NextQuarterlyReviewAdjuster implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate date = LocalDate.from(temporal);
        int year = date.getYear();
        LocalDate[] eventDates = {
            LocalDate.of(year, Month.MARCH, 15),
            LocalDate.of(year, Month.JUNE, 15),
            LocalDate.of(year, Month.SEPTEMBER, 15),
            LocalDate.of(year, Month.DECEMBER, 15)
        };

        for (LocalDate eventDate : eventDates) {
            if (!date.isAfter(eventDate)) {
                return eventDate;
            }
        }
        // If date is after December 15, move to next year's March 15
        return LocalDate.of(year + 1, Month.MARCH, 15);
    }
}

Usage:

LocalDate today = LocalDate.of(2025, 6, 20);
LocalDate nextReview = today.with(new NextQuarterlyReviewAdjuster());

System.out.println("Next quarterly review after " + today + " is " + nextReview);
// Output: Next quarterly review after 2025-06-20 is 2025-09-15
Click to view full runnable Code

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class NextQuarterlyReviewExample {

    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2025, 6, 20);
        LocalDate nextReview = today.with(new NextQuarterlyReviewAdjuster());

        System.out.println("Next quarterly review after " + today + " is " + nextReview);
        // Output: Next quarterly review after 2025-06-20 is 2025-09-15
    }

    // Custom TemporalAdjuster for quarterly review dates
    static class NextQuarterlyReviewAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            int year = date.getYear();
            LocalDate[] eventDates = {
                LocalDate.of(year, Month.MARCH, 15),
                LocalDate.of(year, Month.JUNE, 15),
                LocalDate.of(year, Month.SEPTEMBER, 15),
                LocalDate.of(year, Month.DECEMBER, 15)
            };

            for (LocalDate eventDate : eventDates) {
                if (!date.isAfter(eventDate)) {
                    return eventDate;
                }
            }

            // If date is after December 15, move to next year's March 15
            return LocalDate.of(year + 1, Month.MARCH, 15);
        }
    }
}

Reflections on Immutability and Reusability

Summary

Custom TemporalAdjusters empower you to encapsulate complex, domain-specific date logic cleanly. By implementing the adjustInto() method, you can create reusable, immutable adjusters—whether for skipping weekends, scheduling company events, or any other specialized temporal rule—making your date handling code easier to understand and maintain.

Index

7.3 Practical Use Cases (e.g., Next Monday, Last Day of Month)

In real-world applications, date calculations often involve business rules like scheduling paydays, identifying specific weekdays, or skipping weekends and holidays. Java’s TemporalAdjusters—both built-in and custom—provide elegant solutions that keep your code clean, readable, and maintainable.

Below are practical examples illustrating how to apply these adjusters for common scenarios.

Example 1: Finding the Next Payday (e.g., 25th of the Month or Last Business Day)

Let’s say payday is on the 25th of every month. However, if the 25th falls on a weekend, the payday moves to the previous Friday (the last business day before the 25th).

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class PaydayExample {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        LocalDate payday = today.withDayOfMonth(25);

        // If payday is Saturday, adjust to Friday (previous day)
        if (payday.getDayOfWeek() == DayOfWeek.SATURDAY) {
            payday = payday.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        }
        // If payday is Sunday, adjust to Friday (two days before)
        else if (payday.getDayOfWeek() == DayOfWeek.SUNDAY) {
            payday = payday.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        }

        System.out.println("Next payday: " + payday);
    }
}

Reflection: Using built-in adjusters like previous() lets you quickly handle weekend adjustments, avoiding manual calculations.

Example 2: Finding the First Friday of the Month

To find the first Friday of the current month, use:

LocalDate firstFriday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY));
System.out.println("First Friday of this month: " + firstFriday);

This is useful for scheduling monthly events like team meetings or reporting deadlines.

Example 3: Adjusting to the Last Business Day of the Month (Custom Adjuster)

If the last day of the month falls on a weekend, the last business day is the previous Friday. Here’s a custom adjuster for that:

import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class LastBusinessDayAdjuster implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate date = LocalDate.from(temporal).with(TemporalAdjusters.lastDayOfMonth());
        DayOfWeek day = date.getDayOfWeek();

        if (day == DayOfWeek.SATURDAY) {
            return date.minusDays(1);
        } else if (day == DayOfWeek.SUNDAY) {
            return date.minusDays(2);
        }
        return date;
    }
}

// Usage:
LocalDate lastBusinessDay = LocalDate.now().with(new LastBusinessDayAdjuster());
System.out.println("Last business day of the month: " + lastBusinessDay);

Example 4: Using the Next Working Day Adjuster (Custom Example from Section 2)

Recall the NextWorkingDayAdjuster that skips weekends:

LocalDate today = LocalDate.of(2025, 5, 30); // Friday
LocalDate nextWorkingDay = today.with(new NextWorkingDayAdjuster());
System.out.println("Next working day after " + today + " is " + nextWorkingDay);

This adjuster simplifies scheduling tasks that should run only on business days.

Click to view full runnable Code

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

public class BusinessDayAdjusterExample {

    public static void main(String[] args) {
        // Example 1: Last business day of the current month
        LocalDate today = LocalDate.now();
        LocalDate lastBusinessDay = today.with(new LastBusinessDayAdjuster());
        System.out.println("Last business day of the month: " + lastBusinessDay);

        // Example 2: Next working day from a fixed date
        LocalDate specificDay = LocalDate.of(2025, 5, 30); // Friday
        LocalDate nextWorkingDay = specificDay.with(new NextWorkingDayAdjuster());
        System.out.println("Next working day after " + specificDay + " is " + nextWorkingDay);
    }

    // Adjuster for the last business day (Mon–Fri) of the month
    static class LastBusinessDayAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal).with(TemporalAdjusters.lastDayOfMonth());
            DayOfWeek day = date.getDayOfWeek();

            if (day == DayOfWeek.SATURDAY) {
                return date.minusDays(1); // move to Friday
            } else if (day == DayOfWeek.SUNDAY) {
                return date.minusDays(2); // move to Friday
            }
            return date; // already a business day
        }
    }

    // Adjuster to get the next working day (skip weekends)
    static class NextWorkingDayAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            DayOfWeek day = date.getDayOfWeek();

            switch (day) {
                case FRIDAY:
                    return date.plusDays(3); // skip Saturday & Sunday
                case SATURDAY:
                    return date.plusDays(2); // skip Sunday
                default:
                    return date.plusDays(1); // next day
            }
        }
    }
}

Why Use TemporalAdjusters?

Summary

Using both built-in and custom TemporalAdjusters helps developers write concise, expressive, and reliable date-time code. From scheduling paydays to identifying special weekdays or business boundaries, adjusters encapsulate complex date logic, enabling you to focus on your application’s core logic rather than error-prone manual calculations.

Index