Qual è il modo migliore per modellare eventi ricorrenti in un'applicazione di calendario?


225

Sto creando un'applicazione di calendario di gruppo che deve supportare eventi ricorrenti, ma tutte le soluzioni che ho escogitato per gestire questi eventi sembrano un trucco. Posso limitare quanto lontano si può guardare e quindi generare tutti gli eventi contemporaneamente. Oppure posso memorizzare gli eventi come ripetuti e visualizzarli dinamicamente quando uno guarda avanti nel calendario, ma dovrò convertirli in un evento normale se qualcuno vuole cambiare i dettagli su una particolare istanza dell'evento.

Sono sicuro che c'è un modo migliore per farlo, ma non l'ho ancora trovato. Qual è il modo migliore per modellare eventi ricorrenti, in cui è possibile modificare i dettagli o eliminare determinate istanze di eventi?

(Sto usando Ruby, ma per favore non lasciare che questo limiti la tua risposta. Se c'è una libreria specifica per Ruby o qualcosa del genere, è bene saperlo.)

Risposte:


93

Vorrei utilizzare un concetto di "collegamento" per tutti i futuri eventi ricorrenti. Vengono visualizzati dinamicamente nel calendario e rimandano a un singolo oggetto di riferimento. Quando si verificano eventi, il collegamento viene interrotto e l'evento diventa un'istanza autonoma. Se si tenta di modificare un evento ricorrente, richiedere di modificare tutti gli elementi futuri (ad esempio, modificare il riferimento singolo collegato) o modificare solo quell'istanza (nel qual caso convertire questo in un'istanza autonoma e quindi apportare la modifica). Quest'ultimo caso è leggermente problematico in quanto è necessario tenere traccia dell'elenco ricorrente di tutti gli eventi futuri che sono stati convertiti in singola istanza. Ma questo è del tutto fattibile.

Quindi, in sostanza, hanno 2 classi di eventi: singole istanze ed eventi ricorrenti.


Mi piace molto la tua idea di collegare e convertire eventi in standalone dopo che sono passati. Due domande: - Perché convertirle in istanze fisse standalone? Perché non lasciarli completamente dinamici? - Puoi condividere i riferimenti per il concetto di link proposto! Grazie in anticipo!
rtindru,

@rtindru un caso d'uso che ho trovato per convertire eventi in standalone è quando devi usare il modello di eventi con altri modelli nel tuo database. Ad esempio, per controllare la presenza di un evento, vorrai associare gli utenti a un evento reale accaduto (o accadrà).
Clinton Yeboah,


33

Ci possono essere molti problemi con eventi ricorrenti, fammi evidenziare alcuni che conosco.

Soluzione 1 - nessuna istanza

Memorizza appuntamento originale + dati di ricorrenza, non memorizzare tutte le istanze.

I problemi:

  • Dovrai calcolare tutte le istanze in una finestra della data quando ne avrai bisogno, costose
  • Impossibile gestire le eccezioni (ad es. Eliminare una delle istanze o spostarla, o meglio, non è possibile farlo con questa soluzione)

Soluzione 2: memorizzare le istanze

Memorizza tutto da 1, ma anche tutte le istanze, ricollegate all'appuntamento originale.

I problemi:

  • Occupa molto spazio (ma lo spazio è economico, quindi minore)
  • Le eccezioni devono essere gestite con garbo, specialmente se torni indietro e modifichi l'appuntamento originale dopo aver fatto un'eccezione. Ad esempio, se si sposta in avanti la terza istanza di un giorno, cosa succede se si torna indietro e si modifica l'ora dell'appuntamento originale, si inserisce un altro giorno originale e si lascia quello spostato? Scollegare quello spostato? Cerchi di cambiare quello spostato in modo appropriato?

Naturalmente, se non hai intenzione di fare eccezioni, allora entrambe le soluzioni dovrebbero andare bene e fondamentalmente scegli uno scenario di trade-time / spazio.


36
Cosa succede se si dispone di un appuntamento ricorrente senza data di fine? Per quanto economico sia lo spazio, non hai spazio infinito, quindi la Soluzione 2 è un non-inizio lì ...
Shaul Behr,

13
La soluzione n. 1 in realtà può gestire le eccezioni. Ad esempio, RFC5545 suggerisce che sono memorizzati come: a) un elenco di date escluse (quando si elimina un'occorrenza); b) ricorrenze "materializzate" con riferimenti al prototipo (quando si sposta un'occorrenza).
Andy Mikhaylenko,

@Andy, alcune interessanti aggiunte alla risposta di Lasse. Ci proverò.
Jonathan Wilson,

