石英计划触发器 - 获取下一次触发时间

28
我正在使用Quartz CronTrigger工具来解析cron调度格式字符串,以确定特定作业应该何时运行。虽然我实际上没有使用Quartz来预定作业。 CronTrigger中有一个名为getFireTimeAfter(Date)的方法,可以在给定日期之后给出作业下一次执行的时间。如果提供的日期是现在或将来,则此方法效果很好。但如果日期是过去的,则似乎无法正常工作。
Date currTime = new Date();
CronTrigger tr = new CronTrigger();
tr.setCronExpression("0 0 23 3,18 * ? *");
Date nextFireAt = tr.getFireTimeAfter(currTime);
System.out.println("Reference time: " + currTime);
System.out.println("Next fire after reference time: " + nextFireAt);

如何设置cron表达式,以在每个月的第3天和第18天的23:00触发。例如,如果今天是8月11日,则设置如下:

Reference time: Thu Aug 11 10:04:25 MDT 2011
Next fire after reference time: Thu Aug 18 23:00:00 MDT 2011

但是如果我将参考日期设置为过去,它会给出相同的下一个触发时间。

Reference time: Wed Dec 31 17:00:00 MST 1969
Next fire after reference time: Thu Aug 18 23:00:00 MDT 2011
我原本期望的输出是:
Reference time: Wed Dec 31 17:00:00 MST 1969
Next fire after reference time: Wed Aug 3 23:00:00 MDT 2011

这个方法是不是本来就不能那样工作,还是我做错了什么?

谢谢!


@Kal - 是的,我知道。我正在尝试弄清楚触发器在给定日期之后何时会触发,即使它是在过去发生的。 - cambo
5个回答

43

您真正想使用CronExpression对象,而不是CronTrigger对象。正如您发现的那样,它不会计算过去的下次运行时间...但CronExpression会!

CronExpression有一个方法:getNextValidTimeAfter。这就是您想要的。


有没有一种方法可以在日期时间范围内找到所有出现的情况? - Manik Arora
@ManikArora:你可以使用while循环,将开始日期作为参数传递给getNextValidTimeAfter函数,并在每次迭代中传递返回的时间戳,只要这些时间戳仍然小于结束时间,就继续循环。 在每次迭代中,您可以将结果日期添加到出现列表中。 - cvacca

6

在Spring中,您可以遵循他们在集成测试中使用的相同方法。 CronTriggerTests

CronTrigger trigger = new CronTrigger();
trigger.setCronExpression("0 0 23 3,18 * ? *");
SimpleTriggerContext triggerContext = new SimpleTriggerContext();
triggerContext.update(null, null, new Date());
Date nextFireAt = trigger.nextExecutionTime(triggerContext);

1

CronTrigger.getFireTimeAfter() 方法具有保护机制,以防止指定的时间在过去。

可以通过首先使用过去的时间调用 setStartTime 来规避此问题。然后,调用 getFireTimeAfter 应返回有效结果。


0

这有点奇怪,但保护机制是存在的。不知道 Quartz 团队为什么要进行那项检查。下面的测试只有在我将一个小时添加到 nxtDay (nxtDay = nxtTrigger.Value.DateTime.AddHours(1);) 后才会通过。

var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");

//FREQUENCY OF SECONDS 
var reccurrence = new Reccurrence
{
    StartTime = new TimeSpan(9, 0, 0),
    StopTime = new TimeSpan(11, 0, 0),
    WeekDay = "MON-SUN",
    TimeZoneInfo = timeZoneInfo,
    Frequency = 15,
    FrequencyUnit = FrequencyUnit.Seconds
};

var autoPublishDetail = new AutoPublishJobDetail("TestReccurence_Seconds", _log, null, reccurrence);
var trigger = autoPublishDetail.GetDailyTrigger();
var nxt = trigger.GetNextFireTimeUtc();

var jobSeconds = JobBuilder.Create().WithIdentity(new JobKey("TestReccurence_Seconds")).OfType(typeof(AutoPublishJob)).Build();

_scheduler.ScheduleJob(jobSeconds, trigger);

//This job sud run 4 times in a 60 secs and from 9 am to 11 am (Span of 3 hrs including 11 onwards to 11:59:59)
//Total runs = 4 x 60 x 3 = 720
var today = DateTime.Now;
var tomorrow = today.AddDays(1);
DateTime? nxtDay = new DateTime(tomorrow.Year, tomorrow.Month, tomorrow.Day);

int jobCnt = 0;
do
{
    var nxtTrigger = trigger.GetFireTimeAfter(nxtDay.Value);

    if (nxtTrigger.Value.Year.Equals(today.AddDays(2).Year) && nxtTrigger.Value.Month.Equals(today.AddDays(2).Month) && nxtTrigger.Value.Day.Equals(today.AddDays(2).Day))
        break;

    jobCnt++;
    nxtDay = nxtTrigger.Value.DateTime.AddHours(1);

} while (true);

Assert.AreEqual(720, jobCnt);

0
根据Quartz doc

返回给定时间之后CronTrigger将要触发的下一个时间

“将要触发”意味着“在未来”。也就是说,下一次CronTrigger将要触发的时间不是过去,并且不会返回过去的日期。
我不知道您想要实现什么,但您可能会对以下方法感兴趣:
  • getPreviousFireTime():返回触发器上一次触发的时间
  • getTimeBefore():返回给定日期之前触发器触发的时间(适用于过去)--注意:目前似乎未实现此方法

我正在尝试使用CronTrigger来确定某个“到期日期”是否已经发生。我拥有的两个信息是“上次到期时间”和cron计划。所以我想做的是使用getFireTimeAfter(lastDueDate)来告诉我是否已经过了当前的到期日期。看起来getTimeBefore()可能就是我要找的东西——可惜它还没有被实现。 - cambo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接