ZetCode

Java TemporalAdjuster Interface

Last modified: April 16, 2025

The java.time.temporal.TemporalAdjuster interface provides flexible date adjustments. It allows complex date manipulations beyond simple plus/minus operations. Implementations can find the next or previous day of week.

TemporalAdjuster is a functional interface with a single method. It works with all temporal types in the java.time package. Common adjustments are provided by the TemporalAdjusters utility class.

TemporalAdjuster Interface Overview

The interface defines one method adjustInto that takes a temporal object and returns an adjusted version. The TemporalAdjusters class provides many predefined adjusters. Custom adjusters can implement complex logic.

public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}

public final class TemporalAdjusters {
    public static TemporalAdjuster firstDayOfMonth();
    public static TemporalAdjuster lastDayOfMonth();
    public static TemporalAdjuster firstDayOfNextMonth();
    public static TemporalAdjuster firstDayOfYear();
    public static TemporalAdjuster lastDayOfYear();
    public static TemporalAdjuster firstDayOfNextYear();
    public static TemporalAdjuster next(DayOfWeek dayOfWeek);
    public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek);
    public static TemporalAdjuster previous(DayOfWeek dayOfWeek);
    public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek);
}

The code shows the interface and key methods from TemporalAdjusters. These provide common date adjustments like finding month boundaries or specific weekdays. The adjusters are thread-safe and immutable.

Using Predefined Adjusters

The TemporalAdjusters class provides many useful adjusters. These handle common cases like finding month boundaries or specific weekdays. They can be used with any temporal type that supports the adjustment.

Main.java
package com.zetcode;

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

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15);
        
        // First day of month
        LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("First day of month: " + firstDay);
        
        // Last day of month
        LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month: " + lastDay);
        
        // Next Tuesday
        LocalDate nextTuesday = date.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
        System.out.println("Next Tuesday: " + nextTuesday);
        
        // First day of next year
        LocalDate firstNextYear = date.with(TemporalAdjusters.firstDayOfNextYear());
        System.out.println("First day of next year: " + firstNextYear);
    }
}

This example demonstrates several predefined adjusters. Each adjustment creates a new date object without modifying the original. The adjusters handle edge cases like month length variations automatically.

Finding Weekdays

TemporalAdjusters provides methods to find specific weekdays relative to a date. These include next, previous, and same-day variants. They are useful for scheduling recurring events.

Main.java
package com.zetcode;

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

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15); // Tuesday
        
        // Next Friday
        LocalDate nextFriday = date.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("Next Friday: " + nextFriday);
        
        // Previous Monday
        LocalDate prevMonday = date.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
        System.out.println("Previous Monday: " + prevMonday);
        
        // Next or same Wednesday
        LocalDate nextOrSameWed = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY));
        System.out.println("Next or same Wednesday: " + nextOrSameWed);
        
        // Previous or same Friday
        LocalDate prevOrSameFri = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
        System.out.println("Previous or same Friday: " + prevOrSameFri);
    }
}

This example shows weekday-related adjusters. The "next" and "previous" methods exclude the current day if it matches. The "OrSame" variants include the current day when it matches the target weekday.

Custom Temporal Adjuster

Custom adjusters can implement complex date logic. They are created by implementing the interface or using lambda expressions. This provides flexibility for business-specific date rules.

Main.java
package com.zetcode;

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

public class Main {

    static class NextPaydayAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            int day = date.getDayOfMonth();
            
            if (day < 15) {
                return date.withDayOfMonth(15);
            } else {
                return date.withDayOfMonth(date.lengthOfMonth());
            }
        }
    }

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 10);
        TemporalAdjuster paydayAdjuster = new NextPaydayAdjuster();
        
        // Using class implementation
        LocalDate nextPayday = date.with(paydayAdjuster);
        System.out.println("Next payday: " + nextPayday);
        
        // Using lambda expression
        TemporalAdjuster taxDayAdjuster = t -> {
            LocalDate d = LocalDate.from(t);
            return d.withMonth(4).withDayOfMonth(15);
        };
        
        LocalDate taxDay = date.with(taxDayAdjuster);
        System.out.println("Tax day: " + taxDay);
    }
}

