Pregunta

Estoy creando una aplicación de calendario grupal que necesita admitir eventos recurrentes, pero todas las soluciones que se me han ocurrido para manejar estos eventos parecen un truco.Puedo limitar qué tan lejos se puede mirar y luego generar todos los eventos a la vez.O puedo almacenar los eventos como repetitivos y mostrarlos dinámicamente cuando uno mira hacia adelante en el calendario, pero tendré que convertirlos en un evento normal si alguien quiere cambiar los detalles en una instancia particular del evento.

Estoy seguro de que hay una mejor manera de hacer esto, pero aún no la he encontrado.¿Cuál es la mejor manera de modelar eventos recurrentes, donde se pueden cambiar detalles o eliminar instancias de eventos particulares?

(Estoy usando Ruby, pero no dejes que eso limite tu respuesta.Sin embargo, si hay una biblioteca específica de Ruby o algo así, es bueno saberlo).

¿Fue útil?

Solución

Usaría un concepto de "vínculo" para todos los eventos recurrentes futuros.Se muestran dinámicamente en el calendario y se vinculan a un único objeto de referencia.Cuando los eventos han tenido lugar, el vínculo se rompe y el evento se convierte en una instancia independiente.Si intenta editar un evento recurrente, solicite cambiar todos los elementos futuros (es decir,cambiar la referencia vinculada única) o cambiar solo esa instancia (en cuyo caso conviértala en una instancia independiente y luego realice el cambio).Este último caso es un poco problemático ya que necesita realizar un seguimiento en su lista recurrente de todos los eventos futuros que se convirtieron a una instancia única.Pero esto es totalmente factible.

Entonces, en esencia, tenemos 2 clases de eventos: instancias únicas y eventos recurrentes.

Otros consejos

Martin Fowler - Eventos recurrentes para calendarios contiene algunas ideas y patrones interesantes.

Enano gem implementa este patrón.

Puede haber muchos problemas con los eventos recurrentes, permítanme resaltar algunos que conozco.

Solución 1: sin instancias

Almacene los datos de la cita original + la recurrencia, no almacene todas las instancias.

Problemas:

  • Tendrás que calcular todas las instancias en una ventana de fecha cuando las necesites, lo cual es costoso.
  • No se pueden manejar excepciones (es decir.eliminas una de las instancias, o la mueves, o mejor dicho, no puedes hacer esto con esta solución)

Solución 2: almacenar instancias

Almacene todo, desde 1, pero también todas las instancias, vinculadas a la cita original.

Problemas:

  • Ocupa mucho espacio (pero el espacio es barato, por lo que es mínimo)
  • Las excepciones se deben manejar con elegancia, especialmente si regresa y edita la cita original después de hacer una excepción.Por ejemplo, si adelanta la tercera instancia un día, ¿qué sucede si regresa y edita la hora de la cita original, vuelve a insertar otra en el día original y deja la que se movió?¿Desvincular el movido?¿Intenta cambiar el movido adecuadamente?

Por supuesto, si no va a hacer excepciones, entonces cualquiera de las soluciones debería estar bien y, básicamente, elige entre un escenario de compensación de tiempo/espacio.

Es posible que desee consultar las implementaciones del software iCalendar o el estándar en sí (RFC 2445 RFC 5545).Los que me vienen a la mente rápidamente son los proyectos de Mozilla. http://www.mozilla.org/projects/calendar/ Una búsqueda rápida revela http://icalendar.rubyforge.org/ también.

Se pueden considerar otras opciones dependiendo de cómo vayas a almacenar los eventos.¿Está creando su propio esquema de base de datos?¿Usas algo basado en iCalendar, etc.?

Estoy trabajando con lo siguiente:

y una gema en progreso que extiende formtastic con un tipo de entrada: recurrente (form.schedule :as => :recurring), que presenta una interfaz similar a iCal y un before_filter para serializar la vista en un IceCube objeto de nuevo, gueto-ly.

Mi idea es hacer increíblemente fácil agregar atributos recurrentes a un modelo y conectarlo fácilmente en la vista.Todo en un par de líneas.


Entonces, ¿qué me aporta esto?Atributos indexados, editables y recurrentes.

events almacena una instancia de un solo día, y se usa en la vista del calendario/ayudante task.schedule almacena el yaml'd IceCube objeto, por lo que puedes hacer llamadas como: task.schedule.next_suggestion.

