source

일정관리 응용프로그램에서 반복 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?

factcode 2023. 6. 3. 08:46
반응형

일정관리 응용프로그램에서 반복 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?

반복 이벤트를 지원해야 하는 그룹 캘린더 애플리케이션을 구축하고 있지만, 이러한 이벤트를 처리하기 위해 생각해 낸 모든 솔루션이 해킹처럼 보입니다.미리 볼 수 있는 범위를 제한하여 모든 이벤트를 한 번에 생성할 수 있습니다.또는 이벤트를 반복으로 저장하고 일정관리에서 미리 볼 때 동적으로 표시할 수도 있지만, 이벤트의 특정 인스턴스에 대한 세부 정보를 변경하려는 경우 일반 이벤트로 변환해야 합니다.

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

(Ruby를 사용하고 있습니다만, 답변에 제약이 되지 않도록 부탁드립니다.하지만 루비에 특화된 도서관이나 다른 것이 있다면, 그것은 알아두면 좋습니다.)

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

따라서 기본적으로 이벤트 클래스는 단일 인스턴스와 반복 이벤트의 두 가지입니다.

저는 여러 달력 기반 응용프로그램을 개발했으며, 반복을 지원하는 재사용 가능한 JavaScript 달력 구성요소를 작성했습니다.저는 누군가에게 도움이 될 수 있는 재발을 위한 설계 방법에 대한 개요를 작성했습니다.제가 작성한 라이브러리에 대한 몇 가지 세부 사항이 있지만, 제안된 대부분의 조언은 모든 달력 구현에 일반적입니다.

몇 가지 핵심 사항:

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

이 주제는 정말 복잡한 주제입니다. 이를 구현하기 위한 많은 유효한 접근법이 있습니다.저는 재발을 실제로 여러 번 성공적으로 실행했다고 말할 것이고, 실제로 실행하지 않은 사람들로부터 이 주제에 대한 조언을 듣는 것을 경계할 것입니다.

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

솔루션 1 - 인스턴스 없음

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

문제:

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

솔루션 2 - 인스턴스 저장

1부터 모든 항목을 저장하고 모든 인스턴스를 원래 약속에 다시 연결합니다.

문제:

  • 많은 공간이 필요함(그러나 공간이 저렴하여 부차적임)
  • 특히 예외를 만든 후 원래 약속으로 돌아가서 편집하는 경우에는 예외를 적절하게 처리해야 합니다.예를 들어, 세 번째 인스턴스를 하루 앞당긴 경우 원래 약속 시간으로 돌아가서 편집한 후 원래 날짜에 다른 인스턴스를 다시 삽입하고 이동한 인스턴스를 그대로 두면 어떻게 됩니까?이동된 연결을 해제하시겠습니까?이동된 항목을 적절하게 변경하시겠습니까?

물론 예외를 적용하지 않을 경우 두 솔루션 모두 문제가 없을 것이며 기본적으로 시간/공간 균형 시나리오에서 선택할 수 있습니다.

iCalendar 소프트웨어 구현 또는 표준 자체를 살펴볼 수 있습니다. RFC 2445 RFC 5545).빠르게 떠오르는 것은 Mozilla 프로젝트 http://www.mozilla.org/projects/calendar/ 입니다. 빠른 검색을 통해 http://icalendar.rubyforge.org/ 도 확인할 수 있습니다.

이벤트를 저장하는 방법에 따라 다른 옵션을 고려할 수 있습니다.자체 데이터베이스 스키마를 작성하고 있습니까?iCalendar 기반 사용 등?

저는 다음과 같이 일하고 있습니다.

  • http://github.com/elevation/event_calendar - 달력 모델 및 도우미
  • http://github.com/seejohnrun/ice_cube - 멋진 반복 보석
  • http://github.com/justinfrench/formtastic - 간단한 양식

type :입력형:그)으로 formatic을 확장하는 진행 중인 .form.schedule :as => :recurring와 a)를 렌더링합니다.before_filter뷰를 일련화하여IceCube다시 반대해, 게토리.

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


그래서 이것이 저에게 무엇을 제공할까요?인덱스, 편집 가능, 반복 특성입니다.

events일 보기됩니다.task.schedule 얌들저다니를 합니다.IceCube객체를 사용하여 다음과 같은 호출을 수행할 수 있습니다.task.schedule.next_suggestion.

요약: 저는 달력 디스플레이에 플랫 모델과 기능에 속성 d 모델 두 개를 사용합니다.

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

http://github.com/bakineggs/recurring_events_for

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

