ما هي أفضل طريقة لنمذجة الأحداث المتكررة في تطبيق التقويم؟

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

سؤال

أقوم بإنشاء تطبيق تقويم جماعي يحتاج إلى دعم الأحداث المتكررة، ولكن جميع الحلول التي توصلت إليها للتعامل مع هذه الأحداث تبدو وكأنها اختراق.يمكنني تحديد مدى التقدم الذي يمكن للمرء أن ينظر إليه، ومن ثم توليد كل الأحداث مرة واحدة.أو يمكنني تخزين الأحداث كأحداث متكررة وعرضها ديناميكيًا عندما يتطلع المرء إلى الأمام في التقويم، ولكن سيتعين علي تحويلها إلى حدث عادي إذا أراد شخص ما تغيير التفاصيل في مثيل معين للحدث.

أنا متأكد من أن هناك طريقة أفضل للقيام بذلك، لكنني لم أجدها بعد.ما هي أفضل طريقة لنمذجة الأحداث المتكررة، حيث يمكنك تغيير تفاصيل أو حذف حالات معينة من الأحداث؟

(أنا أستخدم روبي، ولكن من فضلك لا تدع ذلك يقيد إجابتك.إذا كانت هناك مكتبة خاصة بروبي أو شيء من هذا القبيل، فمن الجيد أن نعرف ذلك.)

هل كانت مفيدة؟

المحلول

سأستخدم مفهوم "الارتباط" لجميع الأحداث المتكررة في المستقبل.يتم عرضها ديناميكيًا في التقويم ويتم ربطها مرة أخرى بكائن مرجعي واحد.عند وقوع الأحداث، ينقطع الارتباط ويصبح الحدث حدثًا مستقلاً.إذا حاولت تعديل حدث متكرر، فاطلب تغيير جميع العناصر المستقبلية (على سبيل المثال.قم بتغيير مرجع مرتبط واحد) أو قم بتغيير هذا المثيل فقط (وفي هذه الحالة قم بتحويل هذا إلى مثيل مستقل ثم قم بإجراء التغيير).تمثل الحالة الأخيرة مشكلة بعض الشيء حيث تحتاج إلى متابعة جميع الأحداث المستقبلية التي تم تحويلها إلى مثيل واحد في قائمتك المتكررة.لكن هذا أمر ممكن القيام به تمامًا.

لذا، في الأساس، هناك فئتان من الأحداث - حالات فردية وأحداث متكررة.

نصائح أخرى

مارتن فاولر - الأحداث المتكررة للتقويمات يحتوي على بعض الأفكار والأنماط المثيرة للاهتمام.

قزم جوهرة تنفذ هذا النمط.

يمكن أن يكون هناك العديد من المشاكل في الأحداث المتكررة، اسمحوا لي أن أسلط الضوء على القليل منها الذي أعرفه.

الحل 1 - لا توجد حالات

قم بتخزين الموعد الأصلي + بيانات التكرار، ولا تقم بتخزين جميع المثيلات.

مشاكل:

  • سيتعين عليك حساب جميع المثيلات في نافذة التاريخ عندما تحتاج إليها، وهو أمر مكلف
  • غير قادر على التعامل مع الاستثناءات (أي.قمت بحذف إحدى المثيلات، أو نقلها، أو بالأحرى، لا يمكنك القيام بذلك مع هذا الحل)

الحل 2 - تخزين المثيلات

قم بتخزين كل شيء بدءًا من 1، ولكن أيضًا جميع المثيلات المرتبطة بالموعد الأصلي.

مشاكل:

  • تستهلك مساحة كبيرة (لكن المساحة رخيصة، وصغيرة جدًا)
  • يجب التعامل مع الاستثناءات بأمان، خاصة إذا قمت بالرجوع وتحرير الموعد الأصلي بعد إجراء استثناء.على سبيل المثال، إذا قمت بنقل المثيل الثالث يومًا واحدًا للأمام، فماذا لو عدت وعدلت وقت الموعد الأصلي، وأعدت إدخال موعد آخر في اليوم الأصلي وتركت الموعد المنقول؟إلغاء ربط المنقول؟حاول تغيير المنقول بشكل مناسب؟

