it-swarm-eu.dev

Wie lassen sich wiederkehrende Ereignisse in einer Kalenderanwendung am besten modellieren?

Ich erstelle eine Gruppenkalenderanwendung, die wiederkehrende Ereignisse unterstützen muss, aber alle Lösungen, die ich für diese Ereignisse gefunden habe, wirken wie ein Hack. Ich kann einschränken, wie weit man in die Zukunft schauen kann, und dann alle Ereignisse auf einmal generieren. Oder ich speichere die Ereignisse als Wiederholung und zeige sie dynamisch an, wenn man im Kalender nach vorne schaut, aber ich muss sie in ein normales Ereignis umwandeln, wenn jemand die Details einer bestimmten Instanz des Ereignisses ändern möchte.

Ich bin mir sicher, dass es einen besseren Weg gibt, aber ich habe ihn noch nicht gefunden. Wie lassen sich wiederkehrende Ereignisse am besten modellieren, bei denen Sie Details bestimmter Ereignisinstanzen ändern oder löschen können?

(Ich verwende Ruby, aber bitte lassen Sie Ihre Antwort nicht einschränken. Wenn es eine Ruby-spezifische Bibliothek oder etwas anderes gibt, ist das gut zu wissen.)

213

Ich würde ein "Link" -Konzept für alle zukünftigen wiederkehrenden Ereignisse verwenden. Sie werden dynamisch im Kalender angezeigt und sind mit einem einzelnen Referenzobjekt verknüpft. Wenn Ereignisse stattgefunden haben, wird die Verbindung unterbrochen und das Ereignis wird zu einer eigenständigen Instanz. Wenn Sie versuchen, ein wiederkehrendes Ereignis zu bearbeiten, werden Sie aufgefordert, alle zukünftigen Elemente zu ändern (d. H. Eine einzelne verknüpfte Referenz zu ändern) oder nur diese Instanz zu ändern (in diesem Fall konvertieren Sie diese in eine eigenständige Instanz und nehmen dann Änderungen vor). Letzteres ist etwas problematisch, da Sie in Ihrer wiederkehrenden Liste alle zukünftigen Ereignisse festhalten müssen, die in eine einzelne Instanz konvertiert wurden. Dies ist jedoch völlig machbar.

Es gibt also im Wesentlichen zwei Ereignisklassen - einzelne Instanzen und wiederkehrende Ereignisse.

84
user16068

Martin Fowler - Wiederkehrende Ereignisse für Kalender enthält einige interessante Einsichten und Muster.

Runt gem implementiert dieses Muster.

54
Daniel Maurić

Es kann viele Probleme mit wiederkehrenden Ereignissen geben, lassen Sie mich einige hervorheben, die mir bekannt sind.

Lösung 1 - keine Instanzen

Speichern Sie die ursprünglichen Termin- und Wiederholungsdaten. Speichern Sie nicht alle Instanzen.

Probleme:

  • Sie müssen alle Instanzen in einem Datumsfenster berechnen, wenn Sie sie kostenintensiv benötigen
  • Ausnahmen können nicht behandelt werden (dh Sie löschen eine der Instanzen oder verschieben sie oder können dies mit dieser Lösung nicht tun)

Lösung 2 - Instanzen speichern

Speichern Sie alles von 1, aber auch alle Instanzen, die mit dem ursprünglichen Termin verknüpft sind.

Probleme:

  • Nimmt viel Platz in Anspruch (aber der Platz ist billig, so gering)
  • Ausnahmen müssen mit Sorgfalt behandelt werden, insbesondere wenn Sie den ursprünglichen Termin nach dem Auftreten einer Ausnahme erneut bearbeiten. Wenn Sie beispielsweise die dritte Instanz um einen Tag vorwärts verschieben, können Sie dann die Uhrzeit des ursprünglichen Termins ändern, eine weitere Instanz am ursprünglichen Tag einfügen und die verschobene beibehalten. Verknüpfung des Verschobenen aufheben? Versuchen Sie, das Verschobene entsprechend zu ändern?

Wenn Sie keine Ausnahmen machen, sollte natürlich jede Lösung in Ordnung sein, und Sie wählen im Grunde genommen aus einem Zeit/Raum-Kompromiss-Szenario.

