TemporalAdjuster In Java

Introduced in Java 8, the TemporalAdjuster interface, which is a Functional Interface (a functional interface has only one abstract method), provides a strategy for adjusting a temporal object.

A Temporal

This is the base interface type for date, time, offset or a combination of these. A Temporal represents objects that are complete enough to be manipulated using plus and minus. Some Temporal implementations include:

  1. java.time.LocalDate: representing a date without a time-zone. e.g 2007-12-03
  2. java.time.LocalDateTime: representing a date-time without a time-zone. e.g 2007-12-03T10:15:30
  3. java.time.OffsetDateTime: representing a date-time with an offset from UTC/Greenwich. e.g 2007-12-03T10:15:30+01:00
  4. java.time.chrono.MinguoDate: representing a date in the Minguo Calendar System. This Calendar System is primary used in the Republic of China

Now, each of the above-listed Temporal implementations are Temporal since they implement the Temporal Interface.

The TemporalAdjuster interface

Fig 1 shows the only abstract method in the TemporalAdjuster interface: adjustInto. It takes as parameter a Temporal object, and returns a Temporal. Hence it is a functional interface (an interface having only one abstract method). From Java 8, Functional Interfaces can be annotated with @FunctionalInterface.

Fig1: TemporalAdjuster Interface showing its only method

TemporalAdjusters

Java 8 also introduced another class with common and useful standard TemporalAdjusters, provided as static methods. The TemporalAdjusters include:

  1. Finding the first or last day of the month
  2. Finding the first day of next month
  3. Finding the first or last day of the year
  4. Finding the first day of next year
  5. Finding the first or last day-of-week within a month. Such as first wednesday in June.
  6. Finding the next or previous day-of-week, such as "next Thursday".

Showing how to use TemporalAdjusters

  1. Finding the first or last day of month
package com.codelab.hobby;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;

public class AdjusterTest {

    public static void main(String[] args) {
        LocalDate today = LocalDate.parse("2022-04-04", DateTimeFormatter.ISO_LOCAL_DATE);
        LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last Day of Month: " + lastDayOfThisMonth);
    }
}


Result

  1. Finding the first day of next month
package com.codelab.hobby;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;

public class AdjusterTest {

    public static void main(String[] args) {
        LocalDate today = LocalDate.parse("2022-04-04", DateTimeFormatter.ISO_LOCAL_DATE);
        LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("First Day of next Month: " + lastDayOfThisMonth);
    }
}

Result

  1. More complex use-case
    Let's say a Company had several events, for most weekends in 2021, and each time people came for such a weekend event, they enrolled. Looking at the data, they needed to know what month had the highest enrolment. This can easily be done using TemporalAdjuster on the Temporal (LocalDate). See example code below
package com.codelab.hobby;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class AdjusterTest {

    static class Person {
        private final String    name;
        private final int       age;
        private final LocalDate enrolmentDate;

        public Person(String name, int age, LocalDate enrolmentDate) {
            this.name = name;
            this.age = age;
            this.enrolmentDate = enrolmentDate;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", enrolmentDate=" + enrolmentDate +
                    '}';
        }
    }

    private static final List dates = Arrays.asList("2021-01-01", "2021-01-02", "2021-01-03", "2021-01-05", "2021-01-17",
            "2021-02-10", "2021-02-11",
            "2021-03-13", "2021-03-14");


    public static void main(String[] args) {
        Map> datePersonHashMap;

        List persons = new ArrayList<>();

        for (int i = 0; i < dates.size(); i++) {
            persons.add(new Person("Person" +i, i, LocalDate.parse(dates.get(i), DateTimeFormatter.ISO_LOCAL_DATE)));
        }

        datePersonHashMap =  persons
                .stream()
                .collect(Collectors.groupingBy(it -> it.enrolmentDate.with(TemporalAdjusters.firstDayOfMonth())));
        for (Map.Entry> entry : datePersonHashMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue().size());
        }
    }
}

Result

Happy Learning !!!