캘린더 애플리케이션에서 반복 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/85699

문제

반복되는 이벤트를 지원해야 하는 그룹 캘린더 애플리케이션을 구축 중이지만 이러한 이벤트를 처리하기 위해 내가 생각해낸 모든 솔루션은 해킹처럼 보입니다.얼마나 멀리 볼 수 있는지 제한한 다음 모든 이벤트를 한 번에 생성할 수 있습니다.또는 이벤트를 반복되는 것으로 저장하고 달력에서 미리 볼 때 동적으로 표시할 수 있지만 누군가 이벤트의 특정 인스턴스에 대한 세부 정보를 변경하려면 일반 이벤트로 변환해야 합니다.

이 작업을 수행하는 더 좋은 방법이 있다고 확신하지만 아직 찾지 못했습니다.특정 이벤트 인스턴스의 세부정보를 변경하거나 삭제할 수 있는 반복 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?

(저는 Ruby를 사용하고 있지만 이것이 귀하의 답변을 제한하지 않도록 하십시오.하지만 Ruby 전용 라이브러리 등이 있다면 알아두면 좋습니다.)

도움이 되었습니까?

해결책

앞으로 반복되는 모든 이벤트에 대해 '링크' 개념을 사용하겠습니다.달력에 동적으로 표시되며 단일 참조 개체에 다시 연결됩니다.이벤트가 발생하면 링크가 끊어지고 이벤트는 독립형 인스턴스가 됩니다.반복 이벤트를 편집하려고 하면 향후 모든 항목을 변경하라는 메시지가 표시됩니다(예:단일 링크 참조 변경) 또는 해당 인스턴스만 변경합니다(이 경우 독립 실행형 인스턴스로 변환한 다음 변경).후자의 경우 단일 인스턴스로 변환된 모든 향후 이벤트의 반복 목록을 추적해야 하기 때문에 약간 문제가 있습니다.그러나 이것은 전적으로 가능합니다.

따라서 본질적으로 이벤트에는 단일 인스턴스와 반복 이벤트라는 두 가지 클래스가 있습니다.

다른 팁

마틴 파울러(Martin Fowler) - 달력에 대한 반복 이벤트 흥미로운 통찰력과 패턴이 포함되어 있습니다.

꼬마 gem은 이 패턴을 구현합니다.

반복되는 이벤트에는 많은 문제가 있을 수 있습니다. 제가 알고 있는 몇 가지를 강조하겠습니다.

해결 방법 1 - 인스턴스 없음

원래 약속 + 반복 데이터를 저장하고 모든 인스턴스를 저장하지 않습니다.

문제:

  • 필요할 때 날짜 창에서 모든 인스턴스를 계산해야 하며 비용이 많이 듭니다.
  • 예외를 처리할 수 없습니다(예:인스턴스 중 하나를 삭제하거나 이동하거나 이 솔루션으로는 이 작업을 수행할 수 없습니다)

솔루션 2 - 인스턴스 저장

1의 모든 항목과 모든 인스턴스를 원래 약속에 다시 연결하여 저장합니다.

문제:

  • 공간을 많이 차지함(단, 공간이 저렴해서 작음)
  • 예외는 적절하게 처리되어야 하며, 특히 예외를 만든 후 원래 약속으로 돌아가서 편집하는 경우에는 더욱 그렇습니다.예를 들어 세 번째 인스턴스를 하루 앞으로 옮겼다면 다시 돌아가서 원래 약속 시간을 편집하고 원래 날짜에 다른 인스턴스를 다시 삽입하고 이동된 약속을 남겨두면 어떻게 될까요?이동한 항목을 연결 해제하시겠습니까?이동한 것을 적절하게 변경해 볼까요?

물론 예외를 수행하지 않으려면 두 가지 솔루션 모두 괜찮고 기본적으로 시간/공간 절충 시나리오에서 선택합니다.

iCalendar 소프트웨어 구현이나 표준 자체(RFC 2445 RFC 5545).빨리 떠오르는 것은 Mozilla 프로젝트입니다. http://www.mozilla.org/projects/calendar/ 빠르게 검색해 보면 알 수 있습니다 http://icalendar.rubyforge.org/ 또한.

이벤트를 저장하는 방법에 따라 다른 옵션을 고려할 수 있습니다.자신만의 데이터베이스 스키마를 구축하고 있나요?iCalendar 기반 등을 사용하고 계십니까?

나는 다음과 같이 일하고 있습니다 :

입력 유형 :recurring(form.schedule :as => :recurring), 이는 iCal과 유사한 인터페이스와 before_filter 뷰를 직렬화하려면 IceCube 다시 반대하세요, 게토처럼.

