o7planning

Java TemporalQuery Tutorial with Examples

  1. TemporalQuery
  2. Ex: AllLeapYearsInSameDecadeQuery
  3. Ex: AllMondaysInSameMonthQuery
  4. Ex: AllZoneIdsSameOffsetQuery
  5. TemporalQueries

1. TemporalQuery

The TemporalQuery interface is a tool for querying a TemporalAccessor object to extract information. Basically, TemporalQuery exists to externalize the querying information of the TemporalAccessor object instead of extracting the information directly.
@FunctionalInterface
public interface TemporalQuery<R> {
    R queryFrom(TemporalAccessor temporal);
}
Example: We write HourMinuteQuery class to extract hour and minute information as a string "HH:mm" from TemporalAccessor objects.
TemporalAccessor
Example
Using HourMinuteQuery
LocalDateTime
2020-11-25 13:30:45
13:30
ZonedDateTime
2020-11-25 15:30:45+06:00[Asia/Bishkek]
15:30
LocalTime
21:45:45
21:45
HourMinuteQuery.java
package org.o7planning.temporalquery.ex;

import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;

public class HourMinuteQuery implements TemporalQuery<String> {
    @Override
    public String queryFrom(TemporalAccessor temporal) {
        if (temporal.isSupported(ChronoField.HOUR_OF_DAY) //
                && temporal.isSupported(ChronoField.MINUTE_OF_HOUR)) {
            int hour = temporal.get(ChronoField.HOUR_OF_DAY);
            int minute = temporal.get(ChronoField.MINUTE_OF_HOUR);
            return hour + ":" + minute;
        }
        return null;
    }
}
Using HourMinuteQuery:
HourMinuteQuery_ex1.java
package org.o7planning.temporalquery.ex;

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class HourMinuteQuery_ex1  {
    public static void main(String[] args) {
        // TemporalAccessor object:
        ZonedDateTime parisNow = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
        
        System.out.printf("Paris now is: %s%n", parisNow);
        
        HourMinuteQuery query = new HourMinuteQuery();
        
        String info = query.queryFrom(parisNow);
        System.out.println("Info: " + info);
    }
}
Output:
Paris now is: 2021-07-05T19:17:54.978689+02:00[Europe/Paris]
Info: 19:17
There are two ways to query a TemporalAccessor object, in which the second approach is recommended.
// These two lines are equivalent, but the second approach is recommended
information = thisQuery.queryFrom(temporalAccessor);  // (1)
information = temporalAccessor.query(thisQuery);          // (2)

2. Ex: AllLeapYearsInSameDecadeQuery

Create a TemporalQuery to search all leap years in the same decade with a TemporalAccessor object.
AllLeapYearsInSameDecadeQuery.java
package org.o7planning.temporalquery.ex;

import java.time.DateTimeException;
import java.time.chrono.ChronoLocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.ArrayList;
import java.util.List;

// Return all leap years in the same Decade with a TemporalAccessor.
public class AllLeapYearsInSameDecadeQuery implements TemporalQuery<List<Integer>> {

    @Override
    public List<Integer> queryFrom(TemporalAccessor temporal) {
        if (!(temporal instanceof ChronoLocalDate)) {
            throw new DateTimeException("Only ChronoLocalDate is supported");
        }
        List<Integer> leapYears = new ArrayList<>();

        ChronoLocalDate localDate = (ChronoLocalDate) temporal;

        int year = localDate.get(ChronoField.YEAR);
        int firstYearInSameDecade = year - (year % 10);
        
        for (int i = 0; i < 10; i++) {
            int y = firstYearInSameDecade + i;
            ChronoLocalDate newLocalDate = localDate.with(ChronoField.YEAR, y);
            if (newLocalDate.isLeapYear()) {
                leapYears.add(newLocalDate.get(ChronoField.YEAR));
            }
        }
        return leapYears;
    }
}
AllLeapYearsInSameDecadeQuery_ex1.java
LocalDate today = LocalDate.now();
System.out.println("Today is: " + today);
System.out.println("Year: " + today.getYear());

System.out.printf("Leap years in the same decade with %d:%n%n", today.getYear());

AllLeapYearsInSameDecadeQuery query = new AllLeapYearsInSameDecadeQuery();

// Return leap years in the same decade with today.
List<Integer> leapYears = query.queryFrom(today);
for(Integer leapYear: leapYears)  {
    System.out.println("Leap year: " + leapYear);
}
Output:
Today is: 2021-07-05
Year: 2021
Leap years in the same decade with 2021:

Leap year: 2020
Leap year: 2024
Leap year: 2028

3. Ex: AllMondaysInSameMonthQuery

