在日历应用程序中对重复事件进行建模的最佳方法是什么?

发布于 2024-07-04 09:20:21 字数 1721 浏览 13 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

惟欲睡 2024-07-11 09:20:22

我已经简单地实现了这个功能! 逻辑如下,首先需要两张表。 RuleTable存储一般或回收父事件。 ItemTable是存储循环事件的。 例如,创建循环事件时,开始时间为2015年11月6日,结束时间为12月6日(或永远),循环一周。 您向RuleTable中插入数据,字段如下:

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

现在您要查询11月20日到12月20日的数据。 你可以写一个函数RecurringEventBE(长开始,长结束),根据起止时间,WeekLy,就可以计算出你想要的集合,< 循环A11.20、循环A 11.27、循环A 12.4......>。
除了11月6日,其余的我都称他为虚拟事件。 当用户在之后更改虚拟事件的名称(例如,cycleA11.27)时,您可以将数据插入到 ItemTable 中。 字段如下:

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

在函数 RecurringEventBE(长开始,长结束)中,您使用此数据覆盖虚拟事件(cycleB11.27)
对不起我的英语,我试过了。

这是我的重复事件BE:

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;
}   

I have Simply implemented this feature! Logic is as follows, first you need two tables. RuleTable store general or recycle paternal events. ItemTable is stored cycle events. For example, when you create a cyclic event, the start time for 6 November 2015, the end time for the December 6 (or forever), cycle for one week. You insert data into a RuleTable, fields are as follows:

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.

Now you want to query November 20 to December 20 data. You can write a function RecurringEventBE (long start, long end), based on the starting and ending time, WeekLy, you can calculate the collection you want, < cycleA11.20, cycleA 11.27, cycleA 12.4 ......>.
In addition to November 6, and the rest I called him a virtual event. When the user changes a virtual event' name after (cycleA11.27 for example), you insert a data into a ItemTable. Fields are as follows:

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

In function RecurringEventBE (long start, long end), you use this data covering virtual event (cycleB11.27)
sorry about my english, I tried.

This is my 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;
}   
灰色世界里的红玫瑰 2024-07-11 09:20:22

如果您有一个没有结束日期的定期约会怎么办? 尽管空间很便宜,但你没有无限的空间,所以解决方案 2 是行不通的......

我可以建议“没有结束日期”可以解决为本世纪末的结束日期。 即使是日常活动,空间仍然很便宜。

What if you have a recurring appointment with no end date? As cheap as space is, you don't have infinite space, so Solution 2 is a non-starter there...

May I suggest that "no end date" can be resolved to an end date at the end of the century. Even for a dayly event the amount of space remains cheap.

下壹個目標 2024-07-11 09:20:22

我建议使用日期库的强大功能和 ruby​​ 范围模块的语义。 重复事件实际上是一个时间、一个日期范围(开始和结束)并且通常是一周中的某一天。 使用日期和 您可以回答任何问题的范围:

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

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

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

产生事件的所有日期,包括闰年!

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

I'd recommend using the power of the date library and the semantics of the range module of ruby. A recurring event is really a time, a date range (a start & end) and usually a single day of the week. Using date & range you can answer any question:

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

Produces all days of the event, including the leap year!

# =>"[\"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\"]"
池木 2024-07-11 09:20:22

您可以将事件存储为重复事件,如果编辑了特定实例,则使用相同的事件 ID 创建一个新事件。 然后在查找事件时,搜索具有相同事件ID的所有事件,即可获取所有信息。 我不确定您是否推出了自己的事件库,或者您是否正在使用现有的事件库,因此这可能是不可能的。

You could store the events as repeating, and if a particular instance was edited, create a new event with the same event ID. Then when looking up the event, search for all events with the same event ID to get all the information. I'm not sure if you rolled your own event library, or if you're using an existing one so it may not be possible.

野鹿林 2024-07-11 09:20:22

查看下面的文章,了解三个优秀的 Ruby 日期/时间库。
对于重复规则和事件日历所需的其他内容来说,ice_cube 似乎是一个可靠的选择。
http://www.rubyinside.com /3-new-date-and-time-libraries-for-rubyists-3238.html

Check the article below for three good ruby date/time libraries.
ice_cube in particular seems a solid choice for recurrence rules and other stuff that an event calendar would need.
http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

只为守护你 2024-07-11 09:20:22

在 javascript 中:

处理重复计划:
http://bunkat.github.io/later/

处理复杂事件和这些计划之间的依赖关系:
http://bunkat.github.io/schedule/

基本上,您创建规则,然后询问lib 来计算接下来的 N 个重复事件(指定或不指定日期范围)。 可以解析/序列化规则以将它们保存到您的模型中。

