使用 Java 中的 Cron 表达式查找上次触发时间

发布于 2024-08-30 09:19:01 字数 347 浏览 10 评论 0原文

Java 有没有办法从 Cron 表达式中找到“上次触发时间”?

例如,如果 now = 25-Apr-2010 10PM,并且 cron 表达式为 0 15 10 ? * * (quartz),它应该返回 25-Apr-2010 10:15AM

注意:

  1. 我不在乎我们是否使用标准 cron 表达式(如 Unix 和 Quartz)或不太流行的表达式,如果它们可以为我获取正确的“上次触发时间”
  2. 而且它也不是字面上的“上次触发时间”,因为触发器可能没有发射,但从逻辑上讲,应该有一种方法来判断它最后一次发射的时间。

Is there a way in Java to find the "Last Fired Time" from a Cron Expression?

E.g. If now = 25-Apr-2010 10PM, and the cron expression is 0 15 10 ? * * (quartz), it should return 25-Apr-2010 10:15AM.

Note:

  1. I do not care if we use standard cron expressions (like Unix and Quartz) or less popular ones if they can fetch me the correct "Last Fired Time"
  2. Also it is not literally "Last Fire time" as the trigger may not have fired, but logically there should be a way of telling when it (would have) fired last.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

牛↙奶布丁 2024-09-06 09:19:01

cron-utils 是一个开源 Java 库,用于解析、验证、迁移 cron,支持您所需的操作。
要简单地从给定时间之前的 cron 获取上一个日期:

//Get date for last execution
DateTime now = DateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("* * * * * * *"));
DateTime lastExecution = executionTime.lastExecution(now));

请记住,在当前状态下,它有点错误,并且可能无法正确计算更复杂的 cron 表达式。

cron-utils is an opensource Java library to parse, validate, migrate crons that supports the operation you need.
To get the previous date from a cron before a given time simply:

//Get date for last execution
DateTime now = DateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("* * * * * * *"));
DateTime lastExecution = executionTime.lastExecution(now));

Bear in mind that in its current state it is a bit buggy and may not compute correctly for more complex cron expressions.

风吹过旳痕迹 2024-09-06 09:19:01

首先,我不知道现有的库支持此功能。 Quartz 可能会,但标准 Java 类库肯定不会。

其次,严格来说你所要求的最初所要求的是不可能的。库可以告诉您的最好信息是 cron 表达式应该触发。唯一可以(理论上)告诉您 cron 表达式最后一次实际触发的时间的是调度程序实例本身。

First, I am not aware of an existing library that supports this. Quartz might, but the standard Java class libraries certainly don't.

Second, strictly speaking what you are asking for originally asked for is impossible. The best that a library can tell you is that the cron expression would have or should have fired. The only thing that could (theoretically) tell you the last time that a cron expression actually fired is the scheduler instance itself.

明月夜 2024-09-06 09:19:01

Quartz 似乎有一些对 cron 表达式的库支持。

请参阅CronExpression 类的 Javadoc,它有一个方法称为 getTimeBefore。即,

CronExpression cron = new CronExpression("0 15 10 ? * *");
Date today = new Date();
Date previousExecution = cron.getTimeBefore(today);

这是否有效可能取决于 Quartz 版本。

查看最新的源代码(撰写本文时版本为 2.3.0),该方法尚未实现,并且始终返回 null。

Quartz seems to have some library support for cron expressions.

See the Javadoc for the CronExpression class, which has a method called getTimeBefore. I.e.,

CronExpression cron = new CronExpression("0 15 10 ? * *");
Date today = new Date();
Date previousExecution = cron.getTimeBefore(today);

It might depend on the Quartz version whether this works.

Looking at the latest source (version 2.3.0 at time of writing) this method has not been implemented and always returns null.

往日 2024-09-06 09:19:01
import org.springframework.scheduling.support.CronSequenceGenerator;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

public class DateCalendarUtil {

