it-swarm-vi.tech

Cách tốt nhất để mô hình các sự kiện định kỳ trong ứng dụng lịch là gì?

Tôi đang xây dựng một ứng dụng lịch nhóm cần hỗ trợ các sự kiện định kỳ, nhưng tất cả các giải pháp tôi đưa ra để xử lý các sự kiện này có vẻ như là một hack. Tôi có thể giới hạn khoảng cách phía trước người ta có thể nhìn, và sau đó tạo ra tất cả các sự kiện cùng một lúc. Hoặc tôi có thể lưu trữ các sự kiện như lặp lại và hiển thị động chúng khi chúng nhìn về phía trước trên lịch, nhưng tôi sẽ phải chuyển đổi chúng thành một sự kiện bình thường nếu ai đó muốn thay đổi chi tiết về một trường hợp cụ thể của sự kiện.

Tôi chắc chắn có một cách tốt hơn để làm điều này, nhưng tôi chưa tìm thấy nó. Cách tốt nhất để mô hình hóa các sự kiện định kỳ, nơi bạn có thể thay đổi chi tiết hoặc xóa các trường hợp sự kiện cụ thể?

(Tôi đang sử dụng Ruby, nhưng xin đừng để điều đó làm hạn chế câu trả lời của bạn. Tuy nhiên, nếu có thư viện dành riêng cho Ruby hoặc một cái gì đó, thì đó là điều tốt để biết.)

213
Clinton N. Dreisbach

Tôi sẽ sử dụng khái niệm 'liên kết' cho tất cả các sự kiện định kỳ trong tương lai. Chúng được hiển thị động trong lịch và liên kết lại với một đối tượng tham chiếu duy nhất. Khi các sự kiện đã diễn ra, liên kết bị hỏng và sự kiện trở thành một thể hiện độc lập. Nếu bạn cố gắng chỉnh sửa một sự kiện định kỳ thì Nhắc để thay đổi tất cả các mục trong tương lai (nghĩa là thay đổi tham chiếu được liên kết đơn) hoặc chỉ thay đổi trường hợp đó (trong trường hợp đó chuyển đổi thành một trường hợp độc lập và sau đó thực hiện thay đổi). Trường hợp thứ hai hơi có vấn đề vì bạn cần theo dõi trong danh sách định kỳ của mình về tất cả các sự kiện trong tương lai đã được chuyển đổi thành một thể hiện duy nhất. Nhưng, điều này là hoàn toàn có thể làm được.

Vì vậy, về bản chất, có 2 lớp sự kiện - các trường hợp đơn lẻ và các sự kiện định kỳ.

84
user16068

Martin Fowler - Sự kiện định kỳ cho Lịch chứa một số hiểu biết và mô hình thú vị.

Runt gem thực hiện mô hình này.

54
Daniel Maurić

Có thể có nhiều vấn đề với các sự kiện định kỳ, hãy để tôi nhấn mạnh một vài vấn đề mà tôi biết.

Giải pháp 1 - không có trường hợp

Lưu trữ cuộc hẹn ban đầu + dữ liệu lặp lại, không lưu trữ tất cả các trường hợp.

Các vấn đề:

  • Bạn sẽ phải tính toán tất cả các trường hợp trong một cửa sổ ngày khi bạn cần chúng, tốn kém
  • Không thể xử lý các trường hợp ngoại lệ (ví dụ: bạn xóa một trong các trường hợp hoặc di chuyển nó, hoặc đúng hơn, bạn không thể làm điều này với giải pháp này)

Giải pháp 2 - lưu trữ trường hợp

Lưu trữ mọi thứ từ 1, nhưng tất cả các trường hợp, được liên kết trở lại cuộc hẹn ban đầu.

Các vấn đề:

  • Chiếm rất nhiều không gian (nhưng không gian thì rẻ, rất nhỏ)
  • Các ngoại lệ phải được xử lý một cách duyên dáng, đặc biệt nếu bạn quay lại và chỉnh sửa cuộc hẹn ban đầu sau khi thực hiện một ngoại lệ. Chẳng hạn, nếu bạn di chuyển phiên bản thứ ba về phía trước một ngày, điều gì sẽ xảy ra nếu bạn quay lại và chỉnh sửa thời gian của cuộc hẹn ban đầu, chèn lại một phiên bản khác vào ngày ban đầu và để lại di chuyển? Bỏ liên kết di chuyển một? Cố gắng thay đổi di chuyển một cách thích hợp?

