使用 Java 中的 Cron 表达式查找上次触发时间
Java 有没有办法从 Cron 表达式中找到“上次触发时间”?
例如,如果 now = 25-Apr-2010 10PM
,并且 cron 表达式为 0 15 10 ? * *
(quartz),它应该返回 25-Apr-2010 10:15AM
。
注意:
- 我不在乎我们是否使用标准 cron 表达式(如 Unix 和 Quartz)或不太流行的表达式,如果它们可以为我获取正确的“上次触发时间”
- 而且它也不是字面上的“上次触发时间”,因为触发器可能没有发射,但从逻辑上讲,应该有一种方法来判断它最后一次发射的时间。
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:
- 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"
- 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
cron-utils 是一个开源 Java 库,用于解析、验证、迁移 cron,支持您所需的操作。
要简单地从给定时间之前的 cron 获取上一个日期:
请记住,在当前状态下,它有点错误,并且可能无法正确计算更复杂的 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:
Bear in mind that in its current state it is a bit buggy and may not compute correctly for more complex cron expressions.
首先,我不知道现有的库支持此功能。 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 fororiginally 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.Quartz 似乎有一些对 cron 表达式的库支持。
请参阅CronExpression 类的 Javadoc,它有一个方法称为
getTimeBefore
。即,这是否有效可能取决于 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.,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.
我在 Quartz 中发现的一种解决方案是返回触发器的一个时间间隔并计算下一次触发时间。通过迭代所有触发器,可以确定触发器在过去应该触发的最近时间。
计算每次触发之间的间隔:
查找一次下一次触发时间,直到过去的间隔:
我发现,如果您使用
CronTrigger
,这可以防止您询问过去的触发时间。为了解决这个问题,我修改了开始时间,因此上面的代码片段变成:迭代所有触发器并找到最近的触发器:
我将把它作为练习留给读者,将其重构为帮助程序方法或类。实际上,我在子类委托触发器中使用上述算法,然后将其放入按先前触发时间排序的集合中。
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:
Find the next firing time for one time until interval in the past:
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:Iterate through all of the triggers and find the one that is most recently in the past:
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.
如果您使用 org.quartz,可以使用
context.getPreviousFireTime();
示例:
如果您使用
context.getTrigger().getPreviousFireTime()
,您将具有当前正在运行的作业的触发时间。In case you are using org.quartz, is possible to use
context.getPreviousFireTime();
Example:
If you are using
context.getTrigger().getPreviousFireTime()
, you will have the triggered time of the Job that is running at the moment.令人惊讶的是,仍然没有基于 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
):• 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。
来自(https://dzone.com/articles/quartz-scheduler-misfire)
例如:
官方文档: 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
):• 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.
From (https://dzone.com/articles/quartz-scheduler-misfire)
e.g.:
Official doc: https://www.quartz-scheduler.org/api/2.1.7/org/quartz/CronTrigger.html
我正在使用 cron-utils 9.1.6 来获取基于“现在”的最后执行时间以及“cron 表达式”,以及预期结束时间加上预期持续时间(以秒为单位)。
代码的语言是Scala。
输出是:
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.
the output is:
有Spring的 < code>org.springframework.scheduling.support.CronSequenceGenerator 计算相对于任意时间的 cron 执行的
next()
时间。假设“最后触发时间”是指满足next(t) ≥ now的最早时间t,可以通过简单的迭代算法找到:CronSequenceGenerator
可以替换为CronExpression
或任何具有类似于next()
函数的内容 - 代码只是演示了仅使用查找“上一个”的方法“下一个”。There is Spring's
org.springframework.scheduling.support.CronSequenceGenerator
which computesnext()
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:The
CronSequenceGenerator
can be replaced byCronExpression
or whatever having similar tonext()
function—the code just demonstrates approach to find "previous" using only "next".源代码位于 https://github.com/devbhuwan/cron-micro-utils
Source Code in https://github.com/devbhuwan/cron-micro-utils
它的工作解决方案
在这里我得到了当前时间和下一次火灾时间之间的差异
并排除当前时间的差异时间,以便我们得到最后一次触发的时间
Its working solution
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