    public static Date lastRunOn(String cronExpression) {
        final Date nextExecution = fromLocalDateTime(toLocalDate(nextCronDate(cronExpression)).atTime(23, 59, 59));
        return subtract(nextExecution, numberOfDays(nextExecution, nextCronDate(cronExpression, nextExecution)).intValue());
    }

    /**
     * Converts {@link Date} to {@link LocalDate} with default system {@link ZoneId}
     *
     * @param date to be converted to {@link LocalDate}
     * @return converted {@link Date}
     */
    public static LocalDate toLocalDate(Date date) {
        return toLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Converts {@link Date} to {@link LocalDate} with provided {@link ZoneId}
     * @param date to be converted to {@link LocalDate}
     * @param zoneId with which {@link Date} will be converted
     * @return converted {@link Date}
     */
    public static LocalDate toLocalDate(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDate();
    }

    /**
     * Converts {@link Date} to {@link LocalDateTime} with provided {@link ZoneId}
     * @param date to be converted to {@link LocalDateTime} with provided {@link ZoneId}
     * @param zoneId with which {@link Date} will be converted
     * @return converted {@link Date}
     */
    public static LocalDateTime toLocalDateTime(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDateTime();
    }

    /**
     * Converts {@link Date} to {@link LocalDateTime} with system default {@link ZoneId}
     *
     * @param date to be converted to {@link LocalDateTime}
     * @return converted {@link Date}
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return toLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDate} to {@link Date} with default system {@link ZoneId}
     * @param localDate to be converted to {@link Date}
     * @return converted {@link LocalDate}
     */
    public static Date fromLocalDate(LocalDate localDate) {
        return fromLocalDate(localDate, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDate} to {@link Date} with provided {@link ZoneId}
     * @param localDate to be converted to {@link Date}
     * @param zoneId with which {@link LocalDate} converted
     * @return converted {@link LocalDate}
     */
    public static Date fromLocalDate(LocalDate localDate, ZoneId zoneId) {
        return Date.from(localDate.atStartOfDay(zoneId).toInstant());
    }

    /**
     * Converts {@link LocalDateTime} to {@link Date} with default system {@link ZoneId}
     *
     * @param localDateTime to be converted to {@link Date}
     * @return converted {@link LocalDateTime}
     */
    public static Date fromLocalDateTime(LocalDateTime localDateTime) {
        return fromLocalDateTime(localDateTime, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDateTime} to {@link Date} with provided {@link ZoneId}
     *
     * @param localDateTime to be converted to {@link Date}
     * @param zoneId        with which localDateTime converted to {@link Date}
     * @return converted {@link Date}
     */
    private static Date fromLocalDateTime(LocalDateTime localDateTime, ZoneId zoneId) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }

    public static Date yesterday() {
        return yesterday(TimeZone.getDefault());
    }

    public static Date yesterday(TimeZone timezone) {
        return subtract(new Date(), 1, timezone);
    }

    /**
     * Generates start time of give date with system default {@link TimeZone}
     * @param date Date of which start time to be generated
     * @return Date with start time as 00:00:00
     */
    public static Date startTime(Date date) {
        return startTime(date, TimeZone.getDefault());
    }

    /**
     * Generates start time of give date with provided {@link TimeZone}
     * @param date Date of which start time to be generated
     * @param timeZone with which {@link Calendar} created
     * @return Date with start time as 00:00:00
     */
    public static Date startTime(Date date, TimeZone timeZone) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    /**
     * Generates end time of give date with system default {@link TimeZone}
     * @param date Date of which end time to be generated
     * @return Date with end time as 23:59:59
     */
    public static Date endTime(Date date) {
        return endTime(date, TimeZone.getDefault());
    }

    /**
     * Generates end time of give date with provided {@link TimeZone}
     * @param date Date of which end time to be generated
     * @param timeZone with which {@link Calendar} created
     * @return Date with end time as 23:59:59
     */
    public static Date endTime(Date date, TimeZone timeZone) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

    /**
     * Calculates number of days between from and to
     * @param from start Date
     * @param to end date
     * @return number of days including last date
     */
    public static Long numberOfDays(Date from, Date to) {
        return TimeUnit.DAYS.convert(to.getTime() - from.getTime(), TimeUnit.MILLISECONDS) + 1;
    }

    /**
     * Gives next {@link Date} from given cron expression
     * @param cronExpression cron expression
     * @return next {@link Date}
     */
    public static Date nextCronDate(String cronExpression) {
        return nextCronDate(cronExpression, new Date());
    }

    public static Date nextCronDate(String cronExpression, Date date) {
        CronSequenceGenerator generator = new CronSequenceGenerator(cronExpression, TimeZone.getTimeZone("IST"));
        return DateCalendarUtil.fromLocalDate(DateCalendarUtil.toLocalDate(generator.next(date)));
    }
}
import org.springframework.scheduling.support.CronSequenceGenerator;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

public class DateCalendarUtil {

    public static Date lastRunOn(String cronExpression) {
        final Date nextExecution = fromLocalDateTime(toLocalDate(nextCronDate(cronExpression)).atTime(23, 59, 59));
        return subtract(nextExecution, numberOfDays(nextExecution, nextCronDate(cronExpression, nextExecution)).intValue());
    }

    /**
     * Converts {@link Date} to {@link LocalDate} with default system {@link ZoneId}
     *
     * @param date to be converted to {@link LocalDate}
     * @return converted {@link Date}
     */
    public static LocalDate toLocalDate(Date date) {
        return toLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Converts {@link Date} to {@link LocalDate} with provided {@link ZoneId}
     * @param date to be converted to {@link LocalDate}
     * @param zoneId with which {@link Date} will be converted
     * @return converted {@link Date}
     */
    public static LocalDate toLocalDate(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDate();
    }

    /**
     * Converts {@link Date} to {@link LocalDateTime} with provided {@link ZoneId}
     * @param date to be converted to {@link LocalDateTime} with provided {@link ZoneId}
     * @param zoneId with which {@link Date} will be converted
     * @return converted {@link Date}
     */
    public static LocalDateTime toLocalDateTime(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDateTime();
    }

    /**
     * Converts {@link Date} to {@link LocalDateTime} with system default {@link ZoneId}
     *
     * @param date to be converted to {@link LocalDateTime}
     * @return converted {@link Date}
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return toLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDate} to {@link Date} with default system {@link ZoneId}
     * @param localDate to be converted to {@link Date}
     * @return converted {@link LocalDate}
     */
    public static Date fromLocalDate(LocalDate localDate) {
        return fromLocalDate(localDate, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDate} to {@link Date} with provided {@link ZoneId}
     * @param localDate to be converted to {@link Date}
     * @param zoneId with which {@link LocalDate} converted
     * @return converted {@link LocalDate}
     */
    public static Date fromLocalDate(LocalDate localDate, ZoneId zoneId) {
        return Date.from(localDate.atStartOfDay(zoneId).toInstant());
    }

    /**
     * Converts {@link LocalDateTime} to {@link Date} with default system {@link ZoneId}
     *
     * @param localDateTime to be converted to {@link Date}
     * @return converted {@link LocalDateTime}
     */
    public static Date fromLocalDateTime(LocalDateTime localDateTime) {
        return fromLocalDateTime(localDateTime, ZoneId.systemDefault());
    }

    /**
     * Converts {@link LocalDateTime} to {@link Date} with provided {@link ZoneId}
     *
     * @param localDateTime to be converted to {@link Date}
     * @param zoneId        with which localDateTime converted to {@link Date}
     * @return converted {@link Date}
     */
    private static Date fromLocalDateTime(LocalDateTime localDateTime, ZoneId zoneId) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }

    public static Date yesterday() {
        return yesterday(TimeZone.getDefault());
    }

    public static Date yesterday(TimeZone timezone) {
        return subtract(new Date(), 1, timezone);
    }

    /**
     * Generates start time of give date with system default {@link TimeZone}
     * @param date Date of which start time to be generated
     * @return Date with start time as 00:00:00
     */
    public static Date startTime(Date date) {
        return startTime(date, TimeZone.getDefault());
    }

    /**
     * Generates start time of give date with provided {@link TimeZone}
     * @param date Date of which start time to be generated
     * @param timeZone with which {@link Calendar} created
     * @return Date with start time as 00:00:00
     */
    public static Date startTime(Date date, TimeZone timeZone) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    /**
     * Generates end time of give date with system default {@link TimeZone}
     * @param date Date of which end time to be generated
     * @return Date with end time as 23:59:59
     */
    public static Date endTime(Date date) {
        return endTime(date, TimeZone.getDefault());
    }

    /**
     * Generates end time of give date with provided {@link TimeZone}
     * @param date Date of which end time to be generated
     * @param timeZone with which {@link Calendar} created
     * @return Date with end time as 23:59:59
     */
    public static Date endTime(Date date, TimeZone timeZone) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

    /**
     * Calculates number of days between from and to
     * @param from start Date
     * @param to end date
     * @return number of days including last date
     */
    public static Long numberOfDays(Date from, Date to) {
        return TimeUnit.DAYS.convert(to.getTime() - from.getTime(), TimeUnit.MILLISECONDS) + 1;
    }

    /**
     * Gives next {@link Date} from given cron expression
     * @param cronExpression cron expression
     * @return next {@link Date}
     */
    public static Date nextCronDate(String cronExpression) {
        return nextCronDate(cronExpression, new Date());
    }

    public static Date nextCronDate(String cronExpression, Date date) {
        CronSequenceGenerator generator = new CronSequenceGenerator(cronExpression, TimeZone.getTimeZone("IST"));
        return DateCalendarUtil.fromLocalDate(DateCalendarUtil.toLocalDate(generator.next(date)));
    }
}
童话里做英雄 2024-09-06 09:19:01

我在 Quartz 中发现的一种解决方案是返回触发器的一个时间间隔并计算下一次触发时间。通过迭代所有触发器,可以确定触发器在过去应该触发的最近时间。


计算每次触发之间的间隔:

Date nextFireTime = trigger.getNextFireTime();
Date subsequentFireTime = trigger.getFireTimeAfter(nextFireTime);
long interval = subsequentFireTime.getTime() - nextFireTime.getTime();

查找一次下一次触发时间,直到过去的间隔:

Date previousPeriodTime = new Date(System.currentTimeMillis() - interval);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);

我发现,如果您使用CronTrigger,这可以防止您询问过去的触发时间。为了解决这个问题,我修改了开始时间,因此上面的代码片段变成:

Date originalStartTime = trigger.getStartTime(); // save the start time
Date previousPeriodTime = new Date(originalStartTime.getTime() - interval);
trigger.setStartTime(previousPeriodTime);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);
trigger.setStartTime(originalStartTime); // reset the start time to be nice

迭代所有触发器并找到最近的触发器:

for (String groupName : scheduler.getTriggerGroupNames()) {
    for (String triggerName : scheduler.getTriggerNames(groupName)) {
        Trigger trigger = scheduler.getTrigger(triggerName, groupName);
        // code as detailed above...
        interval = ...
        previousFireTime = ...
    }
}

我将把它作为练习留给读者,将其重构为帮助程序方法或类。实际上,我在子类委托触发器中使用上述算法,然后将其放入按先前触发时间排序的集合中。

A solution I have found with Quartz is to go back one time interval for the trigger and calculate what would have been the next firing time. By iterating through all the triggers the most recent time that a trigger should have fired in the past can be determined.


Calculate the interval between each firing:

Date nextFireTime = trigger.getNextFireTime();
Date subsequentFireTime = trigger.getFireTimeAfter(nextFireTime);
long interval = subsequentFireTime.getTime() - nextFireTime.getTime();

Find the next firing time for one time until interval in the past:

Date previousPeriodTime = new Date(System.currentTimeMillis() - interval);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);