Tất nhiên, nếu bạn sẽ không làm ngoại lệ, thì một trong hai giải pháp sẽ ổn và về cơ bản bạn chọn từ kịch bản đánh đổi không gian/thời gian.

32

Bạn có thể muốn xem triển khai phần mềm iCalWiki hoặc bản thân tiêu chuẩn (RFC 2445 RFC 5545 ). Những ý tưởng nhanh chóng xuất hiện là các dự án Mozilla http://www.mozilla.org/projects/calWiki/ Một tìm kiếm nhanh cho thấy http://icalWiki.rubyforge.org/ cũng vậy.

Các tùy chọn khác có thể được xem xét tùy thuộc vào cách bạn sẽ lưu trữ các sự kiện. Bạn đang xây dựng lược đồ cơ sở dữ liệu của riêng bạn? Sử dụng một cái gì đó dựa trên iCalWiki, v.v.?

17
Kris Kumler

Tôi đang làm việc với những điều sau đây:

và một viên ngọc đang trong quá trình mở rộng formtastic với loại đầu vào: định kỳ (form.schedule :as => :recurring), kết xuất lại giao diện giống iCal và before_filter để tuần tự hóa chế độ xem thành đối tượng IceCube ghetto-ly.

Ý tưởng của tôi là làm cho việc dễ dàng thêm các thuộc tính định kỳ vào mô hình và kết nối nó dễ dàng trong chế độ xem. Tất cả trong một vài dòng.


Vì vậy, những gì này mang lại cho tôi? Thuộc tính được lập chỉ mục, có thể chỉnh sửa, định kỳ.

events lưu trữ một cá thể một ngày và được sử dụng trong chế độ xem lịch/người trợ giúp nói task.schedule lưu trữ đối tượng yaml'd IceCube, vì vậy bạn có thể thực hiện các cuộc gọi như: task.schedule.next_suggestion .

Tóm tắt: Tôi sử dụng hai mô hình, một căn hộ, để hiển thị lịch và một thuộc tính cho chức năng.

16
Vee

Tôi đã phát triển nhiều ứng dụng dựa trên lịch và cũng là tác giả của một bộ các thành phần lịch JavaScript có thể sử dụng lại hỗ trợ tái phát. Tôi đã viết lên một cái nhìn tổng quan về cách thiết kế để tái phát điều đó có thể hữu ích cho ai đó. Mặc dù có một vài bit dành riêng cho thư viện tôi đã viết, nhưng phần lớn lời khuyên được đưa ra là chung cho bất kỳ triển khai lịch nào.

Một số điểm chính:

  • Lưu trữ tái phát bằng cách sử dụng định dạng iCal RRULE - đó là một bánh xe bạn thực sự không muốn phát minh lại
  • KHÔNG lưu trữ các sự kiện định kỳ riêng lẻ dưới dạng hàng trong cơ sở dữ liệu của bạn! Luôn luôn lưu trữ một mô hình tái phát.
  • Có nhiều cách để thiết kế lược đồ sự kiện/ngoại lệ của bạn, nhưng một ví dụ về điểm bắt đầu cơ bản được cung cấp
  • Tất cả các giá trị ngày/giờ nên được lưu trữ trong UTC và được chuyển đổi thành cục bộ để hiển thị
  • Ngày kết thúc được lưu trữ cho sự kiện định kỳ phải luôn là ngày kết thúc của phạm vi lặp lại (hoặc "ngày tối đa" của nền tảng của bạn nếu lặp lại "mãi mãi") và thời lượng sự kiện phải là lưu trữ riêng. Điều này là để đảm bảo một cách truy vấn lành mạnh cho các sự kiện sau này.
  • Một số thảo luận xung quanh việc tạo các trường hợp sự kiện và chiến lược chỉnh sửa lặp lại được bao gồm

Đây là một chủ đề thực sự phức tạp với nhiều, nhiều cách tiếp cận hợp lệ để thực hiện nó. Tôi sẽ nói rằng tôi thực sự đã thực hiện tái phát nhiều lần thành công và tôi sẽ cảnh giác khi nhận lời khuyên về chủ đề này từ bất kỳ ai chưa thực sự làm điều đó.

14
Brian Moeskau

Tôi đang sử dụng lược đồ cơ sở dữ liệu như được mô tả dưới đây để lưu trữ các tham số lặp lại

http://github.com/bakineggs/recurring_events_for

Sau đó, tôi sử dụng runt để tự động tính toán ngày.

https://github.com/mlipper/runt

