カレンダーアプリケーションで定期的なイベントをモデル化する最良の方法は何ですか?


225

私は定期的なイベントをサポートする必要があるグループカレンダーアプリケーションを構築していますが、これらのイベントを処理するために思いついたすべてのソリューションはハックのように見えます。どれだけ前を見ることができるかを制限し、すべてのイベントを一度に生成できます。または、イベントを繰り返しとして保存し、カレンダーを先に見たときに動的に表示することもできますが、誰かがイベントの特定のインスタンスの詳細を変更したい場合は、イベントを通常のイベントに変換する必要があります。

これを行うにはもっと良い方法があると確信していますが、まだ見つけていません。詳細を変更したり、特定のイベントインスタンスを削除したりできる、定期的なイベントをモデル化する最良の方法は何ですか?

(私はRubyを使用していますが、あなたの答えを制約しないようにしてください。ただし、Ruby固有のライブラリなどがある場合、それは知っておくと役に立ちます。)

回答:


93

今後の定期的なすべてのイベントに「リンク」の概念を使用します。それらはカレンダーに動的に表示され、単一の参照オブジェクトにリンクします。イベントが発生すると、リンクは切断され、イベントはスタンドアロンインスタンスになります。定期的なイベントを編集する場合は、今後のすべてのアイテムを変更するか(つまり、単一のリンクされた参照を変更する)、またはそのインスタンスのみを変更するように求めます(この場合、これをスタンドアロンインスタンスに変換してから変更します)。後者の場合は、単一のインスタンスに変換された将来のすべてのイベントの定期的なリストを追跡する必要があるため、少し問題があります。しかし、これは完全に実行可能です。

つまり、本質的には、2つのクラスのイベント(単一のインスタンスと定期的なイベント)があります。


通過したイベントをリンクしてスタンドアロンに変換するというアイデアが本当に好きです。2つの質問:-なぜそれらをスタンドアロンの固定インスタンスに変換するのですか?それらを完全に動的なままにしないでください。-提案されたリンクコンセプトのリファレンスを共有できますか?前もって感謝します!
rtindru 2017年

@rtindruイベントをスタンドアロンに変換するために見つけたユースケースは、データベース内の他のモデルでイベントモデルを使用する必要がある場合です。たとえば、イベントの出席状況を確認する場合は、ユーザーを実際の(または今後発生する)イベントに関連付ける必要があります。
クリントンイェボア2018年


33

定期的なイベントには多くの問題が発生する可能性があります。知っていることをいくつか紹介します。

ソリューション1-インスタンスなし

元の予定+繰り返しデータを保存し、すべてのインスタンスを保存しないでください。

問題:

  • 必要なときに、日付ウィンドウですべてのインスタンスを計算する必要があり、コストがかかります
  • 例外を処理できません(つまり、インスタンスの1つを削除したか、移動したか、このソリューションではこれを実行できません)

解決策2-インスタンスを保存する

1からすべてを保存しますが、元の予定にリンクされたすべてのインスタンスも保存します。

問題:

  • 多くのスペースを取ります(ただし、スペースが安いため、マイナー)
  • 特に例外を作成した後で元の予定に戻って編集する場合は、例外を適切に処理する必要があります。たとえば、3番目のインスタンスを1日進める場合、元の予定の時間に戻って編集し、元の日に別のインスタンスを再度挿入して、移動した日付のままにしておくとどうなりますか?移動したリンクを解除しますか?移動したものを適切に変更してみますか?

もちろん、例外を実行するつもりがない場合は、どちらのソリューションも問題なく、基本的には時間/空間のトレードオフシナリオから選択します。


36
終了日がない定期的な予定がある場合はどうなりますか?スペースと同じくらい安く、無限のスペースはありません。そのため、ソリューション2はそこにはありません...
Shaul Behr

13
ソリューション#1は実際には例外を処理できます。たとえば、RFC5545では、次のように保存することを推奨しています。b)プロトタイプを参照する「マテリアライズ」オカレンス(オカレンスを移動したとき)。
Andy Mikhaylenko、2011年