بالطبع، إذا كنت لن تقوم بأي استثناءات، فيجب أن يكون أي من الحلين جيدًا، وعليك الاختيار بشكل أساسي من سيناريو مقايضة الزمان والمكان.

قد ترغب في إلقاء نظرة على تطبيقات برنامج iCalendar أو المعيار نفسه (آر إف سي 2445 آر إف سي 5545).المشاريع التي تتبادر إلى ذهني سريعًا هي مشاريع Mozilla http://www.mozilla.org/projects/calendar/ يكشف البحث السريع http://icalendar.rubyforge.org/ أيضًا.

يمكن النظر في خيارات أخرى اعتمادًا على كيفية تخزين الأحداث.هل تقوم ببناء مخطط قاعدة البيانات الخاصة بك؟هل تستخدم شيئًا يعتمد على iCalendar وما إلى ذلك؟

أنا أعمل مع ما يلي:

وجوهرة قيد التقدم تعمل على توسيع النموذج بنوع الإدخال: متكرر (form.schedule :as => :recurring) ، والذي يعرض واجهة تشبه iCal و before_filter لتسلسل العرض إلى ملف IceCube كائن مرة أخرى، الغيتو لاي.

فكرتي هي تسهيل إضافة سمات متكررة إلى النموذج وتوصيلها بسهولة في العرض.كل ذلك في سطرين.


إذن ماذا يعطيني هذا؟سمات مفهرسة وقابلة للتحرير ومتكررة.

events يخزن مثيل يوم واحد ، ويستخدم في عرض التقويم/المساعد يقول task.schedule يخزن اليمل IceCube كائن، حتى تتمكن من إجراء مكالمات مثل: task.schedule.next_suggestion.

خلاصة:أستخدم نموذجين، أحدهما مسطح لعرض التقويم، والآخر خاص بالوظيفة.

لقد قمت بتطوير العديد من التطبيقات المستندة إلى التقويم، وقمت أيضًا بتأليف مجموعة من مكونات تقويم JavaScript القابلة لإعادة الاستخدام والتي تدعم التكرار.لقد كتبت لمحة عامة عن كيفية تصميم للتكرار قد يكون ذلك مفيدًا لشخص ما.في حين أن هناك بعض الأجزاء الخاصة بالمكتبة التي كتبتها، فإن الغالبية العظمى من النصائح المقدمة عامة لأي تطبيق للتقويم.

بعض النقاط الرئيسية:

  • تخزين التكرار باستخدام تنسيق iCal RRULE -- هذه هي العجلة التي لا ترغب حقًا في إعادة اختراعها
  • لا تقم بتخزين حدث متكرر فردي الحالات كصفوف في قاعدة البيانات الخاصة بك!قم دائمًا بتخزين نمط التكرار.
  • هناك العديد من الطرق لتصميم مخطط الحدث/الاستثناء، ولكن تم توفير مثال أساسي لنقطة البداية
  • يجب تخزين جميع قيم التاريخ/الوقت بالتوقيت العالمي المنسق (UTC) وتحويلها إلى القيمة المحلية للعرض
  • يجب أن يكون تاريخ الانتهاء المخزن للحدث المتكرر هو دائمًا تاريخ انتهاء نطاق التكرار (أو "التاريخ الأقصى" للنظام الأساسي الخاص بك إذا كان متكررًا "للأبد") ويجب تخزين مدة الحدث بشكل منفصل.هذا لضمان طريقة عقلانية للاستعلام عن الأحداث لاحقًا.
  • تم تضمين بعض المناقشات حول إنشاء مثيلات الأحداث واستراتيجيات تحرير التكرار

