it-swarm-pt.tech

Qual é a melhor maneira de modelar eventos recorrentes em um aplicativo de calendário?

Estou desenvolvendo um aplicativo de calendário de grupo que precisa suportar eventos recorrentes, mas todas as soluções que desenvolvi para lidar com esses eventos parecem ser um truque. Posso limitar o quão à frente se pode olhar e depois gerar todos os eventos de uma só vez. Ou posso armazenar os eventos como repetidos e exibi-los dinamicamente quando se olha para a frente no calendário, mas precisarei convertê-los em um evento normal se alguém quiser alterar os detalhes de uma instância específica do evento.

Tenho certeza de que há uma maneira melhor de fazer isso, mas ainda não a encontrei. Qual é a melhor maneira de modelar eventos recorrentes, onde você pode alterar detalhes ou excluir instâncias de eventos particulares?

(Estou usando Ruby, mas por favor não deixe que isso restrinja sua resposta. Se houver uma biblioteca específica para Ruby ou algo assim, é bom saber.)

210
Clinton N. Dreisbach

Eu usaria um conceito de 'link' para todos os futuros eventos recorrentes. Eles são exibidos dinamicamente no calendário e são vinculados a um único objeto de referência. Quando os eventos ocorrem, o link é interrompido e o evento se torna uma instância independente. Se você tentar editar um evento recorrente, avise para alterar todos os itens futuros (ou seja, alterar uma referência vinculada única) ou altere apenas essa instância (nesse caso, converta-a em uma instância independente e faça a alteração). O último caso é um pouco problemático, já que você precisa acompanhar sua lista recorrente de todos os eventos futuros que foram convertidos em instância única. Mas isso é totalmente possível.

Então, em essência, temos duas classes de eventos - instâncias únicas e eventos recorrentes.

84
user16068

Martin Fowler - Eventos Recorrentes para Calendários contém alguns insights e padrões interessantes.

Runt gem implementa esse padrão.

53
Daniel Maurić

Pode haver muitos problemas com eventos recorrentes, deixe-me destacar alguns que eu conheço.

Solução 1 - sem instâncias

Armazene o compromisso original + dados de recorrência, não armazene todas as instâncias.

Problemas:

  • Você terá que calcular todas as instâncias em uma janela de data quando precisar delas, caro
  • Não é possível lidar com exceções (isto é, excluir uma das instâncias ou movê-la, ou melhor, você não pode fazer isso com essa solução)

Solução 2 - armazenar instâncias

Armazene tudo de 1, mas também todas as instâncias, vinculadas de volta ao compromisso original.

Problemas:

  • Leva muito espaço (mas o espaço é barato, tão pequeno)
  • Exceções devem ser tratadas com elegância, especialmente se você voltar e editar o compromisso original depois de fazer uma exceção. Por exemplo, se você mover a terceira instância um dia para frente, e se voltar e editar a hora do compromisso original, insira novamente outra no dia original e deixe a outra movida? Desvincular o movido? Tente mudar o movido apropriadamente?

Claro, se você não vai fazer exceções, então a solução deve estar bem, e você basicamente escolhe um cenário de troca de tempo/espaço.

32
Lasse Vågsæther Karlsen

Eu estou trabalhando com o seguinte:

e uma gem em progresso que extende formtastic com um tipo de entrada: recorrente (form.schedule :as => :recurring), que renderiza uma interface parecida com o iCal e um before_filter para serializar a view em um objeto IceCube novamente, guetaneamente.

Minha ideia é tornar incrivelmente fácil adicionar atributos recorrentes a um modelo e conectá-lo facilmente na exibição. Tudo em algumas linhas.


Então, o que isso me dá? Atributos indexados, editáveis ​​e recorrentes.

events armazena uma instância de um único dia e é usado na exibição de calendário/helper dizendo que task.schedule armazena o objeto yaml'd IceCube, para que você possa fazer chamadas como: task.schedule.next_suggestion.

Recapitulação: Eu uso dois modelos, um plano, para a exibição de calendário e um atributo para a funcionalidade.

16
Vee

Você pode examinar as implementações do software iCalendar ou o próprio padrão (RFC 2445 RFC 5545 ). Os que vêm à mente rapidamente são os projetos Mozilla http://www.mozilla.org/projects/calendar/ Uma pesquisa rápida revela http://icalendar.rubyforge.org/ também.

