代码之家  ›  专栏  ›  技术社区  ›  Dónal

重复事件逻辑

  •  12
  • Dónal  · 技术社区  · 13 年前

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

    • 在与开始日期相对应的月份中的某个日期每月
    • 每周,在与开始日期相对应的一周中的某一天
    • 每两周的某一天对应于开始日期
    • 等。

    我最初计划使用Google calendar 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 回复  |  直到 13 年前
        1
  •  8
  •   GaryF    13 年前

    您想要的递归规则的种类在RFC-2445(基本上是iCal规范)中有很好的规定。正确理解这一点可能会很复杂。我建议使用 google-rfc-2445 用于此的库,或该规范的另一个实现,如 iCal4J .

        2
  •  2
  •   mdrg    13 年前

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

    我知道这对你来说可能有点过分了,甚至可能不适用,但是 Quartz Scheduler 很好地处理所有这些重复和事件相关的规则。您不能使用它的调度功能,而只能使用触发器类(如 CronTrigger )为您计算活动日期。

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

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

    夏令时问题也得到了解决。

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

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

    希望这能有用。

        3
  •  0
  •   Max    10 年前

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

    你需要的是像南丫岛这样的专业日程生成库( 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());