如果您有一个重复事件并且只想修改一个重复事件,您可以使用 except() 函数取消特定的一天,然后为此条目添加一个新的修改事件。强>

该库支持非常复杂的模式、时区甚至定时事件。

In javascript:

Handling recurring schedules:
http://bunkat.github.io/later/

Handling complex events and dependencies between those schedules:
http://bunkat.github.io/schedule/

Basically, you create the rules then you ask the lib to compute the next N recurring events (specifying a date range or not). The rules can be parsed / serialised for saving them into your model.

If you have a recurring event and would like to modify only one recurrence you can use the except() function to dismiss a particular day and then add a new modified event for this entry.

The lib supports very complex patterns, timezones and even croning events.

尘世孤行 2024-07-11 09:20:22

从这些答案中,我筛选出了一个解决方案。 我真的很喜欢链接概念。 重复事件可以是一个链表,尾部知道其重复规则。 更改一个事件将很容易,因为链接保持不变,删除事件也很容易 - 您只需取消链接事件,将其删除,然后重新链接事件之前和之后的事件。 每当有人查看日历上以前从未查看过的新时间段时,您仍然必须查询重复事件,但除此之外,这非常干净。

From these answers, I've sort of sifted out a solution. I really like the idea of the link concept. Recurring events could be a linked list, with the tail knowing its recurrence rule. Changing one event would then be easy, because the links stay in place, and deleting an event is easy as well - you just unlink an event, delete it, and re-link the event before and after it. You still have to query recurring events every time someone looks at a new time period never been looked at before on the calendar, but otherwise this is pretty clean.

转身以后 2024-07-11 09:20:22

对于准备支付一些许可费用的 .NET 程序员,您可能会发现 Aspose.Network 有用...它包含一个用于定期约会的 iCalendar 兼容库。

For .NET programmers who are prepared to pay some licensing fees, you might find Aspose.Network useful... it includes an iCalendar compatible library for recurring appointments.

撩人痒 2024-07-11 09:20:22

您可以直接以 iCalendar 格式存储事件,这允许开放式重复、时区本地化等。

您可以将这些存储在 CalDAV 服务器中,然后当您想要显示事件时,可以使用 CalDAV 中定义的报告选项来要求服务器在查看的时间段内扩展重复事件。

或者您可以自己将它们存储在数据库中,并使用某种 iCalendar 解析库来进行扩展,而不需要 PUT/GET/REPORT 与后端 CalDAV 服务器对话。 这可能需要更多工作 - 我确信 CalDAV 服务器将复杂性隐藏在某个地方。

从长远来看,以 iCalendar 格式保存事件可能会让事情变得更简单,因为人们总是希望将它们导出以放入其他软件中。

You store the events in iCalendar format directly, which allows for open-ended repetition, time-zone localisation and so forth.

You could store these in a CalDAV server and then when you want to display the events you can use the option of the report defined in CalDAV to ask the server to do the expansion of the recurring events across the viewed period.

Or you could store them in a database yourself and use some kind of iCalendar parsing library to do the expansion, without needing the PUT/GET/REPORT to talk to a backend CalDAV server. This is probably more work - I'm sure CalDAV servers hide complexity somewhere.

Having the events in iCalendar format will probably make things simpler in the long run as people will always want them to be exported for putting in other software anyway.

用心笑 2024-07-11 09:20:22

将事件存储为重复事件并动态显示它们,但是允许重复事件包含可以覆盖特定日期的默认信息的特定事件列表。

当您查询重复事件时,它可以检查当天的特定覆盖。

如果用户进行更改,那么您可以询问他是否想要更新所有实例(默认详细信息)或仅更新当天(创建新的特定事件并将其添加到列表中)。

如果用户要求删除此事件的所有重复发生,您还可以掌握详细信息列表,并且可以轻松删除它们。

唯一有问题的情况是用户想要更新此事件和所有未来的事件。 在这种情况下,您必须将重复事件分成两部分。 此时,您可能需要考虑以某种方式链接重复发生的事件,以便将它们全部删除。

Store the events as repeating and dynamically display them, however allow the recurring event to contain a list of specific events that could override the default information on a specific day.

When you query the recurring event it can check for a specific override for that day.

If a user makes changes, then you can ask if he wants to update for all instances (default details) or just that day (make a new specific event and add it to the list).

If a user asks to delete all recurrences of this event you also have the list of specifics to hand and can remove them easily.

The only problematic case would be if the user wants to update this event and all future events. In which case you'll have to split the recurring event into two. At this point you may want to consider linking recurring events in some way so you can delete them all.

