it-swarm-id.com

Apa cara terbaik untuk memodelkan acara berulang dalam aplikasi kalender?

Saya sedang membangun aplikasi kalender grup yang perlu mendukung acara yang berulang, tetapi semua solusi yang saya buat untuk menangani acara ini tampak seperti peretasan. Saya dapat membatasi seberapa jauh ke depan seseorang dapat melihat, dan kemudian menghasilkan semua acara sekaligus. Atau saya dapat menyimpan acara sebagai berulang dan menampilkannya secara dinamis ketika seseorang melihat ke depan pada kalender, tetapi saya harus mengubahnya menjadi acara normal jika seseorang ingin mengubah detail pada contoh acara tertentu.

Saya yakin ada cara yang lebih baik untuk melakukan ini, tetapi saya belum menemukannya. Apa cara terbaik untuk memodelkan acara berulang, di mana Anda dapat mengubah detail atau menghapus kejadian acara tertentu?

(Saya menggunakan Ruby, tapi tolong jangan biarkan itu membatasi jawaban Anda. Jika ada perpustakaan khusus-Ruby atau sesuatu, itu baik untuk diketahui.)

213

Saya akan menggunakan konsep 'tautan' untuk semua acara berulang di masa mendatang. Mereka ditampilkan secara dinamis di kalender dan tautan kembali ke objek referensi tunggal. Ketika peristiwa telah terjadi, tautan terputus dan acara tersebut menjadi contoh yang berdiri sendiri. Jika Anda mencoba untuk mengedit acara berulang maka Prompt untuk mengubah semua item yang akan datang (mis. Ubah referensi tunggal yang ditautkan) atau ubah hanya instance itu (dalam hal ini ubah ini menjadi instance yang berdiri sendiri dan kemudian buat perubahan). Cased yang terakhir sedikit bermasalah karena Anda perlu melacak daftar berulang semua peristiwa di masa depan yang dikonversi menjadi satu kejadian. Tapi, ini sepenuhnya bisa dilakukan.

Jadi, pada dasarnya, memiliki 2 kelas acara - kejadian tunggal dan acara berulang.

84
user16068

Martin Fowler - Acara Berulang untuk Kalender berisi beberapa wawasan dan pola yang menarik.

Runt permata menerapkan pola ini.

54
Daniel Maurić

Mungkin ada banyak masalah dengan acara berulang, izinkan saya menyoroti beberapa yang saya ketahui.

Solusi 1 - tidak ada contoh

Menyimpan data janji + pengulangan asli, jangan menyimpan semua instance.

Masalah:

  • Anda harus menghitung semua instance di jendela tanggal ketika Anda membutuhkannya, mahal
  • Tidak dapat menangani pengecualian (mis. Anda menghapus salah satu instance, atau memindahkannya, atau lebih tepatnya, Anda tidak dapat melakukan ini dengan solusi ini)

Solusi 2 - instans toko

Simpan semuanya mulai dari 1, tetapi juga semua instance, ditautkan kembali ke janji temu yang asli.

Masalah:

  • Membutuhkan banyak ruang (tetapi ruang murah, sangat kecil)
  • Pengecualian harus ditangani dengan anggun, terutama jika Anda kembali dan mengedit janji temu setelah membuat pengecualian. Misalnya, jika Anda memindahkan instance ketiga satu hari ke depan, bagaimana jika Anda kembali dan mengedit waktu janji temu yang asli, memasukkan kembali yang lain pada hari yang asli dan meninggalkan yang dipindahkan? Putuskan tautan yang dipindahkan? Coba ubah yang dipindahkan dengan tepat?

Tentu saja, jika Anda tidak akan melakukan pengecualian, maka solusi mana pun sebaiknya baik-baik saja, dan Anda pada dasarnya memilih dari skenario pertukaran waktu/ruang.

Anda mungkin ingin melihat implementasi perangkat lunak iCalendar atau standarnya sendiri (RFC 2445 RFC 5545 ). Satu hal yang terlintas dalam pikiran adalah proyek Mozilla http://www.mozilla.org/projects/calendar/ Pencarian cepat mengungkapkan http://icalendar.rubyforge.org/ juga.

Opsi lain dapat dipertimbangkan tergantung pada bagaimana Anda akan menyimpan acara tersebut. Apakah Anda membangun skema basis data Anda sendiri? Menggunakan sesuatu berbasis iCalendar, dll?

17
Kris Kumler

Saya bekerja dengan yang berikut ini:

dan permata dalam proses yang memperluas formtastic dengan tipe input: berulang (form.schedule :as => :recurring), yang merender antarmuka seperti iCal dan before_filter untuk membuat serialisasi tampilan menjadi objek IceCube lagi, ghetto-ly.

Ide saya adalah membuatnya mudah untuk menambahkan atribut berulang ke model dan menghubungkannya dengan mudah dalam tampilan. Semua dalam beberapa baris.


Jadi apa yang saya berikan? Atribut yang diindeks, dapat diedit, berulang.