Sie können sich die Implementierungen der iCalendar-Software oder den Standard selbst ansehen (RFC 2445 RFC 5545 ). Diejenigen, die Ihnen schnell einfallen, sind die Mozilla-Projekte http://www.mozilla.org/projects/calendar/ Eine schnelle Suche zeigt http://icalendar.rubyforge.org/ auch.

Je nachdem, wie Sie die Ereignisse speichern, können andere Optionen in Betracht gezogen werden. Bauen Sie Ihr eigenes Datenbankschema auf? Verwenden Sie etwas auf iCalendar basierendes usw.?

17
Kris Kumler

Ich arbeite mit:

und ein laufendes Juwel, das formtastic um einen Eingabetyp erweitert: wiederkehrend (form.schedule :as => :recurring), wodurch eine iCal-ähnliche Schnittstelle und ein before_filter zur Serialisierung der Ansicht in ein IceCube gerendert werden. Objekt wieder, Ghetto-ly.

Meine Idee ist es, es unglaublich einfach zu machen, einem Modell wiederkehrende Attribute hinzuzufügen und es einfach in der Ansicht zu verbinden. Alles in ein paar Zeilen.


Was gibt mir das? Indizierte, bearbeitbare, wiederkehrende Attribute.

events speichert eine einzelne Tagesinstanz und wird in der Kalenderansicht/im Hilfsprogramm verwendet. Sagen wir, task.schedule speichert das yaml'd IceCube -Objekt, sodass Sie Anrufe ausführen können wie: task.schedule.next_suggestion.

Fazit: Ich verwende zwei Modelle, ein flaches für die Kalenderanzeige und ein Attribut für die Funktionalität.

16
Vee

Ich habe mehrere kalenderbasierte Anwendungen entwickelt und einen Satz wiederverwendbarer JavaScript-Kalenderkomponenten erstellt, die die Wiederholung unterstützen. Ich habe eine Übersicht über wie man für eine Wiederholung entwirft geschrieben, die für jemanden hilfreich sein könnte. Zwar gibt es ein paar Punkte, die für die von mir geschriebene Bibliothek spezifisch sind, aber die überwiegende Mehrheit der angebotenen Ratschläge bezieht sich auf die Implementierung von Kalendern.

Einige der wichtigsten Punkte:

  • Speichern Sie die Wiederholung mit dem Format iCal RRULE - das ist ein Rad, das Sie wirklich nicht neu erfinden möchten
  • Speichern Sie KEINE einzelnen wiederkehrenden Ereignisinstanzen als Zeilen in Ihrer Datenbank! Speichern Sie immer ein Wiederholungsmuster.
  • Es gibt viele Möglichkeiten zum Entwerfen Ihres Ereignis-/Ausnahmeschemas, es wird jedoch ein grundlegendes Beispiel für einen Ausgangspunkt bereitgestellt
  • Alle Datums-/Zeitwerte sollten in UTC gespeichert und zur Anzeige in lokale Werte konvertiert werden
  • Das für ein wiederkehrendes Ereignis gespeicherte Enddatum sollte immer das Enddatum des Wiederholungsbereichs sein (oder das "maximale Datum" Ihrer Plattform, wenn es "für immer" wiederholt wird) und die Ereignisdauer sollten separat gespeichert werden. Dies soll sicherstellen, dass Ereignisse später auf vernünftige Weise abgefragt werden.
  • Es werden einige Diskussionen zum Generieren von Ereignisinstanzen und zu Strategien zum Bearbeiten von Wiederholungen enthalten

Es ist ein wirklich kompliziertes Thema mit vielen, vielen gültigen Ansätzen zur Implementierung. Ich werde sagen, dass ich die Wiederholung tatsächlich mehrmals erfolgreich implementiert habe, und ich würde mich davor hüten, Ratschläge zu diesem Thema von jedem anzunehmen, der es nicht wirklich getan hat.

14
Brian Moeskau

Ich verwende das unten beschriebene Datenbankschema, um die Wiederholungsparameter zu speichern

http://github.com/bakineggs/recurring_events_for

Dann benutze ich runt, um die Daten dynamisch zu berechnen.

https://github.com/mlipper/runt