Outras opções podem ser consideradas, dependendo de como você armazenará os eventos. Você está construindo seu próprio esquema de banco de dados? Usando algo baseado no iCalendar, etc.?

16
Kris Kumler

Eu desenvolvi vários aplicativos baseados em calendário e também criei um conjunto de componentes de calendário JavaScript reutilizáveis ​​que suportam a recorrência. Eu escrevi uma visão geral de como projetar para recorrência que pode ser útil para alguém. Embora existam alguns bits específicos da biblioteca que escrevi, a grande maioria dos conselhos oferecidos é geral para qualquer implementação de calendário.

Alguns dos pontos principais:

  • Armazene a recorrência usando o formato iCal RRULE - essa é uma roda que você realmente não quer reinventar
  • NÃO armazene eventos individuais recorrentes instâncias como linhas em seu banco de dados! Armazene sempre um padrão de recorrência.
  • Há muitas maneiras de projetar seu esquema de evento/exceção, mas um exemplo básico de ponto de partida é fornecido
  • Todos os valores de data/hora devem ser armazenados em UTC e convertidos em locais para exibição
  • A data final armazenada para um evento recorrente deve ser sempre a data de término do intervalo de recorrência (ou a "data máxima" da sua plataforma se recorrente "para sempre") e a duração do evento deve ser armazenado separadamente. Isso é para garantir uma maneira sadia de consultar eventos posteriormente.
  • Algumas discussões sobre como gerar instâncias de eventos e estratégias de edição de recorrência estão incluídas

É um tópico muito complicado com muitas, muitas abordagens válidas para implementá-lo. Eu vou dizer que eu realmente implementei recorrência várias vezes com sucesso, e eu seria cauteloso em tomar conselhos sobre este assunto de qualquer um que não tenha realmente feito isso.

13
Brian Moeskau

Estou usando o esquema do banco de dados conforme descrito abaixo para armazenar os parâmetros de recorrência

http://github.com/bakineggs/recurring_events_for

Então eu uso o runt para calcular dinamicamente as datas.

https://github.com/mlipper/runt

6
liangzan
  1. Mantenha o controle de uma regra de recorrência (provavelmente com base no iCalendar, per @ Kris K. ). Isso incluirá um padrão e um intervalo (a cada terceira terça-feira, por 10 ocorrências).
  2. Para quando você deseja editar/excluir uma ocorrência específica, acompanhe as datas de exceção para a regra de recorrência acima (datas em que o evento não ocorre como a regra especifica).
  3. Se você excluiu, é tudo o que precisa, se editou, criar outro evento e atribuir a ele um ID pai ao evento principal. Você pode optar por incluir todas as informações do evento principal neste registro ou se ele apenas mantém as alterações e herda tudo o que não muda.

Observe que, se você permitir que as regras de recorrência não terminem, é necessário pensar em como exibir sua quantidade infinita de informações.

Espero que ajude!

5
bdukes

Eu recomendo usar o poder da biblioteca de datas e a semântica do módulo de faixa do Ruby. Um evento recorrente é realmente um momento, um intervalo de datas (início e fim) e geralmente um único dia da semana. Usando data e alcance, você pode responder a qualquer pergunta:

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

Produz todos os dias do evento, incluindo o ano bissexto!

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

A partir dessas respostas, meio que peneirei uma solução. Eu realmente gosto da ideia do conceito de link. Eventos recorrentes podem ser uma lista vinculada, com a cauda sabendo sua regra de recorrência. Alterar um evento seria fácil, pois os links permaneceriam no lugar e a exclusão de um evento também é fácil. Basta desconectar um evento, excluí-lo e vincular novamente o evento antes e depois dele. Você ainda precisa consultar eventos recorrentes toda vez que alguém olha para um novo período de tempo que nunca foi examinado antes no calendário, mas por outro lado isso é bastante claro.

3
Clinton N. Dreisbach

Confira o artigo abaixo para três boas Ruby bibliotecas de data/hora. O ice_cube em particular parece uma escolha sólida para regras de recorrência e outras coisas que um calendário de eventos precisaria. http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

2
gokul.janga