6
liangzan
  1. Theo dõi quy tắc lặp lại (có thể dựa trên iCalWiki, per @ Kris K. ). Điều này sẽ bao gồm một mô hình và một phạm vi (Mỗi thứ ba thứ ba, trong 10 lần xuất hiện).
  2. Vì khi bạn muốn chỉnh sửa/xóa sự xuất hiện cụ thể, hãy theo dõi ngày ngoại lệ cho quy tắc lặp lại ở trên (ngày mà sự kiện không xảy ra quy tắc chỉ định).
  3. Nếu bạn đã xóa, đó là tất cả những gì bạn cần, nếu bạn chỉnh sửa, tạo một sự kiện khác và đặt cho nó một ID gốc được đặt thành sự kiện chính. Bạn có thể chọn có bao gồm tất cả thông tin của sự kiện chính trong hồ sơ này hay không, nếu nó chỉ giữ các thay đổi và kế thừa mọi thứ không thay đổi.

Lưu ý rằng nếu bạn cho phép các quy tắc lặp lại không kết thúc, bạn phải suy nghĩ về cách hiển thị lượng thông tin vô hạn hiện tại của mình.

Mong rằng sẽ giúp!

5
bdukes

Tôi khuyên bạn nên sử dụng sức mạnh của thư viện ngày và ngữ nghĩa của mô-đun phạm vi của Ruby. Một sự kiện định kỳ thực sự là một thời gian, một phạm vi ngày (bắt đầu và kết thúc) và thường là một ngày trong tuần. Sử dụng ngày & phạm vi bạn có thể trả lời bất kỳ câu hỏi:

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

Sản xuất tất cả các ngày của sự kiện, bao gồm năm nhuận!

# =>"[\"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\"]"
4
Purfideas

Từ những câu trả lời này, tôi đã tìm ra một giải pháp. Tôi thực sự thích ý tưởng của khái niệm liên kết. Các sự kiện định kỳ có thể là một danh sách được liên kết, với phần đuôi biết quy tắc lặp lại của nó. Thay đổi một sự kiện sau đó sẽ dễ dàng, bởi vì các liên kết giữ nguyên vị trí và xóa một sự kiện cũng dễ dàng - bạn chỉ cần hủy liên kết một sự kiện, xóa nó và liên kết lại sự kiện trước và sau sự kiện đó. Bạn vẫn phải truy vấn các sự kiện định kỳ mỗi khi ai đó nhìn vào một khoảng thời gian mới chưa từng được xem trước đó trên lịch, nhưng nếu không thì điều này khá sạch sẽ.

3
Clinton N. Dreisbach

Kiểm tra bài viết dưới đây để biết ba thư viện Ruby ngày/giờ tốt. Ice_cube nói riêng dường như là một lựa chọn vững chắc cho các quy tắc lặp lại và những thứ khác mà lịch sự kiện sẽ cần. http://www.rubyinside.com/3-new-date-and-time-lologists-for-rubyists-3238.html

2
gokul.janga

Bạn có thể lưu trữ các sự kiện như lặp lại và nếu một trường hợp cụ thể được chỉnh sửa, hãy tạo một sự kiện mới có cùng ID sự kiện. Sau đó, khi tìm kiếm sự kiện, tìm kiếm tất cả các sự kiện có cùng ID sự kiện để có được tất cả thông tin. Tôi không chắc chắn nếu bạn cuộn thư viện sự kiện của riêng bạn, hoặc nếu bạn đang sử dụng một thư viện hiện có để có thể không thể.

2
Vincent McNabb

Trong javascript:

Xử lý lịch định kỳ: http://bunkat.github.io/later/

Xử lý các sự kiện và phụ thuộc phức tạp giữa các lịch trình đó: http://bunkat.github.io/schedule/

Về cơ bản, bạn tạo các quy tắc sau đó bạn yêu cầu lib tính toán N sự kiện định kỳ tiếp theo (chỉ định phạm vi ngày hay không). Các quy tắc có thể được phân tích cú pháp/tuần tự để lưu chúng vào mô hình của bạn.

Nếu bạn có một sự kiện định kỳ và chỉ muốn sửa đổi một lần lặp lại, bạn có thể sử dụng hàm ngoại trừ () để loại bỏ một ngày cụ thể và sau đó thêm một sự kiện sửa đổi mới cho mục này.

Các lib hỗ trợ các mẫu rất phức tạp, múi giờ và thậm chí các sự kiện định kỳ.

1
Flavien Volken