내 아이디어는 모델에 반복되는 속성을 추가하고 뷰에서 쉽게 연결할 수 있도록 하는 것입니다.모두 몇 줄로 구성되어 있습니다.


그러면 이것이 나에게 무엇을 주는가?색인화되고 편집 가능하며 반복되는 속성입니다.

events 하루 인스턴스를 저장하고 달력보기/도우미에 사용됩니다. task.schedule yaml'd를 저장합니다. IceCube 객체를 사용하여 다음과 같은 호출을 수행할 수 있습니다. task.schedule.next_suggestion.

요약:저는 두 가지 모델을 사용합니다. 하나는 달력 표시용으로, 하나는 기능용으로 속성을 부여한 것입니다.

저는 여러 달력 기반 애플리케이션을 개발했으며 반복을 지원하는 재사용 가능한 JavaScript 달력 구성 요소 세트도 작성했습니다.대략적인 내용을 적어봤습니다 반복을 설계하는 방법 그것은 누군가에게 도움이 될 수 있습니다.내가 작성한 라이브러리에만 적용되는 몇 가지 정보가 있지만 제공되는 대부분의 조언은 모든 캘린더 구현에 일반적입니다.

몇 가지 핵심 사항:

  • 다음을 사용하여 반복을 저장합니다. iCal RRULE 형식 -- 정말 재발명하고 싶지 않은 바퀴입니다.
  • 개별 반복 일정을 저장하지 마세요. 인스턴스 데이터베이스의 행으로!항상 반복 패턴을 저장하세요.
  • 이벤트/예외 스키마를 설계하는 방법에는 여러 가지가 있지만 기본 시작점 예제가 제공됩니다.
  • 모든 날짜/시간 값은 UTC로 저장되고 표시를 위해 로컬로 변환되어야 합니다.
  • 반복 이벤트에 대해 저장된 종료 날짜는 항상 반복 범위의 종료일 (또는 "영원히" 반복되는 경우 플랫폼의 "최대 날짜") 및 이벤트 기간을 별도로 저장해야 합니다.이는 나중에 이벤트를 쿼리하는 올바른 방법을 보장하기 위한 것입니다.
  • 이벤트 인스턴스 생성 및 반복 편집 전략에 대한 일부 논의가 포함되어 있습니다.

구현에 대한 수많은 유효한 접근 방식이 포함된 정말 복잡한 주제입니다.나는 실제로 반복을 여러 번 성공적으로 구현했다고 말하고 실제로 그것을 수행하지 않은 사람으로부터 이 주제에 대한 조언을 받는 것을 조심할 것입니다.

반복 매개변수를 저장하기 위해 아래 설명된 데이터베이스 스키마를 사용하고 있습니다.

http://github.com/bakineggs/recurring_events_for

그런 다음 runt를 사용하여 날짜를 동적으로 계산합니다.

https://github.com/mlipper/runt

  1. 반복 규칙을 추적합니다(아마도 @당 iCalendar를 기반으로 함).크리스 K.).여기에는 패턴과 범위(매달 세 번째 화요일, 10회 발생)가 포함됩니다.
  2. 특정 발생을 편집/삭제하려는 경우, 위의 반복 규칙에 대한 예외 날짜(이벤트가 발생한 날짜)를 추적하십시오. 그렇지 않다 규칙이 지정한 대로 발생합니다).
  3. 삭제했다면 그게 전부이고, 편집했다면 다른 이벤트를 만들고 메인 이벤트에 설정된 상위 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\"]"

이 답변에서 나는 일종의 해결책을 찾아냈습니다.링크 개념이 정말 마음에 듭니다.반복되는 이벤트는 꼬리가 반복 규칙을 알고 있는 연결된 목록일 수 있습니다.링크가 그대로 유지되고 이벤트 삭제도 쉽기 때문에 이벤트 하나를 변경하는 것이 쉽습니다. 이벤트 링크를 해제하고 삭제한 다음 이벤트 전후에 이벤트를 다시 연결하기만 하면 됩니다.누군가가 달력에서 이전에 한 번도 살펴본 적이 없는 새로운 기간을 볼 때마다 반복 이벤트를 쿼리해야 하지만 그렇지 않으면 꽤 깔끔합니다.

이벤트를 반복적으로 저장할 수 있으며, 특정 인스턴스가 편집된 경우 동일한 이벤트 ID로 새 이벤트를 생성할 수 있습니다.그런 다음 이벤트를 검색할 때 동일한 이벤트 ID를 가진 모든 이벤트를 검색하여 모든 정보를 가져옵니다.자신만의 이벤트 라이브러리를 롤링했는지, 아니면 기존 라이브러리를 사용하고 있는지 확실하지 않아서 불가능할 수도 있습니다.

