Java 8 temporal queries

If you work in any business, specifically a financial institution, it is quite common while working with a date where you need to ask questions like what is the current quarter, is the market open today or are we in daylight savings. Luckily in Java 8 the date time api was completely overhauled to provide clarity in method constructs, cleaner readable code, immutable and extensible. As part of this overhaul, the TemporalQuery interface was introduced which can be used to retrieve information from a temporal-based object. In other words, it allows you to ask questions about a date. Let's look at a couple of examples.

Custom TemporalQuery Queries

Lets say we are a property and casualty company in Florida that writes a fair amount of homeowners insurance. In our rating logic, before we can send a quote, there is series of rules that only allow a certain number of quotes to processed if we are within the hurricane season. What we need to do is query todays date and determine if it falls within hurricane season which is between June 1st through November 30th. We could create a static utility class, maybe DateUtil.isInHuricaneSeason, but with java 8 we can create a hurricane season temporal query.

If we create a HurricaneSeasonQuery class, it might look something like the code below. Notice that it implements TemporalQuery and since all we need to know is if it falls within the season a return type of Boolean is specified. The queryFrom method compares the passed-in date against with a MonthDay object, a class that represents the combination of a month and day. It then returns true if it falls between June 1st and November 30th.

public class HurricaneSeasonQuery implements TemporalQuery<Boolean> {

    /*
     * (non-Javadoc)
     *
     * @see java.time.temporal.TemporalQuery#queryFrom(java.time.temporal.
     * TemporalAccessor)
     */
    @Override
    public Boolean queryFrom(TemporalAccessor temporal) {

        LocalDate date = LocalDate.from(temporal);

        MonthDay juneFirst = MonthDay.of(Month.JUNE.getValue(), 1);
        MonthDay novemberThirty = MonthDay.of(Month.NOVEMBER.getValue(), 30);

        if (date.isAfter(juneFirst.atYear(date.getYear()))
                && date.isBefore(novemberThirty.atYear(date.getYear()))) {
            return true;
        } else {
            return false;
        }
    }
}

Using a unit test to demonstrate a caller, we will create a LocalDate and call the query method passing in the TemporalQuery which returns a Boolean object.

@Test
public void is_during_hurricane_season () {

    LocalDate date = LocalDate.of(2014, Month.JUNE, 30);

    Boolean isHurricaneSeason = date.query(new HurricaneSeasonQuery());

    assertTrue(isHurricaneSeason);
}

Predefined queries

The TemporalQueries class provides several predefined queries which can be used with a static import. Lets say we want to find the time zone of a given date. In the code below, we will query the date with the predefined TemporalQueries.zone method which returns the ZoneId.

@Test
public void temporal_zone_id() {

    TemporalQuery<ZoneId> query = TemporalQueries.zone();

    LocalDateTime date = LocalDateTime.of(2014, Month.DECEMBER, 02, 0, 0);
    ZonedDateTime zonedDate = ZonedDateTime.of(date,
            ZoneId.of("Pacific/Chatham"));

    ZoneId zoneId = zonedDate.query(query);

    System.out.println(zoneId); // "Pacific/Chatham"
}

Hopefully using TemporalQueries will allow you to refactor or eliminate the static classes to produce cleaner code for asking questions about dates.