1
@Shaul: non penso che sia un antipasto. John Skeet, che è abbastanza rispettato su SO, suggerisce di archiviare istanze generate nella sua risposta sostanzialmente alla stessa domanda: stackoverflow.com/a/10151804/155268
Utente

1
@Utente - riconosciuto, grazie. È così strano - ho fatto il mio commento più di 4 anni fa e da allora non ho più avuto bisogno di affrontare questo problema. Proprio ieri ho iniziato a progettare un nuovo modulo che prevede appuntamenti ricorrenti e mi chiedevo come gestirli. E poi - ho ricevuto una notifica SO del tuo commento stamattina. Davvero inquietante! Ma grazie! :-)
Shaul Behr,

21

Ho sviluppato più applicazioni basate su calendario e ho anche creato una serie di componenti di calendario JavaScript riutilizzabili che supportano la ricorrenza. Ho scritto una panoramica di come progettare per la ricorrenza che potrebbe essere utile a qualcuno. Mentre ci sono alcuni bit specifici della libreria che ho scritto, la stragrande maggioranza dei consigli offerti è generale per qualsiasi implementazione del calendario.

Alcuni dei punti chiave:

  • Memorizza la ricorrenza utilizzando il formato iCU RRULE : questa è una ruota che non vuoi davvero reinventare
  • NON memorizzare singole istanze di eventi ricorrenti come righe nel database! Memorizza sempre un modello di ricorrenza.
  • Esistono molti modi per progettare lo schema di eventi / eccezioni, ma viene fornito un esempio di base del punto di partenza
  • Tutti i valori di data / ora devono essere memorizzati in UTC e convertiti in locali per la visualizzazione
  • La data di fine memorizzata per un evento ricorrente dovrebbe sempre essere la data di fine dell'intervallo di ricorrenza (o la "data massima" della piattaforma se ricorre "per sempre") e la durata dell'evento deve essere memorizzata separatamente. Questo per garantire un modo sano di interrogare gli eventi in un secondo momento.
  • Sono incluse alcune discussioni sulla generazione di istanze di eventi e sulle strategie di modifica delle ricorrenze

È un argomento davvero complicato con molti, molti approcci validi per implementarlo. Dirò che ho effettivamente implementato la ricorrenza più volte con successo, e sarei diffidente nel prendere consigli su questo argomento da chiunque non l'abbia effettivamente fatto.


Forse memorizza le ricorrenze come eventi quando si verificano in modo che la cronologia del tuo calendario sia accurata
Richard Haven,

@RichardHaven Non lo farei mai. Dovresti sempre generare istanze dai modelli RRULE in modo coerente, passato, presente o futuro. Non ci sarebbe motivo di fare qualcosa di diverso per eventi storici. La tua logica dovrebbe semplicemente valutare un RRULE rispetto a qualsiasi intervallo di date arbitrario e restituire istanze di eventi corrispondenti.
Brian Moeskau,

@BrianMoeskau bella e utile panoramica!
Przemek Nowak,

@BrianMoeskau Ma le visualizzazioni passate del tuo calendario non mostrerebbero informazioni inaccurate quando qualcuno modifica il RRULE dopo che si sono già verificate alcune occorrenze? O forse in quel caso "dirameresti" RRULE e manterresti le versioni modificate dei modelli RRULE che rappresentano esattamente le occorrenze passate?
christian

1
@christian Quando aggiorni una regola di ricorrenza nella maggior parte dei calendari, in genere viene richiesto "modifica tutti gli eventi, o solo questo, o solo futuro", consentendo all'utente di scegliere il comportamento. Nella maggior parte dei casi, l'utente probabilmente significa "cambiarlo in futuro" ma, di nuovo, spetta a te decidere come funziona il tuo software e quali opzioni offri all'utente.
Brian Moeskau,

19

Potresti voler esaminare le implementazioni del software iCalendar o lo standard stesso ( RFC 2445 RFC 5545 ). A venire subito in mente sono i progetti Mozilla http://www.mozilla.org/projects/calendar/ Una rapida ricerca rivela anche http://icalendar.rubyforge.org/ .

Altre opzioni possono essere considerate in base al modo in cui memorizzerai gli eventi. Stai costruendo il tuo schema di database? Usi qualcosa basato su iCalendar, ecc.?


se solo potessi fornire un link a uno di questi, il tuo post sarebbe perfetto
Jean,

7
Sembra che RFC2445 sia stato reso obsoleto da RFC5545 ( tools.ietf.org/html/rfc5545 )
Eric Freese

16

Sto lavorando con il seguente:

