Quartz-Calendar 排除指定节假日时间执行任务


在这里插入图片描述

概述

在实际任务调度中,我们不可能一成不变地按照某个周期性的调度规则运行任务,必须考虑到实现生活中日历上特定日期。

由于节日是每年重复的,所以使用org.quartz.Calendar的AnnualCalendar实现类

注意事项

Quartz 的 Calendar 对象与 Java API 的 java.util.Calendar,它们是应用于不同目的不一样的组件。

Java 的 Calendar 对象是通用的日期和时间工具;许多过去由 Java 的 Date 类提供的功能现在加到了 Calendar 类中了。

Quartz 的 Calendar 专门用于屏闭一个时间区间,使 Trigger 在这个区间中不被触发。

Calendar 排除时间的粒度

Calendar 接口方法参数的类型是 Long。这说明 Quartz Calendar 能够排除的时间细致毫秒级。

你很可能永远都不需要这么细小的位度,因为大部分的 Job 只需要排除特别的日期或许会是小时。然而,假如你真需要排除到毫秒一级的,Calendar 能帮你做到.


BaseCalendar子类说明

这里写图片描述

Calendar 名称用法
BaseCalendarorg.quartz.impl.calendar.BaseCalendar为高级的 Calendar 实现了基本的功能,实现了 org.quartz.Calendar 接口
AnnualCalendarorg.quartz.impl.calendar.AnnualCalendar排除年中一天或多天
CronCalendarorg.quartz.impl.calendar.CronCalendar日历的这种实现排除了由给定的CronExpression表达的时间集合。 例如,您可以使用此日历使用表达式“* * 0-7,18-23?* *”每天排除所有营业时间(上午8点至下午5点)。 如果CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。
DailyCalendarorg.quartz.impl.calendar.DailyCalendar您可以使用此日历来排除营业时间(上午8点 - 5点)每天。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。
HolidayCalendarorg.quartz.impl.calendar.HolidayCalendar特别的用于从 Trigger 中排除节假日
MonthlyCalendarorg.quartz.impl.calendar.MonthlyCalendar排除月份中的指定数天,例如,可用于排除每月的最后一天
WeeklyCalendarorg.quartz.impl.calendar.WeeklyCalendar排除星期中的任意周几,例如,可用于排除周末,默认周六和周日