Você pode armazenar os eventos como repetidos e, se uma instância específica foi editada, crie um novo evento com o mesmo ID de evento. Então, ao procurar o evento, pesquise todos os eventos com o mesmo ID de evento para obter todas as informações. Não tenho certeza se você rolou sua própria biblioteca de eventos ou se está usando uma já existente, portanto, pode não ser possível.

2
Vincent McNabb

Em javascript:

Como lidar com agendas recorrentes: http://bunkat.github.io/later/

Manipulando eventos e dependências complexas entre esses agendamentos: http://bunkat.github.io/schedule/

Basicamente, você cria as regras e então pede ao lib para calcular os próximos N eventos recorrentes (especificando um intervalo de datas ou não). As regras podem ser analisadas/serializadas para salvá-las em seu modelo.

Se você tiver um evento recorrente e desejar modificar apenas uma recorrência, poderá usar a função except () para descartar um dia específico e depois adicione um novo evento modificado para esta entrada.

A lib suporta padrões muito complexos, fusos horários e até eventos de criação de cronogramas.

1
Flavien Volken

Para programadores .NET que estão dispostos a pagar algumas taxas de licenciamento, você pode encontrar Aspose.Network útil ... ele inclui uma biblioteca compatível com iCalendar para compromissos recorrentes.

0
Shaul Behr

Armazene os eventos como repetidos e exiba-os dinamicamente, no entanto, permita que o evento recorrente contenha uma lista de eventos específicos que possam substituir as informações padrão em um dia específico.

Quando você consulta o evento recorrente, ele pode verificar uma substituição específica para esse dia.

Se um usuário fizer alterações, você poderá perguntar se ele deseja atualizar para todas as instâncias (detalhes padrão) ou apenas naquele dia (criar um novo evento específico e adicioná-lo à lista).

Se um usuário pedir para excluir todas as ocorrências deste evento, você também terá a lista de detalhes a serem entregues e poderá removê-los facilmente.

O único caso problemático seria se o usuário quiser atualizar esse evento e todos os eventos futuros. Nesse caso, você terá que dividir o evento recorrente em dois. Neste ponto, você pode querer considerar a vinculação de eventos recorrentes de alguma forma para poder excluí-los.

0
Andrew Johnson

Você armazena os eventos no formato iCalendar diretamente, o que permite repetição em aberto, localização em fuso horário e assim por diante.

Você pode armazená-los em um servidor CalDAV e, quando quiser exibir os eventos, pode usar a opção do relatório definido no CalDAV para solicitar que o servidor faça a expansão dos eventos recorrentes no período exibido.

Ou você pode armazená-los em um banco de dados e usar algum tipo de biblioteca de análise do iCalendar para fazer a expansão, sem precisar do PUT/GET/REPORT para falar com um servidor CalDAV de back-end. Isso provavelmente é mais trabalho - tenho certeza de que os servidores CalDAV escondem a complexidade em algum lugar.

Ter os eventos no formato iCalendar provavelmente tornará as coisas mais simples no longo prazo, já que as pessoas sempre desejarão que sejam exportadas para colocar outros softwares de qualquer maneira.

0
karora

Eu simplesmente implementei esse recurso! A lógica é a seguinte, primeiro você precisa de duas tabelas. RuleTable armazenar geral ou reciclar eventos paternos. ItemTable é um evento de ciclo armazenado. Por exemplo, quando você cria um evento cíclico, a hora de início para 6 de novembro de 2015, a hora de término para o dia 6 de dezembro (ou para sempre), ciclo por uma semana. Você insere dados em um RuleTable, os campos são os seguintes:

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.

Agora você deseja consultar dados de 20 de novembro a 20 de dezembro. Você pode escrever uma função RecurringEventBE (início longo, final longo), com base na hora inicial e final, WeekLy, você pode calcular a coleção desejada, <cycleA11.20, cycleA 11.27, cycleA 12.4 ......>. Além de 6 de novembro, e o resto eu o chamei de um evento virtual. Quando o usuário altera o nome de um evento virtual após (cycleA11.27, por exemplo), você insere um dado em uma ItemTable. Os campos são os seguintes:

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

Na função RecurringEventBE (long start, long end), você usa esses dados cobrindo o evento virtual (cycleB11.27) desculpe pelo meu inglês, tentei.

Este é o meu 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