إنه موضوع معقد حقًا وله العديد والعديد من الأساليب الصحيحة لتنفيذه.سأقول إنني قمت بالفعل بتنفيذ التكرار عدة مرات بنجاح، وسأكون حذرًا من أخذ النصائح حول هذا الموضوع من أي شخص لم يقم بذلك بالفعل.

أنا أستخدم مخطط قاعدة البيانات كما هو موضح أدناه لتخزين معلمات التكرار

http://github.com/bakineggs/recurring_events_for

ثم أستخدم runt لحساب التواريخ ديناميكيًا.

https://github.com/mlipper/runt

  1. تتبع قاعدة التكرار (ربما تعتمد على iCalendar، لكل @كريس ك.).سيتضمن هذا نمطًا ونطاقًا (كل يوم ثلاثاء ثالث، لمدة 10 مرات).
  2. عندما تريد تحرير/حذف حدث معين، تتبع تواريخ الاستثناء لقاعدة التكرار أعلاه (التواريخ التي يقع فيها الحدث لا تحدث كما تحدد القاعدة).
  3. إذا قمت بالحذف، فهذا كل ما تحتاجه، وإذا قمت بالتحرير، فقم بإنشاء حدث آخر، ومنحه معرفًا رئيسيًا تم تعيينه للحدث الرئيسي.يمكنك اختيار ما إذا كنت تريد تضمين كافة معلومات الحدث الرئيسي في هذا السجل، أو إذا كان يحتفظ فقط بالتغييرات ويرث كل شيء لا يتغير.

لاحظ أنه إذا سمحت بقواعد التكرار التي لا تنتهي، فيجب عليك التفكير في كيفية عرض كمية المعلومات التي لا حصر لها الآن.

امل ان يساعد!

أوصي باستخدام قوة مكتبة التاريخ ودلالات وحدة نطاق روبي.الحدث المتكرر هو في الواقع وقت ونطاق زمني (البداية والنهاية) وعادةً ما يكون يومًا واحدًا من الأسبوع.باستخدام التاريخ والنطاق يمكنك الإجابة على أي سؤال:

#!/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\"]"

من هذه الإجابات، لقد قمت بغربلة الحل.أنا حقا أحب فكرة مفهوم الارتباط.يمكن أن تكون الأحداث المتكررة عبارة عن قائمة مرتبطة، مع معرفة الذيل بقاعدة التكرار الخاصة بها.سيكون تغيير حدث واحد أمرًا سهلاً، لأن الروابط تظل في مكانها، كما أن حذف الحدث أمر سهل أيضًا - ما عليك سوى إلغاء ربط الحدث وحذفه وإعادة ربط الحدث قبله وبعده.لا يزال يتعين عليك الاستعلام عن الأحداث المتكررة في كل مرة ينظر فيها شخص ما إلى فترة زمنية جديدة لم يتم النظر إليها من قبل في التقويم، ولكن بخلاف ذلك يعد هذا الأمر نظيفًا جدًا.

يمكنك تخزين الأحداث كأحداث متكررة، وإذا تم تحرير مثيل معين، فقم بإنشاء حدث جديد بنفس معرف الحدث.وبعد ذلك، عند البحث عن الحدث، ابحث عن جميع الأحداث التي لها نفس معرف الحدث للحصول على جميع المعلومات.لست متأكدًا مما إذا كنت قد قمت بإنشاء مكتبة الأحداث الخاصة بك، أو إذا كنت تستخدم مكتبة موجودة بالفعل، فقد لا يكون ذلك ممكنًا.

راجع المقالة أدناه للحصول على ثلاث مكتبات جيدة للتاريخ/الوقت.يبدو أن 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 حساب الأحداث المتكررة التالية (تحديد نطاق زمني أم لا).يمكن تحليل/تسلسل القواعد لحفظها في النموذج الخاص بك.