I have found that if you are using a CronTrigger this prevents you asking for a fire time in the past. To work around this I modify the start time, so the above snippet becomes:

Date originalStartTime = trigger.getStartTime(); // save the start time
Date previousPeriodTime = new Date(originalStartTime.getTime() - interval);
trigger.setStartTime(previousPeriodTime);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);
trigger.setStartTime(originalStartTime); // reset the start time to be nice

Iterate through all of the triggers and find the one that is most recently in the past:

for (String groupName : scheduler.getTriggerGroupNames()) {
    for (String triggerName : scheduler.getTriggerNames(groupName)) {
        Trigger trigger = scheduler.getTrigger(triggerName, groupName);
        // code as detailed above...
        interval = ...
        previousFireTime = ...
    }
}

I'll leave it as an exercise to the reader to refactor this into helper methods or classes. I actually use the above algorithm in a subclassed delegating trigger that I then place in a set sorted by previous firing times.

白况 2024-09-06 09:19:01

如果您使用 org.quartz,可以使用 context.getPreviousFireTime();

示例:

public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        context.getPreviousFireTime(); 
    }
}

如果您使用 context.getTrigger().getPreviousFireTime(),您将具有当前正在运行的作业的触发时间。

In case you are using org.quartz, is possible to use context.getPreviousFireTime();