e una gemma in corso che estende formtastic con un tipo di input: recurring ( form.schedule :as => :recurring), che rende un'interfaccia simile a iCal e una before_filterserializzazione della vista in un IceCubeoggetto, ghetticamente.

La mia idea è di rendere incredibilmente facile aggiungere attributi ricorrenti a un modello e collegarlo facilmente nella vista. Tutto in un paio di righe.


Cosa mi dà questo? Attributi indicizzati, modificabili, ricorrenti.

eventsmemorizza una singola istanza giorno, e viene utilizzato nella visualizzazione del calendario / helper dicono task.schedulememorizza l'yaml'd IceCubeoggetto, in modo da poter fare chiamate come: task.schedule.next_suggestion.

Riepilogo: utilizzo due modelli, uno piatto, per la visualizzazione del calendario e uno attribuito per la funzionalità.


Sarei interessato a vedere cosa ti è venuto in mente. Hai un git / blog / proof of concept ovunque? Grazie!
montrealmike,

Sto lavorando anche a qualcosa di simile. Mi piacerebbe vedere la tua implementazione
thinkpunch


5
  1. Tieni traccia di una regola di ricorrenza (probabilmente basata su iCalendar, per @ Kris K. ). Ciò includerà un modello e un intervallo (ogni terzo martedì, per 10 occorrenze).
  2. Per quando si desidera modificare / eliminare un'occorrenza specifica, tenere traccia delle date di eccezione per la regola di ricorrenza sopra (date in cui l'evento non si verifica come specificato dalla regola).
  3. Se hai eliminato, è tutto ciò che ti serve, se modificato, crea un altro evento e assegnagli un ID genitore impostato sull'evento principale. Puoi scegliere se includere tutte le informazioni sull'evento principale in questo record o se contiene solo le modifiche ed eredita tutto ciò che non cambia.

Nota che se consenti regole di ricorrenza che non finiscono, devi pensare a come visualizzare la tua quantità infinita di informazioni.

Spero che aiuti!


4

Consiglierei di usare la potenza della libreria di date e la semantica del modulo range di ruby. Un evento ricorrente è in realtà un orario, un intervallo di date (un inizio e una fine) e di solito un solo giorno della settimana. Usando data e intervallo puoi rispondere a qualsiasi domanda:

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

Produce tutti i giorni dell'evento, incluso l'anno bisestile!

# =>"[\"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
Questo non è molto flessibile. Un modello di evento ricorrente richiederebbe spesso di specificare il periodo di ripetizione (orario, settimanale, quindicinale, ecc.). Inoltre, la ricorrenza potrebbe non essere qualificata da un numero totale, piuttosto da una data di fine dell'ultima occorrenza
Bo Jeanes,

"Un evento ricorrente è [..] di solito un solo giorno della settimana", questo è solo un caso d'uso limitato e non gestisce molti altri come "Il 5 ° giorno di ogni mese" ecc.
Theraven

3

Da queste risposte ho setacciato una soluzione. Mi piace molto l'idea del concetto di collegamento. Gli eventi ricorrenti potrebbero essere un elenco collegato, con la coda che conosce la sua regola di ricorrenza. La modifica di un evento sarebbe quindi facile, poiché i collegamenti rimangono attivi e anche l'eliminazione di un evento è semplice: basta scollegare un evento, eliminarlo e ricollegare l'evento prima e dopo di esso. Devi ancora interrogare gli eventi ricorrenti ogni volta che qualcuno guarda un nuovo periodo di tempo mai visto prima nel calendario, ma per il resto questo è abbastanza pulito.


2

È possibile archiviare gli eventi come ripetuti e, se una particolare istanza è stata modificata, creare un nuovo evento con lo stesso ID evento. Quindi, quando si cerca l'evento, cercare tutti gli eventi con lo stesso ID evento per ottenere tutte le informazioni. Non sono sicuro se hai creato la tua libreria eventi o se ne stai utilizzando una esistente, quindi potrebbe non essere possibile.


Ho usato questa soluzione una volta. Mi piace il principio di memorizzare un'istanza modificata come un nuovo evento unico che sa chi è la sua mamma. In questo modo è possibile lasciare vuoti tutti i campi tranne quelli diversi per l'evento figlio. Nota che dovrai avere un campo in più che specifica quale figlio di questa madre stai modificando.
Wytze,


1

In javascript:

Gestione delle pianificazioni ricorrenti: http://bunkat.github.io/later/

Gestione di eventi complessi e dipendenze tra tali programmi: http://bunkat.github.io/schedule/

Fondamentalmente, crei le regole e poi chiedi alla lib di calcolare i successivi N eventi ricorrenti (specificando o meno un intervallo di date). Le regole possono essere analizzate / serializzate per salvarle nel modello.

