You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Quartz Scheduler API创建每两周固定工作日执行的触发器

Quartz Scheduler 2.3.0 双周指定工作日Trigger配置方案

需求对应配置逻辑

你的触发规则可以通过CronTrigger + 周间隔过滤的组合实现,核心分为两步:先用Cron规则限定仅周一、周二、周五的08:00:00触发,再通过周间隔校验实现每两周执行的要求。


方案1:CronTrigger + Job层过滤(推荐,无侵入性配置)

该方案无需修改Quartz核心配置,仅在触发逻辑中增加间隔校验即可。

步骤1:构造Trigger

import org.quartz.*;
import java.util.Calendar;
import java.util.Date;

// 构造起始时间:2021年1月25日 08:00:00
Calendar startCal = Calendar.getInstance();
startCal.set(2021, Calendar.JANUARY, 25, 8, 0, 0);
Date startTime = startCal.getTime();

// Cron表达式:匹配所有周一、周二、周五的08:00:00
String cron = "0 0 8 ? * MON,TUE,FRI";

Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("biWeeklyTrigger", "businessGroup")
        .startAt(startTime)
        .withSchedule(CronScheduleBuilder.cronSchedule(cron)
                .withMisfireHandlingInstructionFireAndProceed()) // 配置错过触发的处理策略
        .usingJobData("lastExecWeekOfYear", 0) // 存储上次执行的年周数
        .build();

步骤2:Job执行逻辑中增加间隔校验

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
    JobDataMap triggerData = context.getTrigger().getJobDataMap();
    int lastExecWeek = triggerData.getInt("lastExecWeekOfYear");
    Calendar current = Calendar.getInstance();
    int currentWeek = current.get(Calendar.WEEK_OF_YEAR);
    int currentYear = current.get(Calendar.YEAR);

    // 首次执行直接放行,非首次判断周间隔是否为2周
    if (lastExecWeek == 0 || (currentWeek - lastExecWeek) % 2 == 0) {
        // 你的业务执行逻辑
        // --------------------------
        // doSomething();
        // --------------------------
        // 更新上次执行周数,跨年场景可同步存储年份做差值计算
        triggerData.put("lastExecWeekOfYear", currentWeek);
    }
}

方案2:自定义Calendar全局过滤(无需修改Job代码)

如果不想修改业务Job的代码,可以通过Quartz自带的Calendar扩展实现全局过滤:

步骤1:自定义双周过滤日历

import org.quartz.impl.calendar.BaseCalendar;
import java.util.Calendar;

public class BiWeeklyCalendar extends BaseCalendar {
    private final int startWeek;
    private final int startYear;

    public BiWeeklyCalendar(int startYear, int startWeek) {
        this.startYear = startYear;
        this.startWeek = startWeek;
    }

    @Override
    public boolean isTimeIncluded(long timeStamp) {
        if (!super.isTimeIncluded(timeStamp)) return false;
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timeStamp);
        int currentYear = cal.get(Calendar.YEAR);
        int currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
        // 计算总周差,兼容跨年场景
        int weekDiff = (currentYear - startYear) * 52 + (currentWeek - startWeek);
        return weekDiff % 2 == 0;
    }
}

步骤2:注册日历并绑定Trigger

// 计算起始时间的年和周
Calendar startCal = Calendar.getInstance();
startCal.set(2021, Calendar.JANUARY, 25, 8, 0, 0);
int startYear = startCal.get(Calendar.YEAR);
int startWeek = startCal.get(Calendar.WEEK_OF_YEAR);

// 注册自定义日历到调度器
BiWeeklyCalendar biWeeklyCalendar = new BiWeeklyCalendar(startYear, startWeek);
scheduler.addCalendar("biWeeklyCal", biWeeklyCalendar, false, false);

// 构建Trigger时绑定日历
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("biWeeklyTrigger", "businessGroup")
        .startAt(startCal.getTime())
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 ? * MON,TUE,FRI"))
        .modifiedByCalendar("biWeeklyCal") // 绑定过滤规则
        .build();

注意事项

  • Quartz的周数字规则为1=周日、2=周一、...、7=周六,直接使用MON/TUE/FRI的缩写可避免混淆
  • 如果仅需要每月的第1、3周触发,不需要跨月连续双周间隔,可直接使用Cron表达式0 0 8 ? * MON#1,MON#3,TUE#1,TUE#3,FRI#1,FRI#3,无需额外过滤逻辑
  • 周数计算可根据业务需要调整Calendar的周起始规则,比如设置周一为一周第一天:cal.setFirstDayOfWeek(Calendar.MONDAY)

内容的提问来源于stack exchange,提问作者Sejal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 01:45:02