注意,所有的Calendar既可以是排除,也可以是包含,取决于:

  • AnnualCalendar:指定每年的哪一天。使用方式如上例。精度是【天】

  • CronCalendar:指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以【到秒】

  • DailyCalendar:指定每天的时间段(rangeStartingTime,
    rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以【到毫秒】

  • HolidayCalendar:指定特定的日期,比如20140613。精度到【天】

  • MonthlyCalendar:指定每月的几号。可选值为1-31。精度是【天】

  • WeeklyCalendar:指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是【天】


示例

要使用 Quartz Calendar,你只需简单的实例化,并加入你要排除的日期,然后用 Scheduler 注册它。最后把这个 Calendar 实例与你想要使用该Calendar 的每一个 Trigger 实例关联起来


package com.xgj.quartz.quartzItself.calendarDemo;

import static org.quartz.DateBuilder.dateOf;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.AnnualCalendar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This example will demonstrate how calendars can be used to exclude periods of
 * time when scheduling should not take place.
 */
public class CalendarExample {

	public void run() throws Exception {
		final Logger log = LoggerFactory.getLogger(CalendarExample.class);

		log.info("------- Initializing ----------------------");

		// First we must get a reference to a scheduler
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched = sf.getScheduler();

		log.info("------- Initialization Complete -----------");

		log.info("------- Scheduling Jobs -------------------");

		// Add the holiday calendar to the schedule
		AnnualCalendar holidays = new AnnualCalendar();

		// fourth of July (July 4) Independence Day Of USA
		Calendar fourthOfJuly = new GregorianCalendar(2005, 6, 4);
		holidays.setDayExcluded(fourthOfJuly, true);
		// halloween (Oct 31)
		Calendar halloween = new GregorianCalendar(2005, 9, 31);
		holidays.setDayExcluded(halloween, true);
		// christmas (Dec 25)
		Calendar christmas = new GregorianCalendar(2005, 11, 25);
		holidays.setDayExcluded(christmas, true);

		// tell the schedule about our holiday calendar
		sched.addCalendar("holidays", holidays, false, false);

		// schedule a job to run hourly, starting on halloween
		// at 10 am
		Date runDate = dateOf(0, 0, 10, 31, 10);

		JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1")
				.build();

		SimpleTrigger trigger = newTrigger()
				.withIdentity("trigger1", "group1")
				.startAt(runDate)
				.withSchedule(
						simpleSchedule().withIntervalInHours(1).repeatForever())
				.modifiedByCalendar("holidays").build();

		// schedule the job and print the first run date
		Date firstRunTime = sched.scheduleJob(job, trigger);

		// print out the first execution date.
		// Note: Since Halloween (Oct 31) is a holiday, then
		// we will not run until the next day! (Nov 1)
		log.info(job.getKey() + " will run at: " + firstRunTime
				+ " and repeat: " + trigger.getRepeatCount() + " times, every "
				+ trigger.getRepeatInterval() / 1000 + " seconds");

		// All of the jobs have been added to the scheduler, but none of the
		// jobs
		// will run until the scheduler has been started
		log.info("------- Starting Scheduler ----------------");
		sched.start();

		// wait 30 seconds:
		// note: nothing will run
		log.info("------- Waiting 30 seconds... --------------");
		try {
			// wait 30 seconds to show jobs
			Thread.sleep(30L * 1000L);
			// executing...
		} catch (Exception e) {
			//
		}

		// shut down the scheduler
		log.info("------- Shutting Down ---------------------");
		sched.shutdown(true);
		log.info("------- Shutdown Complete -----------------");

		SchedulerMetaData metaData = sched.getMetaData();
		log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");

	}

	public static void main(String[] args) throws Exception {

		CalendarExample example = new CalendarExample();
		example.run();
	}

}

package com.xgj.quartz.quartzItself.calendarDemo;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * This is just a simple job that gets fired off many times by example 1
 * </p>
 * 
 */
public class SimpleJob implements Job {

    private static Logger _log = LoggerFactory.getLogger(SimpleJob.class);

    /**
     * Empty constructor for job initialization
     */
    public SimpleJob() {
    }

    /**
     * <p>
     * Called by the <code>{@link org.quartz.Scheduler}</code> when a
     * <code>{@link org.quartz.Trigger}</code> fires that is associated with
     * the <code>Job</code>.
     * </p>
     * 
     * @throws JobExecutionException
     *             if there is an exception while executing the job.
     */
    public void execute(JobExecutionContext context)
        throws JobExecutionException {

        // This job simply prints out its job name and the
        // date and time that it is running
        JobKey jobKey = context.getJobDetail().getKey();
        _log.info("SimpleJob says: " + jobKey + " executing at " + new Date());
    }

}



AnnualCalendar

package com.xgj.quartz.quartzItself.calendarDemo.AnnualCalendar;

import static org.quartz.DateBuilder.dateOf;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.AnnualCalendar;

import com.xgj.quartz.quartzItself.calendarDemo.SimpleJob;
/**
 * 
 * 
 * @ClassName: AnnualCalendarDemo
 * 
 * @Description: 此示例将演示如何使用日历来排除不应该进行调度的时间段。
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年11月15日 下午5:14:57
 */
public class AnnualCalendarDemo {
	public static void main(String[] args) throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		System.out.println("-------  初始化 ----------");

		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched = sf.getScheduler();

		// 声明一个节假日 holidayCalendar,标明要排除的日期
		// 法定节日是以每年为周期的,所以使用AnnualCalendar而不是HolidayCalendar
		AnnualCalendar holidays = new AnnualCalendar();

		Calendar fourthOfJuly = new GregorianCalendar(2017, 6, 4); // fourth of
																	// July
																	// (July 4)
																	// 七月四日
		holidays.setDayExcluded(fourthOfJuly, true);
		System.out.println("第一个节假日:" + sdf.format(fourthOfJuly.getTime()));

		Calendar halloween = new GregorianCalendar(2017, 9, 31); // halloween
																	// (Oct 31)
																	// 万圣节(10月31日)
		holidays.setDayExcluded(halloween, true);
		System.out.println("第二节假日:" + sdf.format(halloween.getTime()));

		Calendar christmas = new GregorianCalendar(2017, 11, 25); // christmas
																	// (Dec 25)
																	// christmas
																	// (Dec 25)
		holidays.setDayExcluded(christmas, true);
		System.out.println("第三个节假日:" + sdf.format(christmas.getTime()));

		sched.addCalendar("holidays", holidays, false, false); // 节假日加入schedule调度器

		// 开始在万圣节前夜上午10点,开始任务
		Date runDate = dateOf(0, 0, 10, 31, 10);
		System.out.println("任务开始时间:" + sdf.format(runDate));

		JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1")
				.build();

		SimpleTrigger trigger = newTrigger()
				.withIdentity("trigger1", "group1")
				.startAt(runDate)
				.withSchedule(
						simpleSchedule().withIntervalInHours(1).repeatForever())
				.modifiedByCalendar("holidays").build();

		Date firstRunTime = sched.scheduleJob(job, trigger);

		// 注意:万圣节(10月31日)是假期,所以直到第二天才会运行! (11月1日)
		System.out.println(job.getKey() + " 将运行于:" + firstRunTime + " 并重复:"
				+ trigger.getRepeatCount() + " 次, 间隔 "
				+ trigger.getRepeatInterval() / 1000 + " 秒");

		System.out.println("------- 开始 Scheduler ----------------");
		sched.start();

		System.out.println("------- 等待 30 秒... --------------");
		try {
			Thread.sleep(30L * 1000L);
		} catch (Exception e) {
		}

		sched.shutdown(true);
		System.out.println("------- 关闭调度器 -----------------");

		SchedulerMetaData metaData = sched.getMetaData();
		System.out.println("执行了: " + metaData.getNumberOfJobsExecuted()
				+ " 个jobs.");
	}

}


