使用Quartz每30天进行一次月度工作

时间:2012-07-11 22:11:20

标签: java triggers quartz-scheduler jobs

人,   

我有用户安排的月度工作(使用Quartz)。用户提供开始日期f 或者第一份工作,可能是1-31个月的任何一天

我的问题是如何使用cron触发器安排此操作,记住并非所有月份都有31,30,29天。 在这种情况下,作业应该在最接近的前一天运行。 所以,让我们说四月只有30天,所以工作必须在4月30日运行。

可以使用单个cron触发器完成吗?或者它应该是触发器的组合? 我尝试使用CronExpression来了解它如何处理这种情况:

CronExpression ce = new CronExpression("0 0 0 30 JAN-DEC ? *");
Date nextValidTime = ce.getNextValidTimeAfter(//**27th of February**//);

我的nextValidTime等于 3月30日,所以cron只是“跳过”2月。 任何帮助将受到高度赞赏。提前谢谢。

4 个答案:

答案 0 :(得分:6)

  

“L”字符允许用于日期和星期几字段。该字符>是“最后”的简称,但它在两个领域中都有不同的含义。对于   例如,日期字段中的值“L”表示“最后一天   本月“ - 1月31日,2月28日非飞跃   年份。如果在周日字段中单独使用,则仅表示“7”   或“SAT”。但如果在星期几字段中使用了另一个值,那么   表示“该月的最后一个xxx日” - 例如“6L”表示“   这个月的最后一个星期五“。你也可以指定一个偏移量   这个月的最后一天,例如“L-3”,这意味着   日历月的倒数第三天。当使用'L'选项时,它   重要的是不要像你那样指定列表或值范围   令人困惑/意外的结果。

     

http://quartz-scheduler.org/api/2.0.0/org/quartz/CronExpression.html

new CronExpression("0 0 0 L JAN-DEC ? *");

修改

我会做这样的事情

Calendar tCalendar = Calendar.getInstance();
tCalendar.set(2009, Calendar.FEBRUARY/*int*/, 1); // for example Feb, 2009 -- day doesn't matter here
if(userSelectedDay > tCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) ){
    //Fix user day
    //fixedDay = tCalendar.getActualMaximum(Calendar.DAY_OF_MONTH)

    // Or, for that month 
    //new CronExpression("0 0 0 L JAN-DEC ? *");
}

答案 1 :(得分:1)

这是我的解决方案。这个想法是试图构建一组cron表达式,这些表达式将被传递给触发器生成器。

  • 如果输入日期<28。请使用一个表达式:
  • 如果输入日= 29或=30。请使用2个表达式:2月的最后一天以及其他月份的特定日期。
  • 如果输入日期=31。请使用该月的最后一天。

    public static Set<String> byDay(int day) {
        if (day < 1 || day > 31) {
            throw new IllegalArgumentException("The input day must be in range: 1 <= day <= 31");
        }
        if (day <= 28) {
            return Collections.singleton(String.format("0 0 0 %d JAN-DEC ? *", day));
        }
        if (day == 29 || day == 30) {
            Set<String> expressions = new HashSet<String>();
            expressions.add("0 0 0 L FEB ? *");
            expressions.add(String.format("0 0 0 %d JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC ? *", day));
            return Collections.unmodifiableSet(expressions);
        }
        // day == 31
        return Collections.singleton("0 0 0 L * ? *");
    }
    

答案 2 :(得分:0)

尝试使用此代码段,它会根据日期创建1到3个触发器(涵盖整整一年):

        Set<Trigger> triggers = new HashSet<>(3);

        CronScheduleBuilder interval = CronScheduleBuilder.monthlyOnDayAndHourAndMinute(dayNumber, 0, 0);

        if (dayNumber > 28) {
            CronTrigger trigger28 = TriggerBuilder.newTrigger()
                    .withIdentity("payment_trigger28_" + merchantServiceTemplate.getId(), "payment_triggers")
                    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 28 2 ? *"))
                    .endAt(merchantServiceTemplate.getEndScheduleDate())
                    .build();
            triggers.add(trigger28);

            if (dayNumber == 31) {
                CronTrigger trigger30 = TriggerBuilder.newTrigger()
                        .withIdentity("payment_trigger30_" + merchantServiceTemplate.getId(), "payment_triggers")
                        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 30 4,6,9,11 ? *"))
                        .endAt(merchantServiceTemplate.getEndScheduleDate())
                        .build();
                triggers.add(trigger30);
            }
        }

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("payment_triggerAll_" + merchantServiceTemplate.getId(), "payment_triggers")
                .withSchedule(interval)
                .endAt(merchantServiceTemplate.getEndScheduleDate())
                .build();
        triggers.add(trigger);


        scheduler.scheduleJob(job, triggers, false);

答案 3 :(得分:0)

如前所述,您必须为每个月的日期创建多个CronExpressions,并为每个案例创建一个触发器,然后将所有触发器添加到您所需的作业中。

这是我的版本:

CronExpressions创作:

public static List<CronExpression> getCronExpressionList(int seconds, int minutes,
            int hours, int dayInMonth, Month month,
            DayOfWeek dayOfWeek) {
    final String monthsWith30Days = Month.APR + "," + Month.JUN + ","
                    + Month.SEP + "," + Month.NOV;
    List<CronExpression> crons = new LinkedList<CronExpression>();

    String timeString = String.format(("%s %s %s "), seconds, minutes,
                    hours, 0, 0, 0);
    String dateString = "%s %s %s";
    String cron = null;

    cron = timeString + String.format(dateString, dayInMonth, "*", "?");
    crons.add(new CronExpression(cron));
    if (dayInMonth > 28) {
        String febCron = timeString + getFebruarLastDayDateString(dateString);
        crons.add(new CronExpression(febCron));
        if (dayInMonth == 31) {
            String monthsWithThirtyDaysCron = timeString + String.format(dateString,
                    "L", monthsWith30Days, "?");
            crons.add(new CronExpression(monthsWithThirtyDaysCron));
        }
    }
    return crons;
}

private static String getFebruarLastDayDateString(String initialCron) 
               throws ParseException {
    return String.format(initialCron, "L", Month.FEB, "?");
}

注意我使用&#34; L&#34;在2月份的cron中,因为否则你会在闰年中出现错误。

触发器创建:

        Set<CronTrigger> triggers = new HashSet<>();

        int i = 1;
        for (CronExpression cronEx : cronsList) {
            CronTrigger trigger = newTrigger()
                    .withIdentity("trigger" + i, groupName)
                    .withSchedule(cronSchedule(cronEx))
                    .build();
                triggers.add(trigger);
                i++;
        }