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

举报
小工匠 发表于 2021/09/09 23:51:45 2021/09/09
【摘要】 文章目录 概述注意事项Calendar 排除时间的粒度BaseCalendar子类说明示例AnnualCalendarCronCalendarDailyCalendarHolidayCalenda...


在这里插入图片描述

概述

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

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

文章来源: artisan.blog.csdn.net,作者:小小工匠,版权归原作者所有,如需转载,请联系作者。

原文链接:artisan.blog.csdn.net/article/details/78174050

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。