ZetCode

Java TemporalAdjuster 接口

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

java.time.temporal.TemporalAdjuster 接口提供灵活的日期调整功能。 它允许进行超出简单加/减操作的复杂日期操作。 实现可以找到下一个或上一个星期几。

TemporalAdjuster 是一个只有一个方法的函数式接口。 它适用于 java.time 包中的所有时间类型。 TemporalAdjusters 实用程序类提供了常见的调整。

TemporalAdjuster 接口概述

该接口定义了一个方法 adjustInto,它接受一个时间对象并返回一个调整后的版本。 TemporalAdjusters 类提供了许多预定义的调整器。 自定义调整器可以实现复杂的逻辑。

public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}

public final class TemporalAdjusters {
    public static TemporalAdjuster firstDayOfMonth();
    public static TemporalAdjuster lastDayOfMonth();
    public static TemporalAdjuster firstDayOfNextMonth();
    public static TemporalAdjuster firstDayOfYear();
    public static TemporalAdjuster lastDayOfYear();
    public static TemporalAdjuster firstDayOfNextYear();
    public static TemporalAdjuster next(DayOfWeek dayOfWeek);
    public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek);
    public static TemporalAdjuster previous(DayOfWeek dayOfWeek);
    public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek);
}

代码显示了 TemporalAdjusters 中的接口和关键方法。 这些提供了常见的日期调整,例如查找月份边界或特定的工作日。 调整器是线程安全的且不可变的。

使用预定义的调整器

TemporalAdjusters 类提供了许多有用的调整器。 这些处理常见情况,例如查找月份边界或特定的工作日。 它们可以与支持调整的任何时间类型一起使用。

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15);
        
        // First day of month
        LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("First day of month: " + firstDay);
        
        // Last day of month
        LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month: " + lastDay);
        
        // Next Tuesday
        LocalDate nextTuesday = date.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
        System.out.println("Next Tuesday: " + nextTuesday);
        
        // First day of next year
        LocalDate firstNextYear = date.with(TemporalAdjusters.firstDayOfNextYear());
        System.out.println("First day of next year: " + firstNextYear);
    }
}

此示例演示了几个预定义的调整器。 每个调整都会创建一个新的日期对象,而不会修改原始对象。 调整器会自动处理边缘情况,例如月份长度变化。

查找工作日

TemporalAdjusters 提供了查找相对于日期的特定工作日的方法。 这些包括下一个、上一个和同一天变体。 它们对于安排重复发生的事件很有用。

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15); // Tuesday
        
        // Next Friday
        LocalDate nextFriday = date.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("Next Friday: " + nextFriday);
        
        // Previous Monday
        LocalDate prevMonday = date.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
        System.out.println("Previous Monday: " + prevMonday);
        
        // Next or same Wednesday
        LocalDate nextOrSameWed = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY));
        System.out.println("Next or same Wednesday: " + nextOrSameWed);
        
        // Previous or same Friday
        LocalDate prevOrSameFri = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
        System.out.println("Previous or same Friday: " + prevOrSameFri);
    }
}

此示例显示了与工作日相关的调整器。 如果当前日期匹配,则 "next" 和 "previous" 方法会排除当前日期。 当 "OrSame" 变体与目标工作日匹配时,它们会包含当前日期。

自定义 Temporal Adjuster

自定义调整器可以实现复杂的日期逻辑。 它们通过实现接口或使用 lambda 表达式来创建。 这为特定于业务的日期规则提供了灵活性。

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

public class Main {

    static class NextPaydayAdjuster implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            int day = date.getDayOfMonth();
            
            if (day < 15) {
                return date.withDayOfMonth(15);
            } else {
                return date.withDayOfMonth(date.lengthOfMonth());
            }
        }
    }

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 10);
        TemporalAdjuster paydayAdjuster = new NextPaydayAdjuster();
        
        // Using class implementation
        LocalDate nextPayday = date.with(paydayAdjuster);
        System.out.println("Next payday: " + nextPayday);
        
        // Using lambda expression
        TemporalAdjuster taxDayAdjuster = t -> {
            LocalDate d = LocalDate.from(t);
            return d.withMonth(4).withDayOfMonth(15);
        };
        
        LocalDate taxDay = date.with(taxDayAdjuster);
        System.out.println("Tax day: " + taxDay);
    }
}

此示例显示了创建自定义调整器的两种方法。 第一个使用实现该接口的类。 第二个使用 lambda 表达式。 两种方法都可以实现所需的任何日期计算逻辑。