Create a TemporalQuery to search all Mondays in the same month with a TemporalAccessor object.
AllMondaysInSameMonthQuery.java
package org.o7planning.temporalquery.ex;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.time.temporal.ValueRange;
import java.util.ArrayList;
import java.util.List;

public class AllMondaysInSameMonthQuery implements TemporalQuery<List<LocalDate>>{

    @Override
    public List<LocalDate> queryFrom(TemporalAccessor temporal) {
        if(!(temporal instanceof LocalDate))  {
            throw new DateTimeException("Only LocalDate is supported");
        }
        LocalDate localDate = (LocalDate) temporal;
        List<LocalDate> retList= new ArrayList<LocalDate>();
        
        ValueRange range = localDate.range(ChronoField.DAY_OF_MONTH);
        
        for(long dayOfMonth = range.getMinimum(); dayOfMonth<= range.getMaximum(); dayOfMonth++) {
            LocalDate date = localDate.withDayOfMonth((int) dayOfMonth);
            int dayOfWeek = date.get(ChronoField.DAY_OF_WEEK) ;
            if(dayOfWeek == 1) { // Monday
                retList.add(date);
            }
        }
        return retList;
    }
}
AllMondaysInSameMonthQuery_ex1.java
LocalDate localDate = LocalDate.now();

System.out.println("Today is: " + localDate );
System.out.println("All mondays in the same month:\n");

AllMondaysInSameMonthQuery query = new AllMondaysInSameMonthQuery();

// Same as: mondaysInSameMonth = localDate.query(query);
List<LocalDate> mondaysInSameMonth = query.queryFrom(localDate);

for(LocalDate monday: mondaysInSameMonth)  {
    System.out.println(monday);
}
Output:
Today is: 2021-07-05
All mondays in the same month:

2021-07-05
2021-07-12
2021-07-19
2021-07-26

4. Ex: AllZoneIdsSameOffsetQuery

Create a TemporalQuery to search all ZoneId(s) available in the system and with the same zone-offset as a TemporalAccessor object.
AllZoneIdsSameOffsetQuery.java
package org.o7planning.temporalquery.ex;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

// Find all available ZoneIds with the same offset as a TemporalAccessor.
public class AllZoneIdsSameOffsetQuery implements TemporalQuery<List<ZoneId>> {

    @Override
    public List<ZoneId> queryFrom(TemporalAccessor temporal) {
        ZoneOffset offset = ZoneOffset.from(temporal);

        List<ZoneId> returnList = new ArrayList<>();
        Set<String> zoneIdNames = ZoneId.getAvailableZoneIds();

        if (temporal.isSupported(ChronoField.OFFSET_SECONDS)) {

            for (String zoneIdName : zoneIdNames) {
                ZoneId zid = ZoneId.of(zoneIdName);
                ZoneOffset os = zid.getRules().getOffset(Instant.now());

                if (offset.compareTo(os) == 0) {
                    returnList.add(zid);
                }
            }
        }
        return returnList;
    }
}
AllZoneIdsSameOffsetQuery_ex1.java
ZonedDateTime parisNow = ZonedDateTime.now(ZoneId.of("Europe/Paris"));

System.out.printf("Paris now is: %s%n", parisNow);
System.out.printf("Paris offset: %s%n", parisNow.getOffset());

AllZoneIdsSameOffsetQuery query = new AllZoneIdsSameOffsetQuery();

// Same as: sameOffsetZoneIds = parisNow.query(query);
List<ZoneId> sameOffsetZoneIds = query.queryFrom(parisNow);

System.out.printf("All available ZoneId with the same offset as %s:%n%n",parisNow.getOffset());

for(ZoneId zoneId: sameOffsetZoneIds) {
    System.out.println(zoneId.getId() +" : " + zoneId.getRules().getOffset(Instant.now()));
}
Output:
Paris now is: 2021-07-05T17:44:23.310010+02:00[Europe/Paris]
Paris offset: +02:00
All available ZoneId with the same offset as +02:00:

Africa/Cairo : +02:00
Africa/Mbabane : +02:00
Europe/Brussels : +02:00  
Africa/Harare : +02:00
.....
Africa/Khartoum : +02:00
Africa/Johannesburg : +02:00
Europe/Belgrade : +02:00
Europe/Bratislava : +02:00
Arctic/Longyearbyen : +02:00
Europe/Monaco : +02:00

5. TemporalQueries

The TemporalQueries class provides static methods to obtain common and useful TemporalQuery objects. The information received may be:
  • Chronology
  • LocalDate
  • LocalTime
  • ZoneOffset
  • Precision
  • Zone
  • ZoneId