CronCalendar

package com.xgj.quartz.quartzItself.calendarDemo.CronCalendar;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.CronCalendar;

import com.xgj.quartz.quartzItself.calendarDemo.SimpleJob;

public class CronCalendarDemo {
	public static void main(String[] args) throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		System.out.println("-------  初始化 ----------");

		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched = sf.getScheduler();
		// 允许执行的时间, 星期参数:"7" = "SAT",2 = MON
		// 【秒】 【分钟】 【小时】 【月中天】 【月】 【周中天(1-7)】 [【年(可省略)】]
		String excludeExpression;

		// 这里设置禁用时间段为,每0-20之间,40-59之间不执行
		excludeExpression = "0-20,40-59 * * * * ?";
		CronCalendar cronCalendar = new CronCalendar(excludeExpression);

		// 标明要排除的日期 每天的17点10分
		sched.addCalendar("cronCalendar", cronCalendar, false, false); // 节假日加入schedule调度器

		Date runDate = new Date();
		System.out.println("任务开始时间:" + sdf.format(runDate));

		// 任务每10秒执行一次
		JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1")
				.build();
		SimpleTrigger trigger = newTrigger()
				.withIdentity("trigger1", "group1")
				.startAt(runDate)
				.withSchedule(
						simpleSchedule().withIntervalInSeconds(10)
								.repeatForever())
				.modifiedByCalendar("cronCalendar").build();

		// 触发器加入调度器
		Date firstRunTime = sched.scheduleJob(job, trigger);

		System.out.println(job.getKey() + " 将运行于:" + sdf.format(firstRunTime)
				+ " 并重复:" + trigger.getRepeatCount() + " 次, 间隔 "
				+ trigger.getRepeatInterval() / 1000 + " 秒");

		System.out.println("------- 开始 Scheduler ----------------");
		sched.start();

		try {
			System.out.println("------- 等待 120 秒(2分钟)... --------------");
			Thread.sleep(120L * 1000L);
			// do something
		} catch (Exception e) {
		}

		sched.shutdown(true);
		System.out.println("------- 关闭调度器 -----------------");

		SchedulerMetaData metaData = sched.getMetaData();
		System.out.println("~~~~~~~~~~  执行了 "
				+ metaData.getNumberOfJobsExecuted() + " 个 jobs.");

	}
}


DailyCalendar

package com.xgj.quartz.quartzItself.calendarDemo.DailyCalendar;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.DailyCalendar;

import com.xgj.quartz.quartzItself.calendarDemo.SimpleJob;
/**
 * 
 * 
 * @ClassName: DailyCalendarDemo
 * 
 * @Description: 注意:dailyCalendar.setInvertTimeRange(true); //
 *               时间反转,为true表示只有这次时间段才会被执行,为false表示排除这时间段
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年11月15日 下午5:40:00
 */
public class DailyCalendarDemo {
	public static void main(String[] args) throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		System.out.println("-------  初始化 ----------");

		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched = sf.getScheduler();

		DailyCalendar dailyCalendar = new DailyCalendar("12:17:30", "12:18:20");
		dailyCalendar.setInvertTimeRange(true); // 时间反转,为true表示只有这次时间段才会被执行,为false表示排除这时间段

		// 标明要排除的日期 每天的17点10分
		sched.addCalendar("dailyCalendar", dailyCalendar, false, false); // 节假日加入schedule调度器

		Date runDate = new Date();
		System.out.println("任务开始时间:" + sdf.format(runDate));