@ Andy、Lasseの回答への興味深い追加。それらを試してみるつもりです。
ジョナサンウィルソン

1
@Shaul:スターターではないと思います。SOでかなり尊敬されているJohn Skeetは、基本的に同じ質問への回答に生成されたインスタンスを保存することを提案しています:stackoverflow.com/a/10151804/155268
User

1
@ユーザー-ご了承いただき、ありがとうございます。それはとても奇妙です-私は4年以上前にコメントしましたが、それ以来、この問題に対処する必要はありませんでした。ちょうど昨日、定期的なアポイントメントを含む新しいモジュールの設計に取り掛かり、それらをどう処理するか疑問に思いました。そして-私は今朝あなたのコメントのSO通知を受け取りました。マジ不気味!でもありがとう!:-)
Shaul Behr

21

私は複数のカレンダーベースのアプリケーションを開発し、繰り返しをサポートする再利用可能なJavaScriptカレンダーコンポーネントのセットも作成しました。私は誰かに役立つかもしれない再発のためどのように設計するかの概要を書きました。私が書いたライブラリに固有のいくつかのビットがありますが、提供されるアドバイスの大部分は、すべてのカレンダー実装に一般的です。

重要なポイントのいくつか:

  • iCal RRULE形式を使用して繰り返しを保存します -それは、あなたが本当に再発明したくない1つのホイールです
  • 個々の定期的なイベントインスタンスをデータベースの行として保存しないでください。常に繰り返しパターンを保存します。
  • イベント/例外スキーマを設計するには多くの方法がありますが、基本的な開始点の例が提供されています
  • 日付/時刻の値はすべてUTCで保存し、表示用にローカルに変換する必要があります
  • 繰り返しイベントに保存される終了日は、常に繰り返し範囲の終了日(または繰り返し「永久に」の場合はプラットフォームの「最大日」)である必要があり、イベントの期間は別に保存する必要があります。これは、後でイベントをクエリする正しい方法を確実にするためです。
  • イベントインスタンスの生成と繰り返し編集戦略に関するいくつかの議論が含まれています

これは非常に複雑なトピックであり、実装するための多くの有効なアプローチがあります。私は実際に何度か再発を成功裏に実行したと言いますが、私は実際にそれを行っていない人からこの問題についてアドバイスを受けることに警戒するでしょう。


おそらく、繰り返しが発生したときにイベントとしてイベントを保存して、カレンダーの履歴が正確になるようにします
リチャードヘブン

@RichardHavenそんなことは絶対にしない。RRULEパターンから常に過去、現在、または未来のインスタンスを生成する必要があります。歴史的な出来事のために何か違うことをする理由はないでしょう。ロジックは単に任意の日付範囲に対してRRULEを評価し、一致するイベントインスタンスを返す必要があります。
ブライアンモエスカウ2018

@BrianMoeskau素敵で役立つ概要!
Przemek Nowak、2018

@BrianMoeskauしかし、いくつかの発生がすでに発生した後に誰かがRRULEを編集すると、カレンダーの過去のビューに不正確な情報が表示されませんか?あるいは、その場合、RRULEを「分岐」させて、実際の過去の発生を正確に表すRRULEパターンの変更されたバージョンを保持しますか?
クリスチャン

1
@christianほとんどのカレンダーで繰り返しルールを更新すると、通常、「すべてのイベントを編集するか、このイベントのみを編集するか、将来のみを編集する」などのプロンプトが表示され、ユーザーは動作を選択できます。ほとんどの場合、ユーザーはおそらく「将来的に変更する」ことを意味しますが、ソフトウェアがどのように機能し、ユーザーにどのようなオプションを提供するかは、ユーザー次第です。
ブライアンモエスカウ

19

iCalendarソフトウェアの実装または標準自体(RFC 2445 RFC 5545)を確認することをお勧めします。すぐに頭に浮かぶのは、Mozillaプロジェクトhttp://www.mozilla.org/projects/calendar/です 。クイック検索でhttp://icalendar.rubyforge.org/も明らかになります。