This example shows two ways to create custom adjusters. The first uses a class implementing the interface. The second uses a lambda expression. Both approaches can implement any date calculation logic needed.

Combining Adjusters

Adjusters can be combined to perform multiple operations in sequence. Each adjustment is applied to the result of the previous one. This allows building complex date calculations from simple steps.

Main.java
package com.zetcode;

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

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15);
        
        // First go to first of month, then find next Friday
        LocalDate result = date.with(TemporalAdjusters.firstDayOfMonth())
                              .with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("First Friday of month: " + result);
        
        // First find next Monday, then go to end of that month
        LocalDate result2 = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
                               .with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month containing next Monday: " + result2);
        
        // Custom combined adjuster
        TemporalAdjuster combined = t -> {
            Temporal temp = TemporalAdjusters.firstDayOfMonth().adjustInto(t);
            return TemporalAdjusters.next(DayOfWeek.WEDNESDAY).adjustInto(temp);
        };
        
        LocalDate result3 = date.with(combined);
        System.out.println("First Wednesday of month: " + result3);
    }
}

This example demonstrates combining adjusters in different ways. The first two examples chain adjustments directly. The third creates a combined adjuster as a lambda. All approaches achieve the same goal of sequential adjustments.

Adjusting Time Objects

While commonly used with dates, TemporalAdjusters work with time objects too. They can adjust LocalDateTime, ZonedDateTime, and other temporal types. The adjustment logic must be compatible with the temporal type.

Main.java
package com.zetcode;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

public class Main {

    public static void main(String[] args) {
        
        LocalDateTime dateTime = LocalDateTime.of(2025, 4, 15, 10, 30);
        
        // Adjust LocalDateTime
        LocalDateTime nextMonday = dateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        System.out.println("Next Monday at same time: " + nextMonday);
        
        // Adjust ZonedDateTime
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("America/New_York"));
        ZonedDateTime lastDay = zoned.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month in NY: " + lastDay);
        
        // Custom time adjuster
        TemporalAdjuster toNoon = t -> {
            if (t.isSupported(ChronoField.HOUR_OF_DAY)) {
                return t.with(ChronoField.HOUR_OF_DAY, 12)
                       .with(ChronoField.MINUTE_OF_HOUR, 0)
                       .with(ChronoField.SECOND_OF_MINUTE, 0);
            }
            return t;
        };
        
        LocalDateTime noon = dateTime.with(toNoon);
        System.out.println("Adjusted to noon: " + noon);
    }
}

This example shows adjusters working with different temporal types. The custom time adjuster demonstrates handling time fields specifically. Note the check for field support before attempting adjustment.

Business Day Adjuster

A common use case is adjusting dates to business days. This requires skipping weekends and potentially holidays. The example shows a basic business day adjuster implementation.

Main.java
package com.zetcode;

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

public class Main {

    static class BusinessDaysAdjuster implements TemporalAdjuster {
        private final int days;
        
        public BusinessDaysAdjuster(int days) {
            this.days = days;
        }
        
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            int remaining = days;
            int step = Integer.signum(remaining);
            
            while (remaining != 0) {
                date = date.plus(step, ChronoUnit.DAYS);
                if (date.getDayOfWeek() != DayOfWeek.SATURDAY &&
                    date.getDayOfWeek() != DayOfWeek.SUNDAY) {
                    remaining -= step;
                }
            }
            return temporal.with(date);
        }
    }

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, 4, 15); // Tuesday
        TemporalAdjuster add5BusinessDays = new BusinessDaysAdjuster(5);
        TemporalAdjuster subtract3BusinessDays = new BusinessDaysAdjuster(-3);
        
        LocalDate in5Days = date.with(add5BusinessDays);
        System.out.println("5 business days later: " + in5Days);
        
        LocalDate before3Days = date.with(subtract3BusinessDays);
        System.out.println("3 business days before: " + before3Days);
    }
}

This example implements a business day adjuster that skips weekends. The adjuster works for both forward and backward adjustments. More complex versions could incorporate holiday calendars for complete business date calculations.

Source

Java TemporalAdjuster Documentation

In this article, we've covered the essential methods and features of the Java TemporalAdjuster interface. Understanding these concepts is crucial for complex date manipulations in modern Java applications.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.