反复出现的事件逻辑

时间:2010-10-20 11:58:16

标签: java groovy calendar

我正在开发一个Groovy / Java日历类型的应用程序,允许用户输入具有开始日期和可选重复的事件。如果这是一个反复发生的事件,它可能会重现:

  • 每月与对应于开始日期的月份日期
  • 每周一周的某一天对应于开始日期
  • 每周两周,对应于开始日期

我最初计划使用Google日历API来执行所有重复逻辑,但事实证明它是一个巨大的PITA,因为如果有人关心我会进一步讨论。

所以现在,我决定推出自己的解决方案。给定一个日期,我想弄清楚在这个日期是否发生了定期事件。我的逻辑(伪代码)如下:

public boolean occursOnDate(def date, def event) {

  def firstDate = event.startDate

  if (firstDate > date) {
    return false;

  } else if (event.isWeekly()) {
    return event.dayOfWeek() == date.dayOfWeek()

  } else if (event.isMonthly()) {
    return event.dayOfMonth() == date.dayOfMonth()

  } else {
    // At this point we know the event occurs every X weeks where X > 1
    // Increment firstDate by adding X weeks to it as many times as possible, without
    // going past date
    return firstDate == date
  }  
}

这种逻辑似乎是合理的,但是当您考虑所有奇怪的边缘情况时(例如,如何处理2月份首次发生的1月31日的月度经常性事件),实际上需要付出相当大的努力。

是否有可以帮助我实施此功能的库?我们非常感谢一些具体细节(例如,“使用Joda时间”不会获得任何奖励)。

谢谢, 唐

3 个答案:

答案 0 :(得分:8)

您想要的那种重复规则在RFC-2445(基本上是iCal规范)中得到了相当明确的规定。获得这种正确的细节可能非常复杂。我建议您使用google-rfc-2445库或该规范的其他实现,例如iCal4J

答案 1 :(得分:2)

我对Groovy一无所知,我的第一个建议是Joda,但你知道。

我知道这对你来说似乎有些过分,甚至可能不适用,但Quartz Scheduler可以很好地处理所有这些重复和事件相关的规则。您无法使用其计划功能,只需使用Trigger类(如CronTrigger)为您计算事件日期。

上面的CronTrigger链接显示了一些可用于处理事件的表达式示例,例如这种特别恶劣的情况:

  

“0 0 12 L *?” - 在每个月的最后一天中午触发事件(没有闰年等的头痛)

也会处理夏令时问题。

对于代码,使用所需的重复创建触发器,然后您可以提取所需的所有触发时间:

Date firstFireTime = myTrigger.getNextFireTime();
...
while (...) {
    Date nextFireTime = myTrigger.getFireTimeAfter(previousFireTime);
    ...
}

希望这可能有用。

答案 2 :(得分:0)

我对Groovy库不是很熟悉,但是由于Groovy在JVM上运行,我认为你应该也可以使用Java / Scala库。

您需要的是专业的日程安排生成库,如Lamma(http://lamma.io),而不是像Joda这样的通用日期时间库。

    // monthly on a date of the month that corresponds to the start date
    // output: [Date(2014,6,10), Date(2014,7,10), Date(2014,8,10), Date(2014,9,10), Date(2014,10,10)]
    System.out.println(Dates.from(2014, 6, 10).to(2014, 10, 10).byMonth().build());

    // weekly on a day of the week of that corresponds to the start date
    // output: [Date(2014,6,10), Date(2014,6,17), Date(2014,6,24), Date(2014,7,1), Date(2014,7,8)]
    System.out.println(Dates.from(2014, 6, 10).to(2014, 7, 10).byWeek().build());

    // every 2 weeks on a day of the week of that corresponds to the start date
    // output: [Date(2014,6,10), Date(2014,6,24), Date(2014,7,8)]
    System.out.println(Dates.from(2014, 6, 10).to(2014, 7, 10).byWeeks(2).build());

    // edge cases are handled properly, for example, leap day
    // output: [Date(2012,2,29), Date(2013,2,28), Date(2014,2,28), Date(2015,2,28), Date(2016,2,29)]
    System.out.println(Dates.from(2012, 2, 29).to(2016, 2, 29).byYear().build());