		// 任务每10秒执行一次
		JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1")
				.build();
		SimpleTrigger trigger = newTrigger()
				.withIdentity("trigger1", "group1")
				.startAt(runDate)
				.withSchedule(
						simpleSchedule().withIntervalInSeconds(10)
								.repeatForever())
				.modifiedByCalendar("dailyCalendar").build();

		Date firstRunTime = sched.scheduleJob(job, trigger);

		System.out.println(job.getKey() + " 将运行于:" + sdf.format(firstRunTime)
				+ " 并重复:" + trigger.getRepeatCount() + " 次, 间隔 "
				+ trigger.getRepeatInterval() / 1000 + " 秒");

		System.out.println("------- 开始 Scheduler ----------------");
		sched.start();

		System.out.println("------- 等待 360 秒(3分钟)... --------------");
		try {
			Thread.sleep(360L * 1000L);
			// do something
		} catch (Exception e) {
		}

		sched.shutdown(true);
		System.out.println("------- 关闭调度器 -----------------");

		SchedulerMetaData metaData = sched.getMetaData();
		System.out.println("~~~~~~~~~~  执行了 "
				+ metaData.getNumberOfJobsExecuted() + " 个 jobs.");
	}
}


HolidayCalendar

HolidayCalendar holidayCalendar = new HolidayCalendar();
Calendar calendar = new GregorianCalendar(2017, 10, 1);    // 2017年11月1日
holidayCalendar.addExcludedDate(calendar.getTime());

calendar = new GregorianCalendar(2018, 10, 2);            // 2018年11月2日
holidayCalendar.addExcludedDate(calendar.getTime());

holidayCalendar.getExcludedDates().forEach(date -> {
    System.out.println("假期日:"+ sdf.format(date));
});

sched.addCalendar("holidays", holidayCalendar, false, false);      // 节假日加入schedule调度器

MonthlyCalendar

月日历,你可以定义一个月当中的若干天,例如你可以设置每个月的第一天触发器不进行触发,当然你还可以定义一个月当中的任何一天。

// 设置2,3,4月不触发任务
MonthlyCalendar monthlyCalendar = new MonthlyCalendar();
monthlyCalendar.setDayExcluded(2, true);
monthlyCalendar.setDayExcluded(3, true);
monthlyCalendar.setDayExcluded(4, true);

sched.addCalendar("monthlys", monthlyCalendar, false, false);      // 节假日加入schedule调度器

javaWeeklyCalendar

星期日历,可以定义在一个星期当中的星期几几几 是不触发的日期,例如你可以定义么每个周末(星期天)触发器不触发,你也可以定义一周当中的任何一天或是几天。默认情况SATURDAY ,SUNDAY 这两天是没排除的。

下面的例子设置了每个星期四触发器不触发,并且默认情况周六和周天也是不触发的,这个是默认设置。

如果需要周六周日也触发,那么把它清掉就可以了(weeklyCalendar.setDayExcluded(Calendar.SATURDAY , false)像这样)。

一个需要注意的地方就是传入参数不能直接写数字星期几,因为老外的日子计算的与我们不一样,需要传入(java.util.Calendar)的常量字段,这样才准确。

WeeklyCalendar weeklyCalendar = new WeeklyCalendar();
weeklyCalendar.setDayExcluded(Calendar.THURSDAY, true);
sched.addCalendar("weeklys", weeklyCalendar, false, false);      // 节假日加入schedule调度器

组合日历的使用

上面的例子都是每一个触发器(trigger)关联一个日历的例子,我们在构建触发器的时候通过.modifiedByCalendar(“日历的key”)关联一个注册到引擎当中的日历,这种情况已经能够满足我们大部分的需求。

但是系统的需求往往是复杂多变的,假设有这样一种情况,需要一个触发器在 每周一到周五,早8点-晚晚5点 每隔1小时执行,那么该如何使用日历呢?

其实我们不用日历,使用一个CronTrigger也是可以搞定的,我们这里只不过是抛砖引玉而已。

那让我们来写一个组合日历使用的例子:

DailyCalendar dailyCalendar = new DailyCalendar("8:00:00", "17:00:00");
dailyCalendar.setInvertTimeRange(false);
        
WeeklyCalendar weeklyCalendar = new WeeklyCalendar(dailyCalendar);
sched.addCalendar("weeklyCalendar", weeklyCalendar, false, false);

我们写一个时间间隔的日历dailyCalendar,将其作为参数传递给weeklyCalendar就可以了,这样引擎在计算日历日期的时候会先判断dailyCalendar的时间范围,然后再判断weeklyCalendar是时间范围,当条件都满足的时候,触发器才会被触发


示例源码

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值