Example:

public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        context.getPreviousFireTime(); 
    }
}

If you are using context.getTrigger().getPreviousFireTime(), you will have the triggered time of the Job that is running at the moment.

挽梦忆笙歌 2024-09-06 09:19:01

令人惊讶的是,仍然没有基于 CronExpression 获取上次触发时间的石英方法...

如何获取上次触发时间?

如果您正在操作基本 CRON,如 0 0 0 * * ?(每天上午 00:00:00)您可以使用 João Neves 的解决方案(使用 com.cronutils.model.time.ExecutionTime)。

否则,如果您正在操作像 0 30 11 这样的复杂 CRON ? * 周一、周四 * 它不会起作用。你会得到随机结果(我有一个星期三的结果......)。

编辑:我做了其他测试,最新版本看起来效果更好(之前的测试是使用 <3.1.6 版本进行的)。注意:如果您想使用版本 > 则需要 Java 8 3.1.6。

您可以使用的解决方案是在触发作业时存储它。


如何验证作业是否已被触发?

我找到的解决方案是使用 Quartz (CronExpression) 中的 getNextValidTimeAfter 。这个效果很好。
当我们寻找之前的有效时间时,您会问我在说什么!你说得对,给我一秒钟!

假设我们有一个每月一次的 CRON(0 0 16 1 * ? = 每月第一天下午 16:00:00),我们希望每天检查之前的执行是否有效。
您必须在每次执行时存储 getNextValidTime 并将该日期与今天的日期进行比较。例如(格式DD/MM/YYYY):