Se si dispone di un evento ricorrente e si desidera modificare solo una ricorrenza, è possibile utilizzare la funzione exception () per chiudere un determinato giorno e quindi aggiungere un nuovo evento modificato per questa voce.

La libreria supporta schemi, fusi orari e persino cronologia molto complessi.


0

Archivia gli eventi come ripetuti e visualizzali dinamicamente, tuttavia consenti all'evento ricorrente di contenere un elenco di eventi specifici che potrebbero sovrascrivere le informazioni predefinite in un giorno specifico.

Quando si esegue una query sull'evento ricorrente, è possibile verificare una sostituzione specifica per quel giorno.

Se un utente apporta modifiche, puoi chiedere se desidera aggiornare per tutte le istanze (dettagli predefiniti) o solo quel giorno (crea un nuovo evento specifico e aggiungilo all'elenco).

Se un utente chiede di eliminare tutte le ricorrenze di questo evento, hai anche l'elenco delle specifiche a portata di mano e puoi rimuoverle facilmente.

L'unico caso problematico sarebbe se l'utente desidera aggiornare questo evento e tutti gli eventi futuri. Nel qual caso dovrai dividere l'evento ricorrente in due. A questo punto potresti prendere in considerazione l'idea di collegare gli eventi ricorrenti in qualche modo in modo da poterli eliminare tutti.


0

Per i programmatori .NET che sono disposti a pagare alcune tasse di licenza, potresti trovare utile Aspose.Network ... include una libreria compatibile iCalendar per appuntamenti ricorrenti.


0

Memorizzi direttamente gli eventi nel formato iCalendar, che consente la ripetizione a tempo indeterminato, la localizzazione del fuso orario e così via.

È possibile archiviarli in un server CalDAV e quindi, quando si desidera visualizzare gli eventi, è possibile utilizzare l'opzione del report definito in CalDAV per chiedere al server di eseguire l'espansione degli eventi ricorrenti nel periodo visualizzato.

Oppure puoi archiviarli in un database da solo e utilizzare una sorta di libreria di analisi iCalendar per eseguire l'espansione, senza bisogno di PUT / GET / REPORT per parlare con un server CalDAV back-end. Questo è probabilmente più lavoro - sono sicuro che i server CalDAV nascondono complessità da qualche parte.

Avere gli eventi in formato iCalendar probabilmente renderà le cose più semplici a lungo termine poiché le persone vorranno sempre che vengano esportate per mettere comunque in altro software.


0

Ho semplicemente implementato questa funzione! La logica è la seguente, prima sono necessarie due tabelle. Regola Tabella memorizza eventi generali o di riciclo. ItemTable è gli eventi del ciclo memorizzati. Ad esempio, quando si crea un evento ciclico, l'ora di inizio per il 6 novembre 2015, l'ora di fine per il 6 dicembre (o per sempre), scorrono per una settimana. Inserisci i dati in una RuleTable, i campi sono i seguenti:

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.

Ora vuoi interrogare i dati dal 20 novembre al 20 dicembre. È possibile scrivere una funzione RecurringEventBE (inizio lungo, fine lungo), in base all'ora di inizio e di fine, WeekLy, è possibile calcolare la raccolta desiderata, <ciclo A11.20, ciclo A 11,27, ciclo A 12,4 ......>. Oltre al 6 novembre, e il resto, l'ho chiamato un evento virtuale. Quando l'utente cambia il nome di un evento virtuale dopo (ad esempio cycleA11.27), si inseriscono i dati in una ItemTable. I campi sono i seguenti:

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

Nella funzione RecurringEventBE (inizio lungo, fine lungo), usi questi dati relativi all'evento virtuale (ciclo B11.27) scusami per il mio inglese, ho provato.

Questo è il mio Evento ricorrente ing

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

Cosa succede se si dispone di un appuntamento ricorrente senza data di fine? Per quanto economico sia lo spazio, non hai spazio infinito, quindi la Soluzione 2 è un non-avviatore lì ...

Vorrei suggerire che "nessuna data di fine" può essere risolta in una data di fine alla fine del secolo. Anche per un evento giornaliero la quantità di spazio rimane economica.


7
Quando dimenticheremo le lezioni di y2k ... :)
Ian Mercer,

10
Supponiamo di avere 1000 utenti, ognuno con un paio di eventi giornalieri. 3 eventi × 1000 utenti × 365 giorni × (2100-2011 = 89 anni) = 97,5 milioni di record. Invece di 3000 "piani". Um ...
Andy Mikhaylenko,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.