如何使用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
相关产品推荐
相关产品推荐