• 01/01/2019 → 作业触发,我们存储下一次触发时间(我们称之为nextFireTime):

CronExpression trigger = new CronExpression("0 0 16 1 * ?");
Date nextFireTime = trigger.getNextValidTimeAfter(new Date());
// = 01/02/2019

• 02/01/2019 → 当天的验证:2019年2月1日< 01/02/2019 好的

...

• 01/02/2019 → 让我们假设服务器已关闭,作业未触发。

• 02/02/2019 → 服务器开启,当天验证:02/02/2019 > 2019/01/02 KO!

→ 我们知道之前的火灾时间没有起作用。您可以知道做您想做的事情(触发作业并存储新的 nextFireTime)。


您可能感兴趣的另一个选项,请参阅MISFIRE_INSTRUCTION_FIRE_NOW

调度程序发现misfire后立即执行作业
情况。这是明智的政策。示例场景:您有
安排在凌晨 2 点进行一些系统清理。不幸的是应用程序
当时由于维护而停机,并于凌晨 3 点恢复。所以
触发器失火,调度程序尝试通过以下方式挽救情况
尽快运行它 - 凌晨 3 点。

来自(https://dzone.com/articles/quartz-scheduler-misfire

例如:

Trigger trigger = newTrigger().
 withSchedule(
  cronSchedule("0 0 9-17 ? * MON-FRI").
   withMisfireHandlingInstructionFireAndProceed()
 ).
 build();

官方文档: https://www.quartz -scheduler.org/api/2.1.7/org/quartz/CronTrigger.html

It's amazing that there is still no quartz method for getting the previous fired time based on a CronExpression...

How to get last fired time ?

If you are manipulating basic CRON like 0 0 0 * * ? (At 00:00:00am every day) you can use João Neves' solution (using com.cronutils.model.time.ExecutionTime).

Otherwise if you are manipulating complex CRON like 0 30 11 ? * MON,THU * it will not works. You'll get random results (I had a Wednesday for this one...).

Edit: I did other tests and it looks working better with the latest version (previous tests were made with version < 3.1.6). Note: You need Java 8 if you want to use version > 3.1.6.

The solution you can use is to store it when your job is triggered.


How to verify that the job has been triggered ?

The solution I found is to use getNextValidTimeAfter from Quartz (CronExpression). This one works fine.
You'll ask me what I'm talking about as we are looking for the previous valid time ! You are right, give me one second !

Let's imagine we have a once a month CRON (0 0 16 1 * ? = At 16:00:00pm, on the 1st day, every month) and we want to check everyday that the previous execution worked.
You'll have to store the getNextValidTime at each execution and compare this date with today's date. eg (format DD/MM/YYYY):

• 01/01/2019 → job triggered, we store next fire time (let's call it nextFireTime):

CronExpression trigger = new CronExpression("0 0 16 1 * ?");
Date nextFireTime = trigger.getNextValidTimeAfter(new Date());
// = 01/02/2019

• 02/01/2019 → verification of the day: 02/01/2019 < 01/02/2019 OK

...

• 01/02/2019 → let's imagine server is down, the job is not triggered.

• 02/02/2019 → server on, verification of the day: 02/02/2019 > 01/02/2019 KO !

→ We know the previous fire time hasn't worked. You can know do what you want (trigger the job and store the new nextFireTime).


Another option that may interest you, see MISFIRE_INSTRUCTION_FIRE_NOW.

The job is executed immediately after the scheduler discovers misfire
situation. This is the smart policy. Example scenario: you have
scheduled some system clean up at 2 AM. Unfortunately the application
was down due to maintenance by that time and brought back on 3 AM. So
the trigger misfired and the scheduler tries to save the situation by
running it as soon as it can - at 3 AM.

From (https://dzone.com/articles/quartz-scheduler-misfire)

e.g.:

Trigger trigger = newTrigger().
 withSchedule(
  cronSchedule("0 0 9-17 ? * MON-FRI").
   withMisfireHandlingInstructionFireAndProceed()
 ).
 build();

Official doc: https://www.quartz-scheduler.org/api/2.1.7/org/quartz/CronTrigger.html

拥抱影子 2024-09-06 09:19:01

我正在使用 cron-utils 9.1.6 来获取基于“现在”的最后执行时间以及“cron 表达式”,以及预期结束时间加上预期持续时间(以秒为单位)。

代码的语言是Scala。

import java.time.ZonedDateTime
import com.cronutils.model.definition.CronDefinitionBuilder
import com.cronutils.model.time.ExecutionTime
import com.cronutils.parser.CronParser
import com.cronutils.model.CronType.QUARTZ
import java.time.ZoneOffset.UTC

val parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(QUARTZ))
val cronExpression = "0 0 23 ? * *"
val now = ZonedDateTime.now(UTC)
val expectedDurationSec = 1500

println(s"now: $now")
val executionTime = ExecutionTime.forCron(parser.parse(cronExpression))
val lastExecution = executionTime.lastExecution(now)
println(s"lastExecution: ${lastExecution.get()}")
val expectedEnd = lastExecution.get().plusSeconds(expectedDurationSec)
println(s"expectedEnd: $expectedEnd")

输出是:

now: 2022-04-21T01:08:27.499Z
lastExecution: 2022-04-20T23:00Z
expectedEnd: 2022-04-20T23:25Z

I am using cron-utils 9.1.6 to get the last execution time based on 'now' and the 'cron expression', and the expected end time adding the expected duration in seconds.

The code's language is Scala.

import java.time.ZonedDateTime
import com.cronutils.model.definition.CronDefinitionBuilder
import com.cronutils.model.time.ExecutionTime
import com.cronutils.parser.CronParser
import com.cronutils.model.CronType.QUARTZ
import java.time.ZoneOffset.UTC

val parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(QUARTZ))
val cronExpression = "0 0 23 ? * *"
val now = ZonedDateTime.now(UTC)
val expectedDurationSec = 1500

println(s"now: $now")
val executionTime = ExecutionTime.forCron(parser.parse(cronExpression))
val lastExecution = executionTime.lastExecution(now)
println(s"lastExecution: ${lastExecution.get()}")
val expectedEnd = lastExecution.get().plusSeconds(expectedDurationSec)
println(s"expectedEnd: $expectedEnd")

the output is:

now: 2022-04-21T01:08:27.499Z
lastExecution: 2022-04-20T23:00Z
expectedEnd: 2022-04-20T23:25Z
风铃鹿 2024-09-06 09:19:01

有Spring的 < code>org.springframework.scheduling.support.CronSequenceGenerator 计算相对于任意时间的 cron 执行的 next() 时间。假设“最后触发时间”是指满足next(t) ≥ now的最早时间t,可以通过简单的迭代算法找到:

  1. 从给定时间开始,往回走,找到某个时刻 t,其中 next(t) t now(意味着t属于之前的某个区间)。这可以通过例如步长加倍、采用任意初始步长来完成。假设许多现实生活中的 cron 表达式产生大致相等的间隔,则初始步长的合理猜测是 next(now) – now + εnext(next(now)) – next( now) + ε 当执行之间预计有 now 时,这是根据合约 > 0
  2. t开始,向前迭代t := next(t)直到next(t) ≥ now

static long previous(String cron, TimeZone timezone, long ms) {
    var generator = new CronSequenceGenerator(cron, timezone);

    // 1. Walk back to find some moment where t.next() < ms
    long t1 = generator.next(new Date(ms)).getTime() - ms;  // initial step
    long t;
    do {
        if (t1 <= 0) {      // overflow or negative initial step
            throw new IllegalArgumentException("Failed to find previous time");
        }
        t = ms - t1 - 1;    // next try for t
        t1 *= 2;            // double step size
    } while (generator.next(new Date(t)).getTime() >= ms);

    // 2. Walk forward through next().next()... chain to find
    // the earliest moment where t1.next() >= ms
    while (t < ms) {
        t1 = t;
        t = generator.next(new Date(t)).getTime();
    }
    return t1;
}

CronSequenceGenerator 可以替换为 CronExpression 或任何具有类似于 next() 函数的内容 - 代码只是演示了仅使用查找“上一个”的方法“下一个”。

There is Spring's org.springframework.scheduling.support.CronSequenceGenerator which computes next() time of cron execution relative to arbitrary time. Assuming that "last fired time" means the earliest time t for which holds next(t) ≥ now, it can be found by simple iteration algorithm:

  1. Starting from given time, walk back to find some moment t where next(t) < now (meaning that t belongs to some previous interval). This can be done by e.g. step doubling, taking some arbitrary initial step size. Assuming that many real-life cron expressions produce roughly equal intervals, reasonable guess for initial step size is next(now) – now + ε, or next(next(now)) – next(now) + ε when now is expected between executions, which is by contract > 0;
  2. Starting from t, walk forward iterating t := next(t) until next(t) ≥ now.

static long previous(String cron, TimeZone timezone, long ms) {
    var generator = new CronSequenceGenerator(cron, timezone);

    // 1. Walk back to find some moment where t.next() < ms
    long t1 = generator.next(new Date(ms)).getTime() - ms;  // initial step
    long t;
    do {
        if (t1 <= 0) {      // overflow or negative initial step
            throw new IllegalArgumentException("Failed to find previous time");
        }
        t = ms - t1 - 1;    // next try for t
        t1 *= 2;            // double step size
    } while (generator.next(new Date(t)).getTime() >= ms);

    // 2. Walk forward through next().next()... chain to find
    // the earliest moment where t1.next() >= ms
    while (t < ms) {
        t1 = t;
        t = generator.next(new Date(t)).getTime();
    }
    return t1;
}

The CronSequenceGenerator can be replaced by CronExpression or whatever having similar to next() function—the code just demonstrates approach to find "previous" using only "next".

行雁书 2024-09-06 09:19:01
public class CronMircoUtils {

    /**
     * Use this method to calculate previous valid date for cron
     * @param ce CronExpression object with cron expression
     * @param date Date for find previous valid date
     * @return
     */
    public static Date getPreviousValidDate(CronExpression ce, Date date) {
        try {
            Date nextValidTime = ce.getNextValidTimeAfter(date);
            Date subsequentNextValidTime = ce.getNextValidTimeAfter(nextValidTime);
            long interval = subsequentNextValidTime.getTime() - nextValidTime.getTime();
            return new Date(nextValidTime.getTime() - interval);
        } catch (Exception e) {
            throw new IllegalArgumentException("Unsupported cron or date", e);
        }
    }
}

源代码位于 https://github.com/devbhuwan/cron-micro-utils

public class CronMircoUtils {

    /**
     * Use this method to calculate previous valid date for cron
     * @param ce CronExpression object with cron expression
     * @param date Date for find previous valid date
     * @return
     */
    public static Date getPreviousValidDate(CronExpression ce, Date date) {
        try {
            Date nextValidTime = ce.getNextValidTimeAfter(date);
            Date subsequentNextValidTime = ce.getNextValidTimeAfter(nextValidTime);
            long interval = subsequentNextValidTime.getTime() - nextValidTime.getTime();
            return new Date(nextValidTime.getTime() - interval);
        } catch (Exception e) {
            throw new IllegalArgumentException("Unsupported cron or date", e);
        }
    }
}

Source Code in https://github.com/devbhuwan/cron-micro-utils

哥,最终变帅啦 2024-09-06 09:19:01

它的工作解决方案

   pom :
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

   javaCode:
        //Provide your format of date  want
        String format="dd-MMM-YYYY hh:mm:ss";
        //Provide your cron expression you want
        String cronExpression="* * 12 ? * FRI *";
        SimpleDateFormat sdf=new SimpleDateFormat(format);
        CronExpression expression = new CronExpression(cronExpression);
        Date  currentDate=new Date();
        Date nextDate=expression.getNextValidTimeAfter(currentDate);
        long interval = nextDate.getTime()-currentDate.getTime();
        Date previousDate= new Date(currentDate.getTime() - interval);
        System.out.Println(sdf.format(previousDate));

在这里我得到了当前时间和下一次火灾时间之间的差异
并排除当前时间的差异时间,以便我们得到最后一次触发的时间

Its working solution

   pom :
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

   javaCode:
        //Provide your format of date  want
        String format="dd-MMM-YYYY hh:mm:ss";
        //Provide your cron expression you want
        String cronExpression="* * 12 ? * FRI *";
        SimpleDateFormat sdf=new SimpleDateFormat(format);
        CronExpression expression = new CronExpression(cronExpression);
        Date  currentDate=new Date();
        Date nextDate=expression.getNextValidTimeAfter(currentDate);
        long interval = nextDate.getTime()-currentDate.getTime();
        Date previousDate= new Date(currentDate.getTime() - interval);
        System.out.Println(sdf.format(previousDate));

Here im getting the difference between the current time and next fire time
and exluding this difference time from current time so that we will get last fired time

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文