https://github.com/mlipper/runt

  1. 반복 규칙을 추적합니다(아마도 @Kris K.에 따라 iCalendar를 기반으로 함).여기에는 패턴과 범위가 포함됩니다(매 세 번째 화요일, 10회 발생).
  2. 특정 발생을 편집/삭제하려는 경우 위 반복 규칙의 예외 날짜(규칙이 지정한 대로 이벤트가 발생하지 않는 날짜)를 추적합니다.
  3. 삭제한 경우에는 삭제하고 편집한 경우에는 다른 이벤트를 만들고 기본 이벤트에 대한 상위 ID 집합을 지정하면 됩니다.기본 이벤트의 모든 정보를 이 레코드에 포함할지 또는 변경 사항만 유지하고 변경되지 않는 모든 정보를 상속할지 여부를 선택할 수 있습니다.

종료되지 않는 반복 규칙을 허용하는 경우 현재 무한한 양의 정보를 표시하는 방법을 생각해야 합니다.

도움이 되길 바랍니다!

날짜 라이브러리의 힘과 루비의 범위 모듈의 의미론을 사용하는 것을 추천합니다.반복 이벤트는 실제로 시간, 날짜 범위(시작 및 종료) 및 일반적으로 일주일 중 하루입니다.날짜 및 범위를 사용하여 다음과 같은 질문에 답변할 수 있습니다.

#!/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를 가진 모든 이벤트를 검색하여 모든 정보를 가져옵니다.자체 이벤트 라이브러리를 롤링했는지 또는 기존 라이브러리를 사용 중인지 잘 모르겠습니다.

3개의 좋은 루비 날짜/시간 라이브러리는 아래 문서를 확인하십시오.특히 ice_cube는 이벤트 일정에 필요한 반복 규칙 및 기타 항목에 대한 확실한 선택인 것 같습니다.http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

Javascript에서:

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

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

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

반복 이벤트가 있고 한 번만 수정하려는 경우 except() 함수를 사용하여 특정 날짜를 해제한 다음 이 항목에 대해 수정된 새 이벤트를 추가할 수 있습니다.

lib는 매우 복잡한 패턴, 시간대, 심지어 복제 이벤트까지 지원합니다.

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

반복 이벤트를 쿼리할 때 해당 날짜에 대한 특정 재정의 여부를 확인할 수 있습니다.

사용자가 변경한 경우 모든 인스턴스(기본 세부 정보)에 대해 업데이트할 것인지 또는 해당 날짜에만 업데이트할 것인지(새로운 특정 이벤트를 만들어 목록에 추가할 것인지)를 물어볼 수 있습니다.

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

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

라이센스 비용을 지불할 준비가 된 .NET 프로그래머의 경우 Asphose를 찾을 수 있습니다.네트워크 유용...반복적인 약속을 위한 iCalendar 호환 라이브러리가 포함되어 있습니다.

이벤트를 iCalendar 형식으로 직접 저장하여 무제한 반복, 시간대 현지화 등을 수행할 수 있습니다.

이러한 이벤트를 CalDAV 서버에 저장한 다음 이벤트를 표시하려면 CalDAV에 정의된 보고서 옵션을 사용하여 서버에 조회된 기간 동안 반복 이벤트를 확장하도록 요청할 수 있습니다.

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

이벤트를 iCalendar 형식으로 제공하면 사람들이 항상 다른 소프트웨어를 넣기 위해 내보내기를 원하기 때문에 장기적으로 볼 때 이벤트가 더 단순해질 수 있습니다.

이 기능을 간단히 구현했습니다!논리는 다음과 같습니다, 먼저 두 개의 테이블이 필요합니다.규칙 테이블은 일반 또는 순환 부성 이벤트를 저장합니다.ItemTable은 저장된 주기 이벤트입니다.예를 들어 순환 이벤트를 생성하는 경우 2015년 11월 6일의 시작 시간, 12월 6일(또는 영원히)의 종료 시간을 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.

이제 11월 20일부터 12월 20일까지의 데이터를 쿼리하려고 합니다.시작 시간과 종료 시간인 WeekLy를 기준으로 RecurringEventBE(롱 스타트, 롱 엔드) 함수를 작성하면 원하는 컬렉션을 <cycleA11.20, cycleA11.27, cycleA12.4 ......> 11월 6일 외에 나머지는 가상 이벤트라고 불렀습니다.사용자가 가상 이벤트 이름을 변경한 후(예: A11.27 주기) 항목 테이블에 데이터를 삽입합니다.필드는 다음과 같습니다.

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

recurringEventBE(롱 스타트, 롱 엔드) 함수에서 가상 이벤트(사이클 B11.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;
}   

종료 날짜가 없는 반복적인 약속이 있으면 어떻게 합니까?공간이 저렴하지만 무한한 공간이 없기 때문에 솔루션 2는 시작할 수 없습니다.

"종료일 없음"은 금세기 말에 종료일로 해결할 수 있다고 제안합니다.일일 이벤트에도 공간의 양은 저렴합니다.

언급URL : https://stackoverflow.com/questions/85699/whats-the-best-way-to-model-recurring-events-in-a-calendar-application

반응형