Resumen:Utilizo dos modelos, uno plano, para la visualización del calendario y otro con atributos para la funcionalidad.

He desarrollado múltiples aplicaciones basadas en calendario y también he sido autor de un conjunto de componentes de calendario JavaScript reutilizables que admiten la recurrencia.Escribí una descripción general de cómo diseñar para la recurrencia eso podría ser útil para alguien.Si bien hay algunas partes que son específicas de la biblioteca que escribí, la gran mayoría de los consejos ofrecidos son generales para cualquier implementación de calendario.

Algunos de los puntos clave:

  • Almacenar la recurrencia usando el Formato iCal RRULE -- esa es una rueda que realmente no quieres reinventar
  • NO almacene eventos recurrentes individuales instancias como filas en su base de datos!Almacene siempre un patrón de recurrencia.
  • Hay muchas maneras de diseñar su esquema de evento/excepción, pero se proporciona un ejemplo de punto de partida básico.
  • Todos los valores de fecha/hora deben almacenarse en UTC y convertirse a local para su visualización.
  • La fecha de finalización almacenada para un evento recurrente siempre debe ser la fecha de finalización del rango de recurrencia (o la "fecha máxima" de su plataforma si es recurrente "para siempre") y la duración del evento deben almacenarse por separado.Esto es para garantizar una forma sensata de consultar eventos más adelante.
  • Se incluye alguna discusión sobre la generación de instancias de eventos y estrategias de edición recurrente.

Es un tema realmente complicado con muchos, muchos enfoques válidos para implementarlo.Diré que en realidad he implementado la recurrencia varias veces con éxito, y desconfiaría de recibir consejos sobre este tema de alguien que no lo haya hecho.

Estoy usando el esquema de la base de datos como se describe a continuación para almacenar los parámetros de recurrencia.

http://github.com/bakineggs/recurring_events_for

Luego uso runt para calcular dinámicamente las fechas.

https://github.com/mlipper/runt

  1. Realice un seguimiento de una regla de recurrencia (probablemente basada en iCalendar, por @Kris K.).Esto incluirá un patrón y un rango (cada tercer martes, durante 10 apariciones).
  2. Cuando desee editar/eliminar una ocurrencia específica, realice un seguimiento de las fechas de excepción para la regla de recurrencia anterior (fechas en las que ocurrió el evento). no ocurrir como lo especifica la regla).
  3. Si lo eliminó, eso es todo lo que necesita; si lo editó, cree otro evento y asígnele una ID principal configurada para el evento principal.Puede elegir si desea incluir toda la información del evento principal en este registro o si solo contiene los cambios y hereda todo lo que no cambia.

Tenga en cuenta que si permite reglas de recurrencia que no terminan, debe pensar en cómo mostrar su cantidad ahora infinita de información.

¡Espero que ayude!

Recomiendo usar el poder de la biblioteca de fechas y la semántica del módulo de rango de Ruby.Un evento recurrente es en realidad una hora, un rango de fechas (un inicio y un final) y, por lo general, un solo día de la semana.Usando fecha y rango puedes responder cualquier pregunta:

#!/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 todos los días del evento, incluido el año bisiesto!

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

A partir de estas respuestas, en cierto modo he seleccionado una solución.Realmente me gusta la idea del concepto de enlace.Los eventos recurrentes podrían ser una lista vinculada, y la cola conoce su regla de recurrencia.Entonces, cambiar un evento sería fácil, porque los enlaces permanecen en su lugar y eliminar un evento también es fácil: simplemente desvincula un evento, lo elimina y vuelve a vincular el evento antes y después de él.Todavía tienes que consultar eventos recurrentes cada vez que alguien mira un nuevo período de tiempo que nunca antes se había visto en el calendario, pero por lo demás, esto es bastante limpio.

Puede almacenar los eventos como repetidos y, si se editó una instancia en particular, crear un nuevo evento con el mismo ID de evento.Luego, cuando busque el evento, busque todos los eventos con el mismo ID de evento para obtener toda la información.No estoy seguro de si implementó su propia biblioteca de eventos o si está utilizando una existente, por lo que es posible que no sea posible.