6
liangzan
  1. Verfolgen Sie eine Wiederholungsregel (wahrscheinlich basierend auf iCalendar, per @ Kris K. ). Dies beinhaltet ein Muster und einen Bereich (jeden dritten Dienstag für 10 Vorkommen).
  2. Wenn Sie ein bestimmtes Ereignis bearbeiten/löschen möchten, protokollieren Sie die Ausnahmedaten für die obige Wiederholungsregel (Daten, an denen das Ereignis nicht auftritt als die Regel spezifiziert).
  3. Wenn Sie ein Ereignis gelöscht haben, ist dies alles, was Sie zum Bearbeiten benötigen. Erstellen Sie ein anderes Ereignis und geben Sie ihm eine übergeordnete ID, die für das Hauptereignis festgelegt ist. Sie können wählen, ob alle Informationen des Hauptereignisses in diesen Datensatz aufgenommen werden sollen oder ob nur die Änderungen gespeichert werden und alles übernommen wird, was sich nicht ändert.

Wenn Sie Wiederholungsregeln zulassen, die nicht enden, müssen Sie sich überlegen, wie Sie die jetzt unendliche Menge an Informationen anzeigen.

Hoffentlich hilft das!

5
bdukes

Ich würde empfehlen, die Leistungsfähigkeit der Datumsbibliothek und die Semantik des Bereichsmoduls von Ruby zu nutzen. Ein wiederkehrendes Ereignis ist in Wirklichkeit eine Zeit, ein Datumsbereich (ein Anfang und ein Ende) und normalerweise ein einzelner Wochentag. Mit Datum & Zeitraum können Sie jede Frage beantworten:

#!/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

Produziert alle Tage des Events, einschließlich das Schaltjahr!

# =>"[\"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\"]"
4
Purfideas

Aus diesen Antworten habe ich eine Art Lösung herausgesucht. Die Idee des Link-Konzepts gefällt mir sehr gut. Wiederkehrende Ereignisse können eine verknüpfte Liste sein, deren Wiederholungsregel der Schwanz kennt. Das Ändern eines Ereignisses ist dann einfach, da die Verknüpfungen bestehen bleiben und das Löschen eines Ereignisses ebenfalls einfach ist. Sie müssen nur die Verknüpfung eines Ereignisses aufheben, es löschen und das Ereignis vor und nach dem Ereignis erneut verknüpfen. Sie müssen immer noch wiederkehrende Ereignisse abfragen, wenn jemand einen neuen Zeitraum betrachtet, der noch nie zuvor im Kalender betrachtet wurde, aber ansonsten ist dies ziemlich sauber.

Im folgenden Artikel finden Sie drei gute Ruby Datums-/Zeitbibliotheken. Insbesondere ice_cube scheint eine gute Wahl für Wiederholungsregeln und andere Dinge zu sein, die ein Veranstaltungskalender benötigen würde. http: // www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

2
gokul.janga

Sie können die Ereignisse als sich wiederholend speichern. Wenn eine bestimmte Instanz bearbeitet wurde, erstellen Sie ein neues Ereignis mit derselben Ereignis-ID. Wenn Sie dann nach dem Ereignis suchen, suchen Sie nach allen Ereignissen mit derselben Ereignis-ID, um alle Informationen abzurufen. Ich bin mir nicht sicher, ob Sie Ihre eigene Eventbibliothek gerollt haben oder ob Sie eine vorhandene verwenden, sodass dies möglicherweise nicht möglich ist.

2
Vincent McNabb

In Javascript:

Umgang mit wiederkehrenden Zeitplänen: http://bunkat.github.io/later/

Umgang mit komplexen Ereignissen und Abhängigkeiten zwischen diesen Zeitplänen: http://bunkat.github.io/schedule/

Grundsätzlich erstellen Sie die Regeln und fordern die Bibliothek auf, die nächsten N wiederkehrenden Ereignisse zu berechnen (wobei ein Datumsbereich angegeben wird oder nicht). Die Regeln können analysiert/serialisiert werden, um sie in Ihrem Modell zu speichern.

Wenn Sie ein wiederkehrendes Ereignis haben und nur eine Wiederholung ändern möchten, können Sie die Funktion except () verwenden, um einen bestimmten Tag zu schließen und dann einen neuen hinzuzufügen Geändertes Ereignis für diesen Eintrag.

Die Bibliothek unterstützt sehr komplexe Muster, Zeitzonen und sogar das Anpassen von Ereignissen.