イベントの格納方法に応じて、他のオプションを検討できます。独自のデータベーススキーマを構築していますか?iCalendarベースなどを使用していますか?


これらのいずれかへのリンクを提供できれば、投稿は完璧です
Jean

7
RFC2445がRFC5545(tools.ietf.org/html/rfc5545)によって廃止されているようです
Eric Freese

16

私は次のものを使用しています:

そして、入力タイプ:recurring(form.schedule :as => :recurring)でformtasticを拡張する進行中のgem は、iCalのようなインターフェイスとをレンダリングしてbefore_filter、ビューをIceCube再びオブジェクトにシリアル化します。

私のアイデアは、繰り返し属性をモデルに簡単に追加して、ビューで簡単に接続できるようにすることです。すべては数行です。


それでこれは私に何を与えますか?インデックス付き、編集可能、繰り返し属性。

eventsは1日のインスタンスを格納し、カレンダービュー/ヘルパーで使用されるsay task.scheduleはyamlされたIceCubeオブジェクトを格納するため、次のような呼び出しを実行できますtask.schedule.next_suggestion

要約:私は2つのモデルを使用します。1つはカレンダー表示用で、もう1つは機能性用です。


あなたが思いついたものを見てみたいと思います。git / blog /概念実証はどこにありますか?ありがとう!
モントリオールマイク

私も同様の作業をしています。あなたの実装を見てみたい
thinkpunch 2012年


5
  1. 定期的なルールを追跡します(おそらく@ Kris K.の iCalendarに基づいています)。これには、パターンと範囲が含まれます(第3火曜日、10回)。
  2. 特定の発生を編集/削除する場合は、上記の繰り返しルールの例外日(イベントが発生した日付)を追跡します。 が発生しないルールの指定どおりに発生ます。
  3. 削除した場合はそれで十分です。編集した場合は、別のイベントを作成し、メインイベントに設定された親IDを付与します。このレコードにメインイベントのすべての情報を含めるか、変更のみを保持し、変更されないものすべてを継承するかを選択できます。

終了しない繰り返しルールを許可する場合、無限の情報を表示する方法を考える必要があることに注意してください。

お役に立てば幸いです。


4

日付ライブラリのパワーとrubyのrangeモジュールのセマンティクスを使用することをお勧めします。繰り返し発生するイベントは、実際には時間、日付範囲(開始と終了)であり、通常は1つの曜日です。日付と範囲を使用して、任意の質問に答えることができます。

#!/usr/bin/ruby
require 'date'

start_date = Date.parse('2008-01-01')
end_date   = Date.parse('2008-04-01')
wday = 5 # friday

(start_date..end_date).select{|d| d.wday == wday}.map{|d| d.to_s}.inspect

うるう年を含む、イベントのすべての日を生成します。

# =>"[\"2008-01-04\", \"2008-01-11\", \"2008-01-18\", \"2008-01-25\", \"2008-02-01\", \"2008-02-08\", \"2008-02-15\", \"2008-02-22\", \"2008-02-29\", \"2008-03-07\", \"2008-03-14\", \"2008-03-21\", \"2008-03-28\"]"

2
これはあまり柔軟ではありません。多くの場合、繰り返し発生するイベントモデルでは、繰り返し期間(毎時、毎週、隔週など)を指定する必要があります。さらに、繰り返しは、総数ではなく、最後の発生の終了日によって限定されない可能性があります
Bo Jeanes

これはちょうど1限られたユースケースであり、そのような毎月」などの「5日目として多くの人扱いしない、「A定期的なイベントは、週の[..]通常、単一の日です」
theraven

3

これらの回答から、私は解決策を少しふるいにかけました。リンクのコンセプトがとても気に入っています。繰り返しイベントはリンクされたリストであり、テールはその繰り返しルールを知っています。リンクはそのままで、1つのイベントの変更は簡単です。イベントの削除も簡単です。イベントのリンクを解除して削除し、その前後にイベントを再リンクするだけです。それでも、誰かがカレンダーでこれまでに見られたことのない新しい期間を見るたびに、定期的なイベントをクエリする必要がありますが、それ以外の場合、これはかなりクリーンです。


