我正在开发一个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时间”不会获得任何奖励)。
谢谢, 唐
答案 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());