Đối với các lập trình viên .NET đã sẵn sàng trả một số phí cấp phép, bạn có thể thấy Aspose.Network hữu ích ... nó bao gồm một thư viện tương thích iCalWiki cho các cuộc hẹn định kỳ.

0
Shaul Behr

Lưu trữ các sự kiện như lặp lại và hiển thị động chúng, tuy nhiên cho phép sự kiện định kỳ chứa danh sách các sự kiện cụ thể có thể ghi đè thông tin mặc định vào một ngày cụ thể.

Khi bạn truy vấn sự kiện định kỳ, nó có thể kiểm tra ghi đè cụ thể cho ngày hôm đó.

Nếu người dùng thực hiện thay đổi, thì bạn có thể hỏi liệu anh ta có muốn cập nhật cho tất cả các trường hợp (chi tiết mặc định) hoặc chỉ trong ngày hôm đó (tạo một sự kiện cụ thể mới và thêm nó vào danh sách).

Nếu người dùng yêu cầu xóa tất cả các đợt tái diễn của sự kiện này, bạn cũng có danh sách cụ thể để xử lý và có thể xóa chúng dễ dàng.

Trường hợp duy nhất có vấn đề sẽ là nếu người dùng muốn cập nhật sự kiện này và tất cả các sự kiện trong tương lai. Trong trường hợp đó, bạn sẽ phải chia sự kiện định kỳ thành hai. Tại thời điểm này, bạn có thể muốn xem xét liên kết các sự kiện định kỳ theo một cách nào đó để bạn có thể xóa tất cả chúng.

0
Andrew Johnson

Bạn lưu trữ các sự kiện ở định dạng iCalWiki trực tiếp, cho phép lặp lại kết thúc mở, nội địa hóa múi giờ và vv.

Bạn có thể lưu trữ chúng trong máy chủ CalDAV và sau đó khi bạn muốn hiển thị các sự kiện, bạn có thể sử dụng tùy chọn báo cáo được xác định trong CalDAV để yêu cầu máy chủ thực hiện mở rộng các sự kiện định kỳ trong khoảng thời gian đã xem.

Hoặc bạn có thể tự lưu trữ chúng trong cơ sở dữ liệu và sử dụng một số loại thư viện phân tích cú pháp iCalWiki để thực hiện việc mở rộng mà không cần PUT/GET/BÁO CÁO để nói chuyện với máy chủ CalDAV phụ trợ. Đây có thể là công việc nhiều hơn - Tôi chắc chắn rằng các máy chủ CalDAV ẩn giấu sự phức tạp ở đâu đó.

Có các sự kiện ở định dạng iCalWiki có thể sẽ giúp mọi thứ đơn giản hơn trong thời gian dài vì mọi người sẽ luôn muốn chúng được xuất để đưa vào phần mềm khác.

0
karora

Tôi chỉ đơn giản là thực hiện tính năng này! Logic như sau, đầu tiên bạn cần hai bảng. RuleTable lưu trữ chung hoặc tái chế các sự kiện gia đình. ItemTable được lưu trữ các sự kiện chu kỳ. Ví dụ: khi bạn tạo một sự kiện theo chu kỳ, thời gian bắt đầu vào ngày 6 tháng 11 năm 2015, thời gian kết thúc cho ngày 6 tháng 12 (hoặc mãi mãi), chu kỳ trong một tuần. Bạn chèn dữ liệu vào RuleTable, các trường như sau:

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.

Bây giờ bạn muốn truy vấn dữ liệu ngày 20 tháng 11 đến ngày 20 tháng 12. Bạn có thể viết một hàm RecurringEventBE (bắt đầu dài, kết thúc dài), dựa trên thời gian bắt đầu và kết thúc, WeekLy, bạn có thể tính toán bộ sưu tập bạn muốn, <cyclA11.20, chu kỳ 11,27, chu kỳ 12,4 ......>. Ngoài ngày 6 tháng 11, và phần còn lại tôi gọi anh ta là một sự kiện ảo. Khi người dùng thay đổi tên của một sự kiện ảo sau (chu kỳ chẳng hạn 27,27), bạn chèn dữ liệu vào ItemTable. Các lĩnh vực như sau:

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

Trong chức năng RecurringEventBE (bắt đầu dài, kết thúc dài), bạn sử dụng dữ liệu này bao gồm sự kiện ảo (chu kỳB11.27) xin lỗi về tiếng Anh của tôi, tôi đã thử.

Đây là RecurringEventBE của tôi

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