2

イベントを繰り返しとして保存し、特定のインスタンスが編集された場合は、同じイベントIDで新しいイベントを作成できます。次に、イベントを検索するときに、同じイベントIDを持つすべてのイベントを検索して、すべての情報を取得します。独自のイベントライブラリを展開したのか、それとも既存のライブラリを使用しているので不可能かもしれません。


このソリューションを1回使用しました。変更されたインスタンスを、そのママが誰であるかを知る新しい1回限りのイベントとして保存するという原則が気に入っています。このようにして、子イベントで異なるフィールドを除いて、すべてのフィールドを空のままにすることができます。この母親のどの子を編集するかを指定する追加のフィールドが必要になることに注意してください。
Wytze、2012年


1

JavaScriptで:

定期的なスケジュールの処理: http //bunkat.github.io/later/

複雑なイベントとそれらのスケジュール間の依存関係の処理:http : //bunkat.github.io/schedule/

基本的には、ルールを作成してから、libに次のN回の繰り返しイベント(日付範囲を指定するかどうか)を計算するように要求します。ルールは、モデルに保存するために解析/シリアル化できます。

定期的なイベントがあり、1つの定期的なイベントのみを変更する場合は、excepting ()関数を使用して特定の日を閉じ、このエントリに変更された新しいイベントを追加できます。

libは、非常に複雑なパターン、タイムゾーン、さらにはcroningイベントまでサポートしています。


0

イベントを繰り返しとして保存し、動的に表示しますが、定期的なイベントに、特定の日のデフォルト情報を上書きできる特定のイベントのリストを含めることができます。

定期的なイベントをクエリすると、その日の特定のオーバーライドを確認できます。

ユーザーが変更を加えた場合は、すべてのインスタンス(デフォルトの詳細)を更新するか、その日だけ(新しい特定のイベントを作成してリストに追加する)を更新するかを尋ねることができます。

ユーザーがこのイベントのすべての繰り返しを削除するように依頼した場合、あなたは手渡す詳細のリストも持っており、簡単に削除することができます。

問題となる唯一のケースは、ユーザーがこのイベントと今後のすべてのイベントを更新したい場合です。その場合は、定期的な予定を2つに分割する必要があります。この時点で、定期的なイベントをすべて削除できるように、何らかの方法で定期的なイベントをリンクすることを検討できます。


0

いくつかのライセンス料を支払う準備ができている.NETプログラマーにとって、Aspose.Networkは便利だと思うかもしれません...定期的な予定のためのiCalendar互換ライブラリが含まれています。


0

イベントはiCalendar形式で直接保存します。これにより、オープンエンドの繰り返し、タイムゾーンのローカライズなどが可能になります。

これらをCalDAVサーバーに保存し、イベントを表示する場合は、CalDAVで定義されたレポートのオプションを使用して、表示された期間にわたって定期的なイベントの展開をサーバーに依頼できます。

または、データベースに自分で保存し、何らかのiCalendar解析ライブラリを使用して、PUT / GET / REPORTでバックエンドのCalDAVサーバーと通信することなく拡張を行うこともできます。これはおそらくもっと手間がかかるでしょう。CalDAVサーバーは複雑さをどこかに隠していると思います。

イベントをiCalendar形式で作成すると、他のソフトウェアを追加するために常にイベントをエクスポートする必要が生じるため、長期的に見ればより簡単になります。


0

私は単にこの機能を実装しました!ロジックは次のとおりです。最初に2つのテーブルが必要です。RuleTableは、一般的なイベントまたはリサイクルの父方のイベントを格納します。ItemTableは、保存されたサイクルイベントです。たとえば、周期的なイベントを作成すると、2015年11月6日の開始時刻、12月6日(または無期限)の終了時刻が1週間循環します。RuleTableにデータを挿入すると、フィールドは次のようになります。