events menyimpan instance satu hari, dan digunakan dalam tampilan kalender/penolong katakan task.schedule menyimpan objek _ yaml'd IceCube, sehingga Anda dapat melakukan panggilan seperti: task.schedule.next_suggestion.

Rekap: Saya menggunakan dua model, satu flat, untuk tampilan kalender, dan satu atribut untuk fungsi.

16
Vee

Saya telah mengembangkan beberapa aplikasi berbasis kalender, dan juga menulis satu set komponen kalender JavaScript yang dapat digunakan kembali yang mendukung pengulangan. Saya menulis ikhtisar cara mendesain untuk pengulangan yang mungkin bermanfaat bagi seseorang. Meskipun ada beberapa bit yang khusus untuk perpustakaan yang saya tulis, sebagian besar saran yang ditawarkan bersifat umum untuk setiap implementasi kalender.

Beberapa poin kunci:

  • Simpan perulangan menggunakan format iCal RRULE - itu satu roda yang Anda benar-benar tidak ingin kembalikan
  • JANGAN menyimpan kejadian berulang individual instance sebagai baris dalam basis data Anda! Selalu simpan pola perulangan.
  • Ada banyak cara untuk merancang skema acara/pengecualian Anda, tetapi contoh titik awal dasar disediakan
  • Semua nilai tanggal/waktu harus disimpan dalam UTC dan dikonversi ke lokal untuk tampilan
  • Tanggal akhir yang disimpan untuk acara berulang harus selalu menjadi tanggal akhir dari rentang perulangan (atau "tanggal maksimum" platform Anda jika berulang "selamanya") dan durasi acara harus disimpan secara terpisah. Ini untuk memastikan cara yang wajar untuk menanyakan acara nanti.
  • Beberapa diskusi seputar pembuatan instance acara dan strategi pengulangan perulangan disertakan

Ini adalah topik yang sangat rumit dengan banyak, banyak pendekatan yang valid untuk mengimplementasikannya. Saya akan mengatakan bahwa saya telah benar-benar menerapkan pengulangan beberapa kali dengan sukses, dan saya akan berhati-hati dalam mengambil saran mengenai hal ini dari siapa pun yang belum benar-benar melakukannya.

14
Brian Moeskau

Saya menggunakan skema database seperti yang dijelaskan di bawah ini untuk menyimpan parameter pengulangan

http://github.com/bakineggs/recurring_events_for

Kemudian saya menggunakan runt untuk menghitung tanggal secara dinamis.

https://github.com/mlipper/runt

6
liangzan
  1. Melacak aturan perulangan (mungkin berdasarkan iCalendar, per @ Kris K. ). Ini akan mencakup pola dan kisaran (Setiap Selasa ketiga, untuk 10 kejadian).
  2. Ketika Anda ingin mengedit/menghapus kejadian tertentu, catat tanggal pengecualian untuk aturan perulangan di atas (tanggal di mana acara tidak muncul sebagai aturan menentukan).
  3. Jika Anda menghapus, itu saja yang Anda butuhkan, jika Anda diedit, buat acara lain, dan berikan ID induknya disetel ke acara utama. Anda dapat memilih apakah akan memasukkan semua informasi acara utama dalam catatan ini, atau jika hanya menyimpan perubahan dan mewarisi segala sesuatu yang tidak berubah.

Perhatikan bahwa jika Anda mengizinkan aturan perulangan yang tidak berakhir, Anda harus memikirkan cara menampilkan informasi Anda yang sekarang jumlahnya tak terbatas.

Semoga itu bisa membantu!

5
bdukes

Saya akan merekomendasikan menggunakan kekuatan perpustakaan tanggal dan semantik modul jangkauan Ruby. Acara berulang benar-benar waktu, rentang tanggal (awal & akhir) dan biasanya satu hari dalam seminggu. Menggunakan tanggal & rentang Anda dapat menjawab pertanyaan apa pun:

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

Menghasilkan semua hari acara, termasuk tahun kabisat!

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

Dari jawaban ini, saya semacam menyortir solusi. Saya sangat menyukai ide konsep tautan. Peristiwa berulang bisa menjadi daftar terkait, dengan ekor mengetahui aturan perulangannya. Mengubah satu acara akan menjadi mudah, karena tautannya tetap di tempatnya, dan menghapus acara juga mudah - Anda cukup memutuskan tautan suatu acara, menghapusnya, dan menautkan kembali acara tersebut sebelum dan sesudahnya. Anda masih harus menanyakan acara berulang setiap kali seseorang melihat periode waktu baru yang belum pernah dilihat sebelumnya di kalender, tetapi jika tidak, ini cukup bersih.

3

Periksa artikel di bawah ini untuk tiga pustaka Ruby tanggal/waktu yang baik. Ice_cube khususnya tampaknya merupakan pilihan yang kuat untuk aturan perulangan dan hal-hal lain yang diperlukan kalender acara. http: // www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html

2
gokul.janga

