ZetCode

Java TemporalField Interface

Last modified: April 16, 2025

The java.time.temporal.TemporalField interface represents a field of date-time, such as month-of-year or hour-of-day. It provides access to date-time fields in a generic way. TemporalField is a key interface in the Java Time API.

TemporalField defines methods to get and set field values on temporal objects. It works with both date and time fields. The interface is implemented by ChronoField enum and custom field implementations.

TemporalField Interface Overview

TemporalField provides methods to access field values and perform validations. Key operations include getting value ranges and checking support. The interface enables generic field access across different temporal types.

public interface TemporalField {
    TemporalUnit getBaseUnit();
    TemporalUnit getRangeUnit();
    ValueRange range();
    boolean isDateBased();
    boolean isTimeBased();
    boolean isSupportedBy(TemporalAccessor temporal);
    ValueRange rangeRefinedBy(TemporalAccessor temporal);
    long getFrom(TemporalAccessor temporal);
    <R extends Temporal> R adjustInto(R temporal, long newValue);
}

The code above shows key methods of TemporalField. These methods allow accessing and manipulating temporal fields generically. The interface supports both date-based and time-based fields.

Accessing Field Values

The getFrom method retrieves field values from temporal objects. This works with any temporal type that supports the field. The method returns the field value as a long.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoField;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, 4, 16);
        LocalTime time = LocalTime.of(14, 30);
        
        // Get day-of-month from date
        long dayOfMonth = ChronoField.DAY_OF_MONTH.getFrom(date);
        System.out.println("Day of month: " + dayOfMonth);
        
        // Get hour-of-day from time
        long hourOfDay = ChronoField.HOUR_OF_DAY.getFrom(time);
        System.out.println("Hour of day: " + hourOfDay);
        
        // Get month-of-year from date
        long monthOfYear = ChronoField.MONTH_OF_YEAR.getFrom(date);
        System.out.println("Month of year: " + monthOfYear);
    }
}

This example demonstrates getting field values using TemporalField. We use ChronoField constants which implement the interface. The method works consistently across different temporal types.

Checking Field Support

Before accessing a field, we should check if it's supported by the temporal object. The isSupportedBy method verifies field support. This prevents runtime exceptions.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoField;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        
        // Check date field support
        System.out.println("DAY_OF_MONTH supported in date: " + 
            ChronoField.DAY_OF_MONTH.isSupportedBy(date));
        System.out.println("HOUR_OF_DAY supported in date: " + 
            ChronoField.HOUR_OF_DAY.isSupportedBy(date));
            
        // Check time field support
        System.out.println("HOUR_OF_DAY supported in time: " + 
            ChronoField.HOUR_OF_DAY.isSupportedBy(time));
        System.out.println("MONTH_OF_YEAR supported in time: " + 
            ChronoField.MONTH_OF_YEAR.isSupportedBy(time));
    }
}

This example checks field support before access. Date objects typically don't support time fields and vice versa. The method helps write robust temporal code that handles different types.

Getting Field Value Ranges

Fields have valid value ranges that vary by context. The range methods provide this information. They help validate values before setting.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ValueRange;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, 2, 1); // February 2025 (not leap)
        
        // Get general range for day-of-month
        ValueRange generalRange = ChronoField.DAY_OF_MONTH.range();
        System.out.println("General day-of-month range: " + generalRange);
        
        // Get specific range for this date
        ValueRange specificRange = ChronoField.DAY_OF_MONTH.rangeRefinedBy(date);
        System.out.println("Specific day-of-month range: " + specificRange);
        
        // Get range for month-of-year
        ValueRange monthRange = ChronoField.MONTH_OF_YEAR.range();
        System.out.println("Month-of-year range: " + monthRange);
    }
}

This example shows field range access methods. The general range is fixed, while refined range considers context like month length. Range information is crucial for validation.

Adjusting Temporal Objects

The adjustInto method modifies temporal objects by setting field values. It returns a new adjusted object while keeping the original unchanged. This follows the immutable pattern.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, 4, 16);
        LocalDateTime dateTime = LocalDateTime.of(2025, 4, 16, 14, 30);
        
        // Adjust day-of-month
        LocalDate adjustedDate = (LocalDate) ChronoField.DAY_OF_MONTH.adjustInto(date, 25);
        System.out.println("Adjusted date: " + adjustedDate);
        
        // Adjust hour-of-day
        LocalDateTime adjustedDateTime = (LocalDateTime) 
            ChronoField.HOUR_OF_DAY.adjustInto(dateTime, 18);
        System.out.println("Adjusted date-time: " + adjustedDateTime);
        
        // Chain adjustments
        LocalDateTime finalDateTime = (LocalDateTime) 
            ChronoField.MINUTE_OF_HOUR.adjustInto(
                ChronoField.HOUR_OF_DAY.adjustInto(dateTime, 9), 15);
        System.out.println("Final date-time: " + finalDateTime);
    }
}

This example demonstrates temporal adjustments using TemporalField. The method requires casting to the specific temporal type. Multiple adjustments can be chained for complex modifications.

Working with Custom Temporal Fields

We can implement TemporalField for custom fields. This requires defining field behavior and range rules. Custom fields integrate with the Java Time API.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;

public class Main {

    static final TemporalField QUARTER_OF_YEAR = new TemporalField() {
        @Override
        public TemporalUnit getBaseUnit() {
            return ChronoUnit.MONTHS;
        }
        
        @Override
        public TemporalUnit getRangeUnit() {
            return ChronoUnit.YEARS;
        }
        
        @Override
        public ValueRange range() {
            return ValueRange.of(1, 4);
        }
        
        @Override
        public boolean isDateBased() {
            return true;
        }
        
        @Override
        public boolean isTimeBased() {
            return false;
        }
        
        @Override
        public boolean isSupportedBy(TemporalAccessor temporal) {
            return temporal.isSupported(ChronoField.MONTH_OF_YEAR);
        }
        
        @Override
        public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
            return range();
        }
        
        @Override
        public long getFrom(TemporalAccessor temporal) {
            int month = temporal.get(ChronoField.MONTH_OF_YEAR);
            return (month - 1) / 3 + 1;
        }
        
        @Override
        public <R extends Temporal> R adjustInto(R temporal, long newValue) {
            int currentQuarter = getFrom(temporal);
            int monthChange = (int) ((newValue - currentQuarter) * 3);
            return (R) temporal.plus(monthChange, ChronoUnit.MONTHS);
        }
    };

    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2025, 6, 15);
        
        // Get quarter from custom field
        long quarter = QUARTER_OF_YEAR.getFrom(date);
        System.out.println("Quarter: " + quarter);
        
        // Adjust to different quarter
        LocalDate q4Date = (LocalDate) QUARTER_OF_YEAR.adjustInto(date, 4);
        System.out.println("Q4 date: " + q4Date);
    }
}

This example shows a custom quarter-of-year field implementation. The field calculates quarters from months and allows adjustments. Custom fields must properly implement all interface methods.

Combining TemporalField with TemporalQueries

TemporalField can be used with TemporalQuery for flexible temporal access. This combination enables powerful date-time manipulations and queries.

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalQuery;

public class Main {

    public static void main(String[] args) {
        
        LocalDateTime dateTime = LocalDateTime.of(2025, 4, 16, 14, 30);
        
        // Query for month value
        TemporalQuery<Long> monthQuery = ChronoField.MONTH_OF_YEAR::getFrom;
        Long month = dateTime.query(monthQuery);
        System.out.println("Month from query: " + month);
        
        // Query for hour value
        TemporalQuery<Long> hourQuery = ChronoField.HOUR_OF_DAY::getFrom;
        Long hour = dateTime.query(hourQuery);
        System.out.println("Hour from query: " + hour);
        
        // Combined query
        TemporalQuery<String> combinedQuery = temporal -> {
            long y = temporal.get(ChronoField.YEAR);
            long m = temporal.get(ChronoField.MONTH_OF_YEAR);
            long d = temporal.get(ChronoField.DAY_OF_MONTH);
            return String.format("%d-%02d-%02d", y, m, d);
        };
        String formatted = dateTime.query(combinedQuery);
        System.out.println("Formatted date: " + formatted);
    }
}

This example demonstrates combining TemporalField with queries. Field references can be used directly as queries. More complex queries can combine multiple fields for custom results.

Source

Java TemporalField Interface Documentation

In this article, we've covered the essential methods and features of the Java TemporalField interface. Understanding these concepts is crucial for working with date and time fields in 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.