1
Flavien Volken

Für .NET-Programmierer, die bereit sind, Lizenzgebühren zu zahlen, ist Aspose.Network nützlich. Es enthält eine mit iCalendar kompatible Bibliothek für wiederkehrende Termine.

0
Shaul Behr

Speichern Sie die Ereignisse als sich wiederholend und zeigen Sie sie dynamisch an, lassen Sie jedoch zu, dass das wiederkehrende Ereignis eine Liste bestimmter Ereignisse enthält, die die Standardinformationen an einem bestimmten Tag überschreiben können.

Wenn Sie das wiederkehrende Ereignis abfragen, kann nach einer bestimmten Überschreibung für diesen Tag gesucht werden.

Wenn ein Benutzer Änderungen vornimmt, können Sie ihn fragen, ob er für alle Instanzen (Standarddetails) oder nur für diesen Tag (ein neues bestimmtes Ereignis erstellen und der Liste hinzufügen) aktualisieren möchte.

Wenn ein Benutzer alle Wiederholungen dieses Ereignisses löschen möchte, haben Sie auch eine Liste mit Einzelheiten zur Hand, die Sie problemlos entfernen können.

Der einzige problematische Fall wäre, wenn der Benutzer dieses Ereignis und alle zukünftigen Ereignisse aktualisieren möchte. In diesem Fall müssen Sie das wiederkehrende Ereignis in zwei Teile aufteilen. An dieser Stelle möchten Sie möglicherweise wiederkehrende Ereignisse in irgendeiner Weise verknüpfen, damit Sie sie alle löschen können.

0
Andrew Johnson

Sie speichern die Ereignisse direkt im iCalendar-Format, was eine unbegrenzte Wiederholung, Zeitzonenlokalisierung usw. ermöglicht.

Sie können diese auf einem CalDAV-Server speichern. Wenn Sie dann die Ereignisse anzeigen möchten, können Sie die Option des in CalDAV definierten Berichts verwenden, um den Server aufzufordern, die wiederkehrenden Ereignisse über den angezeigten Zeitraum zu erweitern.

Sie können sie auch selbst in einer Datenbank speichern und eine Art iCalendar-Analysebibliothek verwenden, um die Erweiterung durchzuführen, ohne dass PUT/GET/REPORT für die Kommunikation mit einem Back-End-CalDAV-Server erforderlich ist. Dies ist wahrscheinlich mehr Arbeit - ich bin sicher, CalDAV-Server verbergen die Komplexität irgendwo.

Wenn die Ereignisse im iCalendar-Format vorliegen, wird dies die Dinge auf lange Sicht wahrscheinlich einfacher machen, da die Benutzer immer möchten, dass sie exportiert werden, um sie ohnehin in andere Software zu integrieren.

0
karora

Ich habe diese Funktion einfach implementiert! Die Logik ist wie folgt: Zuerst benötigen Sie zwei Tabellen. RuleTable speichert allgemeine oder wiederverwendbare väterliche Ereignisse. ItemTable enthält gespeicherte Zyklusereignisse. Wenn Sie beispielsweise ein zyklisches Ereignis erstellen, wird die Startzeit für den 6. November 2015, die Endzeit für den 6. Dezember (oder für immer) eine Woche lang wiederholt. Sie fügen Daten in eine Regeltabelle ein. Die Felder lauten wie folgt:

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.

Jetzt möchten Sie Daten vom 20. November bis 20. Dezember abfragen. Sie können eine Funktion RecurringEventBE (langer Anfang, langes Ende) schreiben, basierend auf der Start- und Endzeit WeekLy. Sie können die gewünschte Sammlung berechnen, <cycleA11.20, cycleA 11.27, cycleA 12.4 ......>. Zusätzlich zum 6. November und dem Rest nannte ich ihn ein virtuelles Ereignis. Wenn der Benutzer den Namen eines virtuellen Ereignisses ändert (z. B. cycleA11.27), fügen Sie Daten in eine ItemTable ein. Felder sind wie folgt:

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

In der Funktion RecurringEventBE (langer Anfang, langes Ende) verwenden Sie diese Daten für das virtuelle Ereignis (cycleB11.27). Entschuldigen Sie mein Englisch, ich habe es versucht.

Das ist mein 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); //RuleTable,just 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;
}   
0
fozua