إذا كان لديك حدث متكرر وترغب في تعديل تكرار واحد فقط، فيمكنك استخدام يستثني() وظيفة لاستبعاد يوم معين ثم إضافة حدث معدل جديد لهذا الإدخال.

يدعم lib الأنماط والمناطق الزمنية المعقدة للغاية وحتى أحداث النسخ.

قم بتخزين الأحداث على أنها متكررة وعرضها ديناميكيًا، ولكن اسمح للحدث المتكرر أن يحتوي على قائمة بأحداث معينة يمكن أن تتجاوز المعلومات الافتراضية في يوم محدد.

عند الاستعلام عن الحدث المتكرر، يمكنه التحقق من وجود تجاوز محدد لذلك اليوم.

إذا أجرى المستخدم تغييرات، فيمكنك سؤاله عما إذا كان يريد التحديث لجميع الحالات (التفاصيل الافتراضية) أو في ذلك اليوم فقط (قم بإنشاء حدث محدد جديد وإضافته إلى القائمة).

إذا طلب المستخدم حذف جميع تكرارات هذا الحدث، فلديك أيضًا قائمة بالتفاصيل التي يمكنك تسليمها ويمكنك إزالتها بسهولة.

ستكون الحالة الإشكالية الوحيدة هي إذا أراد المستخدم تحديث هذا الحدث وجميع الأحداث المستقبلية.وفي هذه الحالة سيتعين عليك تقسيم الحدث المتكرر إلى قسمين.عند هذه النقطة قد ترغب في التفكير في ربط الأحداث المتكررة بطريقة ما حتى تتمكن من حذفها جميعًا.

بالنسبة لمبرمجي .NET المستعدين لدفع بعض رسوم الترخيص، قد تجد Aspose.Network مفيد...يتضمن مكتبة متوافقة مع iCalendar للمواعيد المتكررة.

يمكنك تخزين الأحداث بتنسيق iCalendar مباشرة، مما يسمح بالتكرار المفتوح وتوطين المنطقة الزمنية وما إلى ذلك.

يمكنك تخزينها في خادم CalDAV وبعد ذلك عندما تريد عرض الأحداث، يمكنك استخدام خيار التقرير المحدد في CalDAV لمطالبة الخادم بتوسيع الأحداث المتكررة عبر الفترة المعروضة.

أو يمكنك تخزينها في قاعدة بيانات بنفسك واستخدام أحد أنواع مكتبة تحليل iCalendar للقيام بالتوسيع، دون الحاجة إلى PUT/GET/REPORT للتحدث إلى خادم CalDAV الخلفي.ربما يكون هذا مزيدًا من العمل - أنا متأكد من أن خوادم CalDAV تخفي التعقيد في مكان ما.

من المحتمل أن يؤدي وجود الأحداث بتنسيق iCalendar إلى جعل الأمور أكثر بساطة على المدى الطويل حيث سيرغب الأشخاص دائمًا في تصديرها لوضع برامج أخرى على أي حال.

لقد قمت ببساطة بتنفيذ هذه الميزة!المنطق هو كما يلي، أولا تحتاج إلى جدولين.يقوم RuleTable بتخزين الأحداث العامة أو إعادة تدوير الأحداث الأبوية.يتم تخزين ItemTable أحداث الدورة.على سبيل المثال، عند إنشاء حدث دوري، وقت البدء في 6 نوفمبر 2015، وقت الانتهاء لدورة 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.

أنت الآن تريد الاستعلام عن بيانات الفترة من 20 نوفمبر إلى 20 ديسمبر.يمكنك كتابة دالة RecurringEventBE (بداية طويلة، نهاية طويلة)، بناءً على وقت البداية والنهاية، WeekLy، يمكنك حساب المجموعة التي تريدها، <cycleA11.20، CycleA 11.27، CycleA 12.4 ......>.بالإضافة إلى 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 (بداية طويلة ، نهاية طويلة) ، يمكنك استخدام هذه البيانات التي تغطي الحدث الافتراضي (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