Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 什么';在日历应用程序中对重复事件建模的最佳方法是什么?_Ruby_Algorithm_Calendar_Data Modeling_Recurrence - Fatal编程技术网

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

Ruby 什么';在日历应用程序中对重复事件建模的最佳方法是什么?,ruby,algorithm,calendar,data-modeling,recurrence,Ruby,Algorithm,Calendar,Data Modeling,Recurrence,我正在构建一个需要支持重复事件的组日历应用程序,但我提出的所有处理这些事件的解决方案似乎都是一个漏洞。我可以限制一个人向前看的距离,然后一次生成所有事件。或者,我可以将事件存储为重复事件,并在人们查看日历时动态显示它们,但如果有人想更改事件特定实例的详细信息,我必须将它们转换为正常事件 我肯定有更好的方法,但我还没有找到。您可以更改或删除特定事件实例的详细信息,对重复事件建模的最佳方法是什么 (我使用的是Ruby,但请不要因此而限制您的回答。不过,如果有Ruby特定的库或其他东西,很高兴知道。)

我正在构建一个需要支持重复事件的组日历应用程序,但我提出的所有处理这些事件的解决方案似乎都是一个漏洞。我可以限制一个人向前看的距离,然后一次生成所有事件。或者,我可以将事件存储为重复事件,并在人们查看日历时动态显示它们,但如果有人想更改事件特定实例的详细信息,我必须将它们转换为正常事件

我肯定有更好的方法,但我还没有找到。您可以更改或删除特定事件实例的详细信息,对重复事件建模的最佳方法是什么


(我使用的是Ruby,但请不要因此而限制您的回答。不过,如果有Ruby特定的库或其他东西,很高兴知道。)

您可能想看看iCalendar软件实现或标准本身(RFC 2445)。 人们很快就会想到Mozilla项目,快速搜索也会发现这些项目


根据存储事件的方式,可以考虑其他选项。您正在构建自己的数据库模式吗?使用基于iCalendar等的工具?

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

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


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

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

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

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

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

唯一有问题的情况是,如果用户希望更新此事件和所有未来事件。在这种情况下,您必须将重复发生的事件分成两部分。在这一点上,您可能想考虑以某种方式链接循环事件,以便可以删除它们。
  • 跟踪定期规则(可能基于iCalendar,per@)。这将包括一个模式和一个范围(每三个星期二,出现10次)
  • 如果要编辑/删除特定事件,请跟踪上述定期规则的异常日期(规则指定的事件未发生的日期)
  • 如果已删除,则只需执行此操作;如果已编辑,则创建另一个事件,并为其设置主事件的父ID。您可以选择是否在此记录中包含所有主事件的信息,或者它是否仅保存更改并继承所有未更改的内容
  • 请注意,如果您允许不结束的重复规则,您必须考虑如何显示您现在无限量的信息


    希望有帮助

    我建议使用日期库的强大功能和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\"]"
    

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

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

    定期事件可能存在许多问题,让我重点介绍一些我知道的问题

    解决方案1-无实例 存储原始约会+定期数据,不要存储所有实例

    问题:

    • 在需要时,您必须计算日期窗口中的所有实例,代价高昂
    • 无法处理异常(即,您删除或移动其中一个实例,或者更确切地说,您无法使用此解决方案执行此操作)
    解决方案2-存储实例 存储从1开始的所有内容,以及链接回原始约会的所有实例

    问题:

    • 占用大量空间(但空间很便宜,所以很小)
    • 例外情况必须妥善处理,特别是在例外情况发生后返回并编辑原始约会时。例如,如果将第三个实例向前移动一天,如果返回并编辑
      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.
      
      TableID: 1 
      Name, cycleB  
      StartTime, 27 November 2014  
      EndTime,November 6 2015  
      Cycletype, WeekLy
      Foreignkey, 1 (pointingto the table recycle paternal events).
      
      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;
      }