2010-10-20 125 views
10

我正在研究Groovy/Java日曆類型的應用程序,該應用程序允許用戶輸入具有開始日期和可選的重複事件的事件。如果這是一個經常性的事件,它可能recurr:週期性事件邏輯

  • 每月上對應於開始日期
  • 在每週的那一週中的一天,一個月的日期對應的開始日期
  • 每2周的那一週中的一天相當於起始日期

我原本計劃使用的谷歌日曆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 
    } 
} 

這種邏輯看似合理,但實際上是相當多的努力來實現,當你考慮所有的怪異邊緣的情況下(例如,如何處理每月週期性事件發生在1月31日)。

有沒有可以幫我實施的圖書館?一些具體細節將受到高度讚賞(例如,不會因「使用喬達時間」而獲得獎勵)。

謝謝, 唐

+0

類似的問題:[最新最好的Java日期重複模式計算器(http://stackoverflow.com/q/492055/642706) – 2014-06-27 01:10:36

+0

這Martin Fowler的論文[重複事件的日曆(HTTP:/ /martinfowler.com/apsupp/recurring.pdf)可能會有用。 – 2014-06-27 01:11:36

回答

2

我一無所知Groovy和我的第一個建議將是喬達,但你知道這件事。

我知道這可能看起來有點矯枉過正,甚至可能不適用,但Quartz Scheduler處理所有這些重複和事件相關的規則很好。您無法使用其調度功能,只需使用觸發器類(如CronTrigger)爲您計算事件日期即可。

上面的CronTrigger鏈接顯示,你可以用它來處理您的活動,這樣特別惡劣的情況表達式的一些例子:「0012 L *」

- 在每個月中的最後一天的中午時段觸發事件(不會出現閏年等令人頭疼的問題)

夏令時問題也會處理。

至於代碼,創建具有所需復發觸發,然後你可以提取你希望所有的發射時間:

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

希望這可能是有用的。

+0

如果您有關於如何使用Joda/Quartz來實現我的目標的更多具體信息,我很樂意聽到它。 – 2010-10-20 12:11:17

+0

在上面添加了更多信息:) – mdrg 2010-10-20 12:27:01

8

你想要的那種重複規則在RFC-2445(基本上,iCal規範)中有相當明確的規定。獲得這個正確的細節可以相當複雜。我建議使用google-rfc-2445這個庫,或者像iCal4J這樣的規範的另一個實現。

0

我對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());