TableID: 1 Name: cycleA  
StartTime: 6 November 2014 (I kept thenumber of milliseconds),  
EndTime: 6 November 2015 (if it is repeated forever, and you can keep the value -1) 
Cycletype: WeekLy.

次に、11月20日から12月20日までのデータをクエリします。開始時刻と終了時刻、WeekLyに基づいて、RecurringEventBE(ロングスタート、ロングエンド)関数を記述できます。必要なコレクションを計算できます、<cycleA11.20、cycleA 11.27、cycleA 12.4 ......>。11月6日と残りに加えて、私は彼を仮想イベントと呼んだ。ユーザーが仮想イベントの名前を変更した後(たとえば、cycleA11.27)、ItemTableにデータを挿入します。フィールドは次のとおりです。

TableID: 1 
Name, cycleB  
StartTime, 27 November 2014  
EndTime,November 6 2015  
Cycletype, WeekLy
Foreignkey, 1 (pointingto the table recycle paternal events).

関数RecurringEventBE(ロングスタート、ロングエンド)では、仮想イベント(cycleB11.27)をカバーするこのデータを使用します。

これは私のRecurringEventBEです。

public static List<Map<String, Object>> recurringData(Context context,
        long start, long end) { // 重复事件的模板处理,生成虚拟事件(根据日期段)
     long a = System.currentTimeMillis();
    List<Map<String, Object>> finalDataList = new ArrayList<Map<String, Object>>();

    List<Map<String, Object>> tDataList = BillsDao.selectTemplateBillRuleByBE(context); //RuleTablejust select recurringEvent
    for (Map<String, Object> iMap : tDataList) {

        int _id = (Integer) iMap.get("_id");
        long bk_billDuedate = (Long) iMap.get("ep_billDueDate"); // 相当于事件的开始日期 Start
        long bk_billEndDate = (Long) iMap.get("ep_billEndDate"); // 重复事件的截止日期 End
        int bk_billRepeatType = (Integer) iMap.get("ep_recurringType"); // recurring Type 

        long startDate = 0; // 进一步精确判断日记起止点,保证了该段时间断获取的数据不未空,减少不必要的处理
        long endDate = 0;

        if (bk_billEndDate == -1) { // 永远重复事件的处理

            if (end >= bk_billDuedate) {
                endDate = end;
                startDate = (bk_billDuedate <= start) ? start : bk_billDuedate; // 进一步判断日记起止点,这样就保证了该段时间断获取的数据不未空
            }

        } else {

            if (start <= bk_billEndDate && end >= bk_billDuedate) { // 首先判断起止时间是否落在重复区间,表示该段时间有重复事件
                endDate = (bk_billEndDate >= end) ? end : bk_billEndDate;
                startDate = (bk_billDuedate <= start) ? start : bk_billDuedate; // 进一步判断日记起止点,这样就保证了该段时间断获取的数据不未空
            }
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(bk_billDuedate); // 设置重复的开始日期

        long virtualLong = bk_billDuedate; // 虚拟时间,后面根据规则累加计算
        List<Map<String, Object>> virtualDataList = new ArrayList<Map<String, Object>>();// 虚拟事件

        if (virtualLong == startDate) { // 所要求的时间,小于等于父本时间,说明这个是父事件数据,即第一条父本数据

            Map<String, Object> bMap = new HashMap<String, Object>();
            bMap.putAll(iMap);
            bMap.put("indexflag", 1); // 1表示父本事件
            virtualDataList.add(bMap);
        }

        long before_times = 0; // 计算从要求时间start到重复开始时间的次数,用于定位第一次发生在请求时间段落的时间点
        long remainder = -1;
        if (bk_billRepeatType == 1) {

            before_times = (startDate - bk_billDuedate) / (7 * DAYMILLIS);
            remainder = (startDate - bk_billDuedate) % (7 * DAYMILLIS);

        } else if (bk_billRepeatType == 2) {

            before_times = (startDate - bk_billDuedate) / (14 * DAYMILLIS);
            remainder = (startDate - bk_billDuedate) % (14 * DAYMILLIS);

        } else if (bk_billRepeatType == 3) {

            before_times = (startDate - bk_billDuedate) / (28 * DAYMILLIS);
            remainder = (startDate - bk_billDuedate) % (28 * DAYMILLIS);

        } else if (bk_billRepeatType == 4) {

            before_times = (startDate - bk_billDuedate) / (15 * DAYMILLIS);
            remainder = (startDate - bk_billDuedate) % (15 * DAYMILLIS);

        } else if (bk_billRepeatType == 5) {

            do { // 该段代码根据日历处理每天重复事件,当事件比较多的时候效率比较低

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH, 1);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 1 + 1);
                    virtualLong = calendar.getTimeInMillis();
                } else {
                    calendar.add(Calendar.MONTH, 1);
                    virtualLong = calendar.getTimeInMillis();
                }

            } while (virtualLong < startDate);

        } else if (bk_billRepeatType == 6) {

            do { // 该段代码根据日历处理每天重复事件,当事件比较多的时候效率比较低

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH, 2);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 2 + 2);
                    virtualLong = calendar.getTimeInMillis();
                } else {
                    calendar.add(Calendar.MONTH, 2);
                    virtualLong = calendar.getTimeInMillis();
                }

            } while (virtualLong < startDate);

        } else if (bk_billRepeatType == 7) {

            do { // 该段代码根据日历处理每天重复事件,当事件比较多的时候效率比较低

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH, 3);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 3 + 3);
                    virtualLong = calendar.getTimeInMillis();
                } else {
                    calendar.add(Calendar.MONTH, 3);
                    virtualLong = calendar.getTimeInMillis();
                }

            } while (virtualLong < startDate);

        } else if (bk_billRepeatType == 8) {

            do {
                calendar.add(Calendar.YEAR, 1);
                virtualLong = calendar.getTimeInMillis();
            } while (virtualLong < startDate);

        }

        if (remainder == 0 && virtualLong != startDate) { // 当整除的时候,说明当月的第一天也是虚拟事件,判断排除为父本,然后添加。不处理,一个月第一天事件会丢失
            before_times = before_times - 1;
        }

        if (bk_billRepeatType == 1) { // 单独处理天事件,计算出第一次出现在时间段的事件时间

            virtualLong = bk_billDuedate + (before_times + 1) * 7
                    * (DAYMILLIS);
            calendar.setTimeInMillis(virtualLong);

        } else if (bk_billRepeatType == 2) {

            virtualLong = bk_billDuedate + (before_times + 1) * (2 * 7)
                    * DAYMILLIS;
            calendar.setTimeInMillis(virtualLong);
        } else if (bk_billRepeatType == 3) {

            virtualLong = bk_billDuedate + (before_times + 1) * (4 * 7)
                    * DAYMILLIS;
            calendar.setTimeInMillis(virtualLong);
        } else if (bk_billRepeatType == 4) {

            virtualLong = bk_billDuedate + (before_times + 1) * (15)
                    * DAYMILLIS;
            calendar.setTimeInMillis(virtualLong);
        }

        while (startDate <= virtualLong && virtualLong <= endDate) { // 插入虚拟事件
            Map<String, Object> bMap = new HashMap<String, Object>();
            bMap.putAll(iMap);
            bMap.put("ep_billDueDate", virtualLong);
            bMap.put("indexflag", 2); // 2表示虚拟事件
            virtualDataList.add(bMap);

            if (bk_billRepeatType == 1) {

                calendar.add(Calendar.DAY_OF_MONTH, 7);

            } else if (bk_billRepeatType == 2) {

                calendar.add(Calendar.DAY_OF_MONTH, 2 * 7);

            } else if (bk_billRepeatType == 3) {

                calendar.add(Calendar.DAY_OF_MONTH, 4 * 7);

            } else if (bk_billRepeatType == 4) {

                calendar.add(Calendar.DAY_OF_MONTH, 15);

            } else if (bk_billRepeatType == 5) {

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH,
                        1);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 1
                            + 1);
                } else {
                    calendar.add(Calendar.MONTH, 1);
                }

            }else if (bk_billRepeatType == 6) {

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH,
                        2);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 2
                            + 2);
                } else {
                    calendar.add(Calendar.MONTH, 2);
                }

            }else if (bk_billRepeatType == 7) {

                Calendar calendarCloneCalendar = (Calendar) calendar
                        .clone();
                int currentMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);
                calendarCloneCalendar.add(Calendar.MONTH,
                        3);
                int nextMonthDay = calendarCloneCalendar
                        .get(Calendar.DAY_OF_MONTH);

                if (currentMonthDay > nextMonthDay) {
                    calendar.add(Calendar.MONTH, 3
                            + 3);
                } else {
                    calendar.add(Calendar.MONTH, 3);
                }

            } else if (bk_billRepeatType == 8) {

                calendar.add(Calendar.YEAR, 1);

            }
            virtualLong = calendar.getTimeInMillis();

        }

        finalDataList.addAll(virtualDataList);

    }// 遍历模板结束,产生结果为一个父本加若干虚事件的list

    /*
     * 开始处理重复特例事件特例事件,并且来时合并
     */
    List<Map<String, Object>>oDataList = BillsDao.selectBillItemByBE(context, start, end);
    Log.v("mtest", "特例结果大小" +oDataList );


    List<Map<String, Object>> delectDataListf = new ArrayList<Map<String, Object>>(); // finalDataList要删除的结果
    List<Map<String, Object>> delectDataListO = new ArrayList<Map<String, Object>>(); // oDataList要删除的结果


    for (Map<String, Object> fMap : finalDataList) { // 遍历虚拟事件

        int pbill_id = (Integer) fMap.get("_id");
        long pdue_date = (Long) fMap.get("ep_billDueDate");

        for (Map<String, Object> oMap : oDataList) {

            int cbill_id = (Integer) oMap.get("billItemHasBillRule");
            long cdue_date = (Long) oMap.get("ep_billDueDate");
            int bk_billsDelete = (Integer) oMap.get("ep_billisDelete");

            if (cbill_id == pbill_id) {

                if (bk_billsDelete == 2) {// 改变了duedate的特殊事件
                    long old_due = (Long) oMap.get("ep_billItemDueDateNew");

                    if (old_due == pdue_date) {

                        delectDataListf.add(fMap);//该改变事件在时间范围内,保留oMap

                    }

                } else if (bk_billsDelete == 1) {

                    if (cdue_date == pdue_date) {

                        delectDataListf.add(fMap);
                        delectDataListO.add(oMap);

                    }

                } else {

                    if (cdue_date == pdue_date) {
                        delectDataListf.add(fMap);
                    }

                }

            }
        }// 遍历特例事件结束

    }// 遍历虚拟事件结束
    // Log.v("mtest", "delectDataListf的大小"+delectDataListf.size());
    // Log.v("mtest", "delectDataListO的大小"+delectDataListO.size());
    finalDataList.removeAll(delectDataListf);
    oDataList.removeAll(delectDataListO);
    finalDataList.addAll(oDataList);
    List<Map<String, Object>> mOrdinaryList = BillsDao.selectOrdinaryBillRuleByBE(context, start, end);
    finalDataList.addAll(mOrdinaryList);
    // Log.v("mtest", "finalDataList的大小"+finalDataList.size());
    long b = System.currentTimeMillis();
    Log.v("mtest", "算法耗时"+(b-a));

    return finalDataList;
}   

-5

終了日がない定期的な予定がある場合はどうなりますか?スペースと同じくらい安く、無限のスペースはありません。そのため、ソリューション2はそこにはないスターターです...

「終了日なし」を世紀末の終了日に解決できることをお勧めします。毎日のイベントでさえ、スペースの量は安いままです。


7
y2kのレッスンをどれだけ早く忘れるか... :)
Ian Mercer

10
1000人のユーザーがいて、それぞれに1日2、3回のイベントがあります。3イベント×1000ユーザー×365日×(2100-2011 = 89年)= 9750万レコード。3000の「計画」の代わりに。ええと...
アンディ・ミハイレンコ2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.