세 가지 좋은 Ruby 날짜/시간 라이브러리에 대해서는 아래 기사를 확인하세요.특히 ice_cube는 반복 규칙 및 이벤트 달력에 필요한 기타 항목에 대한 확실한 선택인 것 같습니다.http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

자바스크립트에서:

반복 일정 처리:http://bunkat.github.io/later/

복잡한 이벤트와 해당 일정 간의 종속성 처리:http://bunkat.github.io/schedule/

기본적으로 규칙을 만든 다음 lib에 다음 N개의 반복 이벤트(날짜 범위 지정 여부 지정)를 계산하도록 요청합니다.규칙을 구문 분석/직렬화하여 모델에 저장할 수 있습니다.

반복되는 이벤트가 있고 하나의 반복만 수정하려는 경우 다음을 사용할 수 있습니다. 제외하고() 특정 날짜를 닫은 다음 이 항목에 대해 수정된 새 이벤트를 추가하는 기능입니다.

lib는 매우 복잡한 패턴, 시간대, 크로닝 이벤트까지 지원합니다.

이벤트를 반복적으로 저장하고 동적으로 표시합니다. 그러나 반복 이벤트에 특정 날짜의 기본 정보를 재정의할 수 있는 특정 이벤트 목록이 포함될 수 있도록 허용합니다.

반복 이벤트를 쿼리하면 해당 날짜의 특정 재정의를 확인할 수 있습니다.

사용자가 변경하는 경우 모든 인스턴스(기본 세부 정보)에 대해 업데이트할지, 아니면 그날만 업데이트할지(새 특정 이벤트를 만들어 목록에 추가) 물을 수 있습니다.

사용자가 이 이벤트의 모든 반복 항목을 삭제하도록 요청하는 경우 세부 사항 목록도 제공되므로 쉽게 제거할 수 있습니다.

유일한 문제가 되는 경우는 사용자가 이 이벤트와 향후 모든 이벤트를 업데이트하려는 경우입니다.이 경우 반복 일정을 두 개로 분할해야 합니다.이 시점에서 반복 이벤트를 모두 삭제할 수 있도록 어떤 방식으로든 연결하는 것을 고려할 수 있습니다.

일부 라이센스 비용을 지불할 준비가 된 .NET 프로그래머의 경우 다음을 찾을 수 있습니다. Aspose.Network 유용한...반복 약속을 위한 iCalendar 호환 라이브러리가 포함되어 있습니다.

이벤트를 iCalendar 형식으로 직접 저장하면 무제한 반복, 시간대 현지화 등이 가능합니다.

이를 CalDAV 서버에 저장한 다음 이벤트를 표시하려는 경우 CalDAV에 정의된 보고서 옵션을 사용하여 서버에 표시된 기간에 걸쳐 반복 이벤트를 확장하도록 요청할 수 있습니다.

또는 백엔드 CalDAV 서버와 통신하기 위해 PUT/GET/REPORT가 필요 없이 직접 데이터베이스에 저장하고 일종의 iCalendar 구문 분석 라이브러리를 사용하여 확장을 수행할 수 있습니다.아마도 더 많은 작업이 필요할 것입니다. CalDAV 서버가 어딘가에 복잡성을 숨기고 있다고 확신합니다.

iCalendar 형식의 이벤트를 사용하면 사람들이 항상 다른 소프트웨어에 넣기 위해 이벤트를 내보내길 원하기 때문에 장기적으로 일이 더 간단해질 것입니다.

간단하게 이 기능을 구현해봤습니다!논리는 다음과 같습니다. 먼저 두 개의 테이블이 필요합니다.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일까지의 데이터를 쿼리하려고 합니다.시작 및 종료 시간인 WeekLy를 기준으로 RecurringEventBE(긴 시작, 긴 종료) 함수를 작성할 수 있으며 원하는 컬렉션(<cycleA11.20, CycleA 11.27, CycleA 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).

기능 반복에 대해 (Long Start, Long End)에서, 당신은이 데이터를 가상 이벤트 (cycleb11.27) 내 영어에 대해 죄송합니다. 나는 시도했습니다.

이것은 내 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;
}   

종료 날짜 없이 반복되는 약속이 있는 경우 어떻게 해야 합니까?공간이 저렴하기 때문에 무한한 공간이 없으므로 솔루션 2는 시작하기에 적합하지 않습니다.

"종료 날짜 없음"을 세기말의 종료 날짜로 해결할 수 있다고 제안하고 싶습니다.일상적인 행사에도 공간의 양은 저렴합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top