组合调整器

可以组合调整器以按顺序执行多个操作。 每个调整都应用于前一个调整的结果。 这允许从简单的步骤构建复杂的日期计算。

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

public class Main {

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, Month.APRIL, 15);
        
        // First go to first of month, then find next Friday
        LocalDate result = date.with(TemporalAdjusters.firstDayOfMonth())
                              .with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("First Friday of month: " + result);
        
        // First find next Monday, then go to end of that month
        LocalDate result2 = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
                               .with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month containing next Monday: " + result2);
        
        // Custom combined adjuster
        TemporalAdjuster combined = t -> {
            Temporal temp = TemporalAdjusters.firstDayOfMonth().adjustInto(t);
            return TemporalAdjusters.next(DayOfWeek.WEDNESDAY).adjustInto(temp);
        };
        
        LocalDate result3 = date.with(combined);
        System.out.println("First Wednesday of month: " + result3);
    }
}

此示例演示了以不同方式组合调整器。 前两个示例直接链接调整。 第三个创建了一个组合的调整器作为 lambda。 所有方法都实现了顺序调整的相同目标。

调整时间对象

虽然通常与日期一起使用,但 TemporalAdjusters 也适用于时间对象。 它们可以调整 LocalDateTime、ZonedDateTime 和其他时间类型。 调整逻辑必须与时间类型兼容。

Main.java
package com.zetcode;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

public class Main {

    public static void main(String[] args) {
        
        LocalDateTime dateTime = LocalDateTime.of(2025, 4, 15, 10, 30);
        
        // Adjust LocalDateTime
        LocalDateTime nextMonday = dateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        System.out.println("Next Monday at same time: " + nextMonday);
        
        // Adjust ZonedDateTime
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("America/New_York"));
        ZonedDateTime lastDay = zoned.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month in NY: " + lastDay);
        
        // Custom time adjuster
        TemporalAdjuster toNoon = t -> {
            if (t.isSupported(ChronoField.HOUR_OF_DAY)) {
                return t.with(ChronoField.HOUR_OF_DAY, 12)
                       .with(ChronoField.MINUTE_OF_HOUR, 0)
                       .with(ChronoField.SECOND_OF_MINUTE, 0);
            }
            return t;
        };
        
        LocalDateTime noon = dateTime.with(toNoon);
        System.out.println("Adjusted to noon: " + noon);
    }
}

此示例显示了调整器与不同时间类型一起工作。 自定义时间调整器演示了专门处理时间字段。 请注意在尝试调整之前检查字段支持。

工作日调整器

一个常见的用例是将日期调整为工作日。 这需要跳过周末并可能跳过节假日。 该示例显示了一个基本的工作日调整器实现。

Main.java
package com.zetcode;

import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.ChronoUnit;

public class Main {

    static class BusinessDaysAdjuster implements TemporalAdjuster {
        private final int days;
        
        public BusinessDaysAdjuster(int days) {
            this.days = days;
        }
        
        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate date = LocalDate.from(temporal);
            int remaining = days;
            int step = Integer.signum(remaining);
            
            while (remaining != 0) {
                date = date.plus(step, ChronoUnit.DAYS);
                if (date.getDayOfWeek() != DayOfWeek.SATURDAY &&
                    date.getDayOfWeek() != DayOfWeek.SUNDAY) {
                    remaining -= step;
                }
            }
            return temporal.with(date);
        }
    }

    public static void main(String[] args) {
        
        LocalDate date = LocalDate.of(2025, 4, 15); // Tuesday
        TemporalAdjuster add5BusinessDays = new BusinessDaysAdjuster(5);
        TemporalAdjuster subtract3BusinessDays = new BusinessDaysAdjuster(-3);
        
        LocalDate in5Days = date.with(add5BusinessDays);
        System.out.println("5 business days later: " + in5Days);
        
        LocalDate before3Days = date.with(subtract3BusinessDays);
        System.out.println("3 business days before: " + before3Days);
    }
}

此示例实现了一个跳过周末的工作日调整器。 该调整器适用于向前和向后调整。 更复杂的版本可以合并节假日日历,以进行完整的工作日期计算。

来源

Java TemporalAdjuster 文档

在本文中,我们介绍了 Java TemporalAdjuster 接口的基本方法和功能。 了解这些概念对于现代 Java 应用程序中的复杂日期操作至关重要。

作者

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

列出所有Java教程