Anda bisa menyimpan acara sebagai berulang, dan jika instance tertentu diedit, buat acara baru dengan ID acara yang sama. Kemudian saat mencari acara, cari semua acara dengan ID acara yang sama untuk mendapatkan semua informasi. Saya tidak yakin apakah Anda menggulung perpustakaan acara Anda sendiri, atau jika Anda menggunakan yang sudah ada sehingga tidak mungkin.

2
Vincent McNabb

Dalam javascript:

Menangani jadwal berulang: http://bunkat.github.io/later/

Menangani acara dan ketergantungan yang kompleks antara jadwal tersebut: http://bunkat.github.io/schedule/

Pada dasarnya, Anda membuat aturan maka Anda meminta lib untuk menghitung peristiwa berulang N berikutnya (menentukan rentang tanggal atau tidak). Aturan dapat diuraikan/diserialisasi untuk menyimpannya ke dalam model Anda.

Jika Anda memiliki acara berulang dan hanya ingin mengubah satu pengulangan, Anda dapat menggunakan fungsi Kecuali () untuk mengabaikan hari tertentu dan kemudian menambahkan yang baru acara yang dimodifikasi untuk entri ini.

Lib mendukung pola yang sangat kompleks, zona waktu, dan bahkan peristiwa kroning.

1
Flavien Volken

Untuk .NET programmer yang siap membayar beberapa biaya lisensi, Anda mungkin menemukan Aspose.Network bermanfaat ... itu termasuk perpustakaan yang kompatibel dengan iCalendar untuk janji temu berulang.

0
Shaul Behr

Menyimpan acara sebagai berulang dan menampilkannya secara dinamis, namun memungkinkan acara berulang untuk berisi daftar peristiwa tertentu yang dapat menggantikan informasi default pada hari tertentu.

Saat Anda menanyakan acara berulang, ia dapat memeriksa penimpaan spesifik untuk hari itu.

Jika pengguna melakukan perubahan, maka Anda dapat bertanya apakah dia ingin memperbarui untuk semua instance (detail default) atau hanya hari itu (membuat acara spesifik baru dan menambahkannya ke daftar).

Jika seorang pengguna meminta untuk menghapus semua perulangan dari acara ini Anda juga memiliki daftar spesifik untuk diberikan dan dapat menghapusnya dengan mudah.

Satu-satunya kasus yang bermasalah adalah jika pengguna ingin memperbarui acara ini dan semua acara mendatang. Dalam hal ini Anda harus membagi acara berulang menjadi dua. Pada titik ini, Anda mungkin ingin mempertimbangkan untuk menautkan acara yang berulang sehingga Anda dapat menghapus semuanya.

0
Andrew Johnson

Anda menyimpan acara dalam format iCalendar secara langsung, yang memungkinkan untuk pengulangan terbuka, lokalisasi zona waktu dan sebagainya.

Anda bisa menyimpan ini di server CalDAV dan kemudian ketika Anda ingin menampilkan acara, Anda dapat menggunakan opsi laporan yang ditentukan di CalDAV untuk meminta server melakukan perluasan acara berulang di seluruh periode yang dilihat.

Atau Anda bisa menyimpannya di database sendiri dan menggunakan semacam parsing pustaka iCalendar untuk melakukan ekspansi, tanpa perlu PUT/GET/LAPORAN untuk berbicara dengan server CalDAV backend. Ini mungkin lebih banyak pekerjaan - saya yakin server CalDAV menyembunyikan kompleksitas di suatu tempat.

Memiliki acara dalam format iCalendar mungkin akan membuat hal-hal yang lebih sederhana dalam jangka panjang karena orang akan selalu ingin mereka diekspor untuk dimasukkan ke perangkat lunak lain.

0
karora

Saya telah Cukup menerapkan fitur ini! Logikanya adalah sebagai berikut, pertama Anda perlu dua tabel. Toko RuleTable umum atau mendaur ulang acara ayah. ItemTable adalah peristiwa siklus yang disimpan. Misalnya, ketika Anda membuat acara siklik, waktu mulai untuk 6 November 2015, waktu akhir untuk 6 Desember (atau selamanya), siklus selama satu minggu. Anda memasukkan data ke dalam RuleTable, bidang adalah sebagai berikut:

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.

Sekarang Anda ingin menanyakan data 20 November hingga 20 Desember. Anda dapat menulis fungsi RecurringEventBE (awal, akhir), berdasarkan waktu mulai dan berakhir, WeekLy, Anda dapat menghitung koleksi yang Anda inginkan, <cycleA11.20, cycleA 11.27, cycleA 12.4 ......>. Selain 6 November, dan sisanya aku memanggilnya acara virtual. Ketika pengguna mengubah nama peristiwa virtual 'setelah (cycleA11.27 misalnya), Anda memasukkan data ke dalam ItemTable. Fields adalah sebagai berikut:

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

Dalam fungsi RecurringEventBE (awal, akhir), Anda menggunakan data ini yang meliputi peristiwa virtual (cycleB11.27) maaf tentang bahasa Inggris saya, saya mencoba.

Ini adalah RecurringEventBE saya :

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