IT story

캘린더 응용 프로그램에서 되풀이 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?

hot-time 2020. 4. 28. 08:25
반응형

캘린더 응용 프로그램에서 되풀이 이벤트를 모델링하는 가장 좋은 방법은 무엇입니까?


반복 일정을 지원해야하는 그룹 일정 응용 프로그램을 작성하고 있지만 이러한 일정을 처리하기 위해 제시 한 모든 솔루션은 해킹처럼 보입니다. 어느 정도 앞을 볼 수 있는지 제한 한 다음 모든 이벤트를 한 번에 생성 할 수 있습니다. 또는 일정을 반복으로 저장하고 캘린더에서 미리 볼 때 동적으로 표시 할 수 있지만 누군가가 특정 이벤트 인스턴스의 세부 정보를 변경하려면 일반 이벤트로 변환해야합니다.

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

(루비를 사용하고 있지만 답을 제한하지 마십시오. 루비 전용 라이브러리 나 다른 것이 있다면 알아두면 좋습니다.)


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

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


Martin Fowler-달력의 되풀이 이벤트 에는 흥미로운 통찰력과 패턴이 포함되어 있습니다.

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


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

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

원래 약속 + 되풀이 데이터를 저장하고 모든 인스턴스를 저장하지는 마십시오.

문제 :

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

해결 방법 2-인스턴스 저장

원래 약속에 다시 연결된 1부터 모든 인스턴스를 모두 저장합니다.

문제 :

  • 많은 공간을 차지합니다 (그러나 공간은 저렴하므로 아주 작습니다)
  • 예외를 처리 한 후 원래 약속을 되돌아 가서 편집하는 경우 예외를 정상적으로 처리해야합니다. 예를 들어, 세 번째 인스턴스를 하루 앞으로 이동하면 원래 약속 시간으로 돌아가서 원래 날짜의 시간을 다시 삽입하고 이동 한 날짜를 그대로두면 어떻게됩니까? 이동 한 링크를 해제 하시겠습니까? 이동 한 것을 적절히 변경하려고합니까?

물론, 예외를하지 않는다면, 어느 쪽의 솔루션이든 괜찮을 것이며 기본적으로 시간 / 공간 상충 관계 시나리오에서 선택합니다.


iCalendar 소프트웨어 구현 또는 표준 자체 ( RFC 2445 RFC 5545 ) 를보고 싶을 수 있습니다 . 모질라 프로젝트는 http://www.mozilla.org/projects/calendar/ 에서 빠르게 기억할 수 있습니다. 빠른 검색 에서도 http://icalendar.rubyforge.org/드러납니다 .

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


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

form.schedule :as => :recurringiCal과 같은 인터페이스를 렌더링 before_filter하고 뷰를 IceCube다시 객체 로 직렬화 하는 입력 유형 : recurring ( )으로 형식을 확장하는 gem이 진행 중입니다 .

내 생각은 반복 속성을 모델에 쉽게 추가하고보기에서 쉽게 연결할 수 있도록하는 것입니다. 두 줄에 모두 있습니다.


그래서 이것은 무엇을 제공합니까? 인덱싱되고 편집 가능한 반복 속성입니다.

events하루 인스턴스를 저장하고 달력보기 / 도우미 task.schedule에서 yaml의 IceCube객체를 저장 한다고 말하면 다음과 같이 호출 할 수 있습니다 task.schedule.next_suggestion.

요약 : 달력 표시에는 하나, 평면에는 하나, 기능에는 하나는 두 가지 모델을 사용합니다.


여러 달력 기반 응용 프로그램을 개발했으며 반복을 지원하는 재사용 가능한 JavaScript 달력 구성 요소 집합도 작성했습니다. 누군가에게 도움이 될 수있는 재발을 디자인하는 방법에 대한 개요를 작성했습니다 . 내가 작성한 라이브러리에 특정한 몇 가지 비트가 있지만 제공되는 조언의 대부분은 모든 캘린더 구현에 일반적입니다.

핵심 포인트 중 일부 :

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

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


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

http://github.com/bakineggs/recurring_events_for

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

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를 가진 모든 이벤트를 검색하여 모든 정보를 얻으십시오. 자신의 이벤트 라이브러리를 롤링했는지 또는 기존 라이브러리를 사용 중인지 확실하지 않으므로 잘 모르겠습니다.


세 가지 좋은 루비 날짜 / 시간 라이브러리에 대해서는 아래 기사를 확인하십시오. 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 개의 반복 이벤트 (날짜 범위 지정 여부)를 계산하도록 요청합니다. 규칙을 모델에 저장하기 위해 파싱 / 직렬화 할 수 있습니다.

되풀이 이벤트가 있고 하나의 반복 만 수정하려면 except () 함수를 사용하여 특정 날짜를 닫은 다음이 항목에 대해 수정 된 새 이벤트를 추가 할 수 있습니다.

lib는 매우 복잡한 패턴, 시간대 및 croning 이벤트를 지원합니다.


이벤트를 반복으로 저장하고 동적으로 표시하지만 반복 이벤트에 특정 날짜의 기본 정보를 대체 할 수있는 특정 이벤트 목록을 포함 할 수 있습니다.

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

사용자가 변경하는 경우 모든 인스턴스 (기본 세부 사항) 또는 해당 날짜에 대한 업데이트를 원하는지 (새 특정 이벤트를 작성하여 목록에 추가) 요청할 수 있습니다.

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

사용자가이 이벤트와 모든 향후 이벤트를 업데이트하려는 경우 문제가 될 수 있습니다. 이 경우 되풀이 이벤트를 2 개로 분할해야합니다. 이 시점에서 되풀이 이벤트 연결을 고려하여 모든 이벤트를 삭제할 수 있습니다.


라이센스 비용을 지불 할 준비가 된 .NET 프로그래머의 경우 Aspose.Network가 유용 할 것입니다. 여기에는 되풀이 약속을위한 iCalendar 호환 라이브러리가 포함되어 있습니다.


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

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

또는 PUT / GET / REPORT를 사용하여 백엔드 CalDAV 서버와 통신 할 필요없이 데이터베이스에 직접 저장하고 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 (long start, long end) 함수를 작성할 수 있습니다. 원하는 콜렉션 (<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).

RecurringEventBE (long start, long end) 함수에서 가상 이벤트 (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;
}   

종료 날짜가없는 되풀이 약속이 있으면 어떻게합니까? 공간이 싸기 때문에 무한한 공간이 없으므로 솔루션 2는 스타터가 아닙니다.

"종료일 없음"을 세기 말에 종료일로 해석 할 수 있다고 제안 할 수 있습니다. 매일 행사를해도 공간은 저렴합니다.

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

반응형