Consulte el artículo siguiente para conocer tres buenas bibliotecas de fecha/hora de Ruby.ice_cube en particular parece una opción sólida para las reglas de recurrencia y otras cosas que necesitaría un calendario de eventos.http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

En javascript:

Manejo de horarios recurrentes:http://bunkat.github.io/later/

Manejo de eventos complejos y dependencias entre esos horarios:http://bunkat.github.io/schedule/

Básicamente, usted crea las reglas y luego le pide a la biblioteca que calcule los siguientes N eventos recurrentes (especificando un rango de fechas o no).Las reglas se pueden analizar/serializar para guardarlas en su modelo.

Si tiene un evento recurrente y desea modificar solo una recurrencia, puede utilizar el excepto() función para descartar un día en particular y luego agregar un nuevo evento modificado para esta entrada.

La biblioteca admite patrones, zonas horarias e incluso eventos croning muy complejos.

Almacene los eventos como repetidos y muéstrelos dinámicamente; sin embargo, permita que el evento recurrente contenga una lista de eventos específicos que podrían anular la información predeterminada en un día específico.

Cuando consulta el evento recurrente, puede verificar si hay una anulación específica para ese día.

Si un usuario realiza cambios, puede preguntarle si desea actualizar para todas las instancias (detalles predeterminados) o solo ese día (crear un nuevo evento específico y agregarlo a la lista).

Si un usuario solicita eliminar todas las recurrencias de este evento, también tendrá a mano la lista de detalles específicos y podrá eliminarlos fácilmente.

El único caso problemático sería si el usuario desea actualizar este evento y todos los eventos futuros.En cuyo caso tendrás que dividir el evento recurrente en dos.En este punto, es posible que desee considerar vincular eventos recurrentes de alguna manera para poder eliminarlos todos.

Para los programadores .NET que estén dispuestos a pagar algunas tarifas de licencia, es posible que encuentren Aspose.Red útil...incluye una biblioteca compatible con iCalendar para citas recurrentes.

Los eventos se almacenan directamente en formato iCalendar, lo que permite la repetición abierta, la localización de la zona horaria, etc.

Puede almacenarlos en un servidor CalDAV y luego, cuando desee mostrar los eventos, puede usar la opción del informe definido en CalDAV para pedirle al servidor que expanda los eventos recurrentes durante el período visto.

O puede almacenarlos usted mismo en una base de datos y usar algún tipo de biblioteca de análisis de iCalendar para realizar la expansión, sin necesidad de PUT/GET/REPORT para comunicarse con un servidor CalDAV backend.Probablemente esto requiera más trabajo; estoy seguro de que los servidores CalDAV ocultan la complejidad en alguna parte.

Tener los eventos en formato iCalendar probablemente simplificará las cosas a largo plazo, ya que la gente siempre querrá que se exporten para instalar otro software de todos modos.

¡Simplemente he implementado esta característica!La lógica es la siguiente, primero necesitas dos tablas.RuleTable almacena eventos paternos generales o los recicla.ItemTable son eventos de ciclo almacenados.Por ejemplo, cuando crea un evento cíclico, la hora de inicio es el 6 de noviembre de 2015, la hora de finalización es el 6 de diciembre (o para siempre), el ciclo dura una semana.Inserta datos en una RuleTable, los campos son los siguientes:

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.

Ahora desea consultar los datos del 20 de noviembre al 20 de diciembre.Puede escribir una función RecurringEventBE (inicio largo, final largo), según la hora de inicio y finalización, WeekLy, puede calcular la colección que desea, <cicloA11.20, cicloA 11.27, cicloA 12.4...>.Además del 6 de noviembre y el resto lo llamé evento virtual.Cuando el usuario cambia el nombre de un evento virtual después (cicloA11.27, por ejemplo), inserta datos en una ItemTable.Los campos son los siguientes:

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

En la función recurringEventBe (comienzo largo, extremo largo), utiliza estos datos que cubren el evento virtual (cicloB11.27) perdón por mi inglés, lo intenté.

Este es mi 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;
}   

¿Qué pasa si tienes una cita recurrente sin fecha de finalización?Por más barato que sea el espacio, no tienes espacio infinito, por lo que la Solución 2 no es viable allí...

Permítaseme sugerir que la expresión "sin fecha de finalización" se puede resolver con una fecha de finalización a finales de siglo.Incluso para un evento diario, la cantidad de espacio sigue siendo barata.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top