小瓶盖 2024-07-11 09:20:21
  1. 跟踪重复规则(可能基于 iCalendar,根据 @克里斯·K.)。 这将包括一个模式和一个范围(每个第三个星期二,出现 10 次)。
  2. 当您想要编辑/删除特定事件时,请跟踪上述重复规则的例外日期(按照规则指定,事件发生的日期)。
  3. 如果您删除了,这就是您所需要的,如果您进行了编辑,则创建另一个事件,并为其指定一个设置为主事件的父 ID。 您可以选择是否在此记录中包含所有主要事件的信息,或者仅保留更改并继承所有未更改的内容。

请注意,如果您允许不结束的重复规则,您必须考虑如何显示您现在无限量的信息。

希望有帮助!

  1. Keep track of a recurrence rule (probably based on iCalendar, per @Kris K.). This will include a pattern and a range (Every third Tuesday, for 10 occurrences).
  2. For when you want to edit/delete a specific occurrence, keep track of exception dates for the above recurrence rule (dates where the event doesn't occur as the rule specifies).
  3. If you deleted, that's all you need, if you edited, create another event, and give it a parent ID set to the main event. You can choose whether to include all of the main event's information in this record, or if it only holds the changes and inherits everything that doesn't change.

Note that if you allow recurrence rules that don't end, you have to think about how to display your now infinite amount of information.

Hope that helps!

忆沫 2024-07-11 09:20:21

我使用如下所述的数据库架构来存储重复参数

http://github.com/bakineggs/recurring_events_for

然后我使用 runt 动态计算日期。

https://github.com/mlipper/runt

I'm using the database schema as described below to store the recurrence parameters

http://github.com/bakineggs/recurring_events_for

Then I use runt to dynamically calculate the dates.

https://github.com/mlipper/runt

硬不硬你别怂 2024-07-11 09:20:21

我正在使用以下内容:

和正在开发的 gem,它使用输入类型扩展了 formattastic :recurring (form.schedule :as => :recurring),它呈现类似 iCal 的界面和 before_filter 将视图序列化为 IceCube > 再次反对,贫民区。

我的想法是让向模型添加重复属性并在视图中轻松连接它变得难以置信的简单。 一切都在几行中。


那么这给了我什么呢? 索引、可编辑、重复属性。

events 存储单日实例,并在日历视图/帮助器中使用
假设 task.schedule 存储 yaml 的 IceCube 对象,因此您可以执行如下调用:task.schedule.next_suggestion

回顾:我使用两种模型,一种是平面模型,用于日历显示,另一种是用于功能的属性模型。

I'm working with the following:

and a gem in progress that extends formtastic with an input type :recurring (form.schedule :as => :recurring), which renders an iCal-like interface and a before_filter to serialize the view into an IceCube object again, ghetto-ly.

My idea is to make it incredibility easy to add recurring attributes to a model and connect it easily in the view. All in a couple of lines.


So what does this give me? Indexed, Edit-able, Recurring attributes.

events stores a single day instance, and is used in the calendar view/helper
say task.schedule stores the yaml'd IceCube object, so you can do calls like : task.schedule.next_suggestion.

Recap: I use two models, one flat, for the calendar display, and one attribute'd for the functionality.

撧情箌佬 2024-07-11 09:20:21

您可能需要查看 iCalendar 软件实现或标准本身 (RFC 2445 RFC 5545)。
很快我就会想到 Mozilla 项目 http://www.mozilla.org/projects/calendar / 快速搜索也会显示http://icalendar.rubyforge.org/

根据您要如何存储事件,可以考虑其他选项。 您正在构建自己的数据库架构吗? 使用基于 iCalendar 的东西等?

You may want to look at iCalendar software implementations or the standard itself (RFC 2445 RFC 5545).
Ones to come to mind quickly are the Mozilla projects http://www.mozilla.org/projects/calendar/ A quick search reveals http://icalendar.rubyforge.org/ as well.

Other options can be considered depending on how you're going to store the events. Are you building your own database schema? Using something iCalendar-based, etc.?

如果没结果 2024-07-11 09:20:21

我会为所有未来重复发生的事件使用“链接”概念。 它们动态显示在日历中并链接回单个参考对象。 当事件发生时,链接就会断开,事件就会成为一个独立的实例。 如果您尝试编辑重复事件,则提示更改所有未来项目(即更改单个链接引用)或仅更改该实例(在这种情况下将其转换为独立实例,然后进行更改)。 后一种情况有点问题,因为您需要在循环列表中跟踪所有转换为单个实例的未来事件。 但是,这是完全可行的。

因此,本质上有两类事件——单一实例和重复事件。

I would use a 'link' concept for all future recurring events. They are dynamically displayed in the calendar and link back to a single reference object. When events have taken place the link is broken and the event becomes a standalone instance. If you attempt to edit a recurring event then prompt to change all future items (i.e. change single linked reference) or change just that instance (in which case convert this to a standalone instance and then make change). The latter cased is slightly problematic as you need to keep track in your recurring list of all future events that were converted to single instance. But, this is entirely do-able.

So, in essence, have 2 classes of events - single instances and recurring events.

眼中杀气 2024-07-11 09:20:21

我开发了多个基于日历的应用程序,还编写了一组支持重复的可重用 JavaScript 日历组件。 我还写了一篇关于如何设计重复性的概述这应该会有帮助。 虽然有一些特定于我编写的库的内容,但提供的绝大多数建议对于任何日历实现都是通用的。

一些要点:

  • 使用 iCal RRULE 格式 存储重复情况 - 这就是您确实不想重新发明一个轮子
  • 不要将单个重复事件实例存储为数据库中的行! 始终存储重复模式。
  • 设计事件/异常模式的方法有很多,但提供了一个基本的起点示例
  • 所有日期/时间值都应存储在 UTC 中并转换为本地显示
  • 为重复事件存储的结束日期应始终为 重复范围的结束日期(如果“永远”重复,则为平台的“最大日期”)和事件持续时间应单独存储。 这是为了确保以后以合理的方式查询事件。 请阅读链接的文章以了解有关此内容的更多详细信息。
  • 包括一些关于生成事件实例和重复编辑策略的讨论,

这是一个非常复杂的主题,有许多可行的实施策略。 我想说的是,我实际上已经成功地实施了多次重复,并且我会谨慎地听取任何尚未实际实施过的人关于这个问题的建议。

I have developed multiple calendar-based applications, and also authored a set of reusable JavaScript calendar components that support recurrence. I also wrote up an overview of how to design for recurrence that should be helpful. While there are a few bits that are specific to the library I wrote, the vast majority of the advice offered is general to any calendar implementation.

Some of the key points:

  • Store recurrence using the iCal RRULE format -- that's one wheel you really don't want to reinvent
  • Do NOT store individual recurring event instances as rows in your database! Always store a recurrence pattern.
  • There are many ways to design your event/exception schema, but a basic starting point example is provided
  • All date/time values should be stored in UTC and converted to local for display
  • The end date stored for a recurring event should always be the end date of the recurrence range (or your platform's "max date" if recurring "forever") and the event duration should be stored separately. This is to ensure a sane way of querying for events later. Read the linked article for more details about this.
  • Some discussion around generating event instances and recurrence editing strategies is included

It's a really complicated topic with many implementation strategies that could work. I will say that I've actually implemented recurrence several times successfully, and I would be wary of taking advice on this subject from anyone who hasn't actually done it.

逆流 2024-07-11 09:20:21

重复发生的事件可能会带来很多问题,让我重点介绍一些我所知道的问题。

解决方案 1 - 无实例

存储原始约会 + 重复数据,不存储所有实例。

问题:

  • 当您需要时,您必须计算日期窗口中的所有实例,成本高昂
  • 无法处理异常(即,您删除其中一个实例,或移动它,或者更确切地说,您不能用此方法执行此操作)解决方案) 解决

方案 2 - 存储实例

存储 1 中的所有内容以及所有实例,链接回原始约会。

问题:

  • 占用大量空间(但空间很便宜,所以很小)
  • 必须妥善处理例外情况,特别是如果您在例外后返回并编辑原始约会。 例如,如果您将第三个实例向前移动一天,如果您返回并编辑原始约会的时间,在原始日期重新插入另一个实例并保留移动的约会,会怎么样? 取消已移动的链接? 尝试适当改变移动的那个?

当然,如果您不打算做例外,那么任何一个解决方案都应该没问题,并且您基本上可以从时间/空间权衡场景中进行选择。

There can be many problems with recurring events, let me highlight a few that I know of.

Solution 1 - no instances

Store original appointment + recurrence data, do not store all the instances.

Problems:

  • You'll have to calculate all the instances in a date window when you need them, costly
  • Unable to handle exceptions (ie. you delete one of the instances, or move it, or rather, you can't do this with this solution)

Solution 2 - store instances

Store everything from 1, but also all the instances, linked back to the original appointment.

Problems:

  • Takes a lot of space (but space is cheap, so minor)
  • Exceptions must be handled gracefully, especially if you go back and edit the original appointment after making an exception. For instance, if you move the third instance one day forward, what if you go back and edit the time of the original appointment, re-insert another on the original day and leave the moved one? Unlink the moved one? Try to change the moved one appropriately?

Of course, if you're not going to do exceptions, then either solution should be fine, and you basically choose from a time/space trade off scenario.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文