【问题标题】:Monthly jobs on every 30th day using Quartz使用 Quartz 每 30 天的月度作业
【发布时间】:2012-07-11 22:11:19
【问题描述】:

伙计们,

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

我的问题是如何使用 cron 触发器来安排这个,请记住并非所有月份都有第 31、30、29 天。 在这种情况下,作业应该在本月最接近的前一天运行。 因此,假设 4 月只有 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 月。 任何帮助将不胜感激。提前致谢。

【问题讨论】:

  • 嘿,你解决了这个问题吗?如果是,你是怎么做的?

标签: java triggers quartz-scheduler jobs


【解决方案1】:

“L”字符允许用于月份和星期几字段。这个字符 > 是“last”的简写,但它在两个字段中的每一个都有不同的含义。为了 例如,日期字段中的值“L”表示“最后一天 本月” - 一月的第 31 天,非闰月的二月的第 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 ? *");
}

【讨论】:

  • 使用 L(或任何变体 L-x)对我的情况没有帮助。用户可以指定第一次运行作业的任何开始日期。必须使用从此开始日期起的月份日期来每月运行作业。因此,它可以是 1-31 之间的任何值。问题在于 29-31,因为并非所有月份都有。因此,如果用户选择 1 月 30 日,则作业必须在 2 月 29 日、3 月 30 日、4 月 30 日等运行。使用 L-3 将在 2 月 25 日(非闰年)、3 月 28 日触发触发器——但这不是我需要的。作业必须在指定的日期或上一个最接近的日期运行。
【解决方案2】:

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

  • 如果输入日期
  • 如果输入 day = 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 * ? *");
    }
    

【讨论】:

    【解决方案3】:

    试试这个 sn-p,它会根据日期创建 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);
    

    【讨论】:

      【解决方案4】:

      如前所述,您必须为每个月的天数创建多个 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, "?");
      }
      

      请注意,我在二月的 cron 中使用了“L”,否则在闰年会出现错误。

      触发器创建:

              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++;
              }
      

      【讨论】:

        猜你喜欢
        • 2013-05-23
        • 1970-01-01
        • 2014-08-14
        • 2013-01-20
        • 2015-05-11
        • 1970-01-01
        • 1970-01-01
        • 2013-03-02
        相关资源
        最近更新 更多