ZetCode

Java TemporalField 接口

最后修改时间:2025 年 4 月 16 日

java.time.temporal.TemporalField 接口表示日期时间的字段,例如年中的月份或小时。 它提供了一种通用的访问日期时间字段的方式。 TemporalField 是 Java Time API 中的关键接口。

TemporalField 定义了在时间对象上获取和设置字段值的方法。 它适用于日期和时间字段。 该接口由 ChronoField 枚举和自定义字段实现来实现。

TemporalField 接口概述

TemporalField 提供了访问字段值和执行验证的方法。 关键操作包括获取值范围和检查支持。 该接口支持跨不同时间类型的通用字段访问。

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);
}

上面的代码显示了 TemporalField 的关键方法。 这些方法允许以通用方式访问和操作时间字段。 该接口支持基于日期和基于时间的字段。

访问字段值

getFrom 方法从时间对象中检索字段值。 这适用于支持该字段的任何时间类型。 该方法将字段值作为 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);
    }
}

此示例演示了使用 TemporalField 获取字段值。 我们使用实现了该接口的 ChronoField 常量。 该方法在不同的时间类型之间一致地工作。

检查字段支持

在访问字段之前,我们应该检查时间对象是否支持它。 isSupportedBy 方法验证字段支持。 这可以防止运行时异常。

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));
    }
}

此示例在访问前检查字段支持。 日期对象通常不支持时间字段,反之亦然。 该方法有助于编写处理不同类型的可靠时间代码。

获取字段值范围

字段具有随上下文变化的有效值范围。 range 方法提供此信息。 它们有助于在设置之前验证值。

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);
    }
}

此示例显示了字段范围访问方法。 一般范围是固定的,而精细范围考虑了上下文,例如月份长度。 范围信息对于验证至关重要。

调整时间对象

adjustInto 方法通过设置字段值来修改时间对象。 它返回一个新的调整后的对象,同时保持原始对象不变。 这遵循了不可变模式。

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);
    }
}

此示例演示了使用 TemporalField 进行时间调整。 该方法需要转换为特定的时间类型。 可以链接多个调整以进行复杂的修改。

使用自定义时间字段

我们可以为自定义字段实现 TemporalField。 这需要定义字段行为和范围规则。 自定义字段与 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);
    }
}

此示例显示了一个自定义的季度字段实现。 该字段根据月份计算季度并允许进行调整。 自定义字段必须正确实现所有接口方法。

将 TemporalField 与 TemporalQueries 结合使用

TemporalField 可以与 TemporalQuery 一起使用,以实现灵活的时间访问。 这种组合支持强大的日期时间操作和查询。

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);
    }
}

此示例演示了将 TemporalField 与查询结合使用。 字段引用可以直接用作查询。 更复杂的查询可以组合多个字段以获得自定义结果。

来源

Java TemporalField 接口文档

在本文中,我们涵盖了 Java TemporalField 接口的基本方法和特性。 了解这些概念对于在 Java 应用程序中处理日期和时间字段至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在该领域拥有多年经验。 我于 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和 8 本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程