Bir takvim uygulamasında yinelenen etkinlikleri modellemenin en iyi yolu nedir?


225

Yinelenen etkinlikleri desteklemesi gereken bir grup takvim uygulaması oluşturuyorum, ancak bu etkinlikleri ele almak için bulduğum tüm çözümler bir kesmek gibi görünüyor. Bir kişinin ne kadar ileriye bakabileceğini sınırlayabilir ve sonra tüm olayları aynı anda oluşturabilirim. Veya etkinlikleri tekrarlanan olarak saklayabilir ve takvimde ileriye baktığında bunları dinamik olarak görüntüleyebilirim, ancak birisi etkinliğin belirli bir örneğindeki ayrıntıları değiştirmek istiyorsa bunları normal bir etkinliğe dönüştürmek zorunda kalacağım.

Eminim bunu yapmanın daha iyi bir yolu var, ama henüz bulamadım. Belirli olay örneklerinin ayrıntılarını değiştirebileceğiniz veya silebileceğiniz yinelenen etkinlikleri modellemenin en iyi yolu nedir?

(Ruby kullanıyorum, ancak lütfen cevabınızı sınırlamasına izin vermeyin. Ruby'ye özgü bir kütüphane ya da başka bir şey varsa, bunu bilmek güzel.)

Yanıtlar:


93

Gelecekte tekrar eden tüm etkinlikler için 'bağlantı' konseptini kullanırdım. Takvimde dinamik olarak görüntülenirler ve tek bir referans nesnesine geri bağlanırlar. Olaylar gerçekleştiğinde bağlantı kopar ve olay bağımsız bir örnek olur. Yinelenen bir etkinliği düzenlemeye çalışırsanız, gelecekteki tüm öğeleri değiştirmeyi (yani tek bağlantılı başvuruyu değiştirmeyi) veya yalnızca bu örneği değiştirmeyi (bu durumda bunu tek başına bir örneğe dönüştürüp sonra değişiklik yapmayı) isteyin. Sonraki kasa, tek bir örneğe dönüştürülen gelecekteki tüm olaylar için tekrarlayan listenizde takip etmeniz gerektiğinden biraz sorunludur. Ancak, bu tamamen yapılabilir.

Yani, özünde, 2 olay sınıfı var - tek örnekler ve yinelenen olaylar.


Gerçekten, olayları geçtikten sonra bağımsız olarak bağlama ve dönüştürme fikriniz gibi. İki soru: - Bunları neden bağımsız sabit örneklere dönüştürmeliyim? Neden onları tamamen dinamik bırakmıyorsunuz? - Önerilen bağlantı konsepti için referans paylaşabilir misiniz? Şimdiden teşekkürler!
rtindru

@rtindru Olayları tek başına dönüştürmek için bulduğum bir kullanım durumu, olay modelini veritabanınızdaki diğer modellerle kullanmanız gerektiğidir . Örneğin, bir etkinliğe katılımı denetlemek için, kullanıcıları gerçekleşen (veya gerçekleşecek) gerçek bir etkinlikle ilişkilendirmek istersiniz.
Clinton Yeboah


33

Yinelenen etkinliklerle ilgili birçok sorun olabilir, bildiğim birkaç tanesini vurgulayayım.

Çözüm 1 - Örnek Yok

Orijinal randevu + yineleme verilerini depolayın, tüm örnekleri saklamayın.

sorunlar:

  • İhtiyacınız olduğunda bir tarih penceresindeki tüm örnekleri maliyetli bir şekilde hesaplamanız gerekir
  • İstisnalar işlenemiyor (örneğin, örneklerden birini siliyorsunuz veya taşıyamıyorsunuz veya daha doğrusu, bu çözümle bunu yapamazsınız)

Çözüm 2 - Örnekleri Saklayın

1'den her şeyi değil, aynı zamanda orijinal randevuya bağlı tüm örnekleri de saklayın.

sorunlar:

  • Çok yer kaplıyor (ama alan ucuz, çok küçük)
  • İstisnalar, özellikle bir istisna yaptıktan sonra geri dönüp orijinal randevuyu düzenlerseniz, ele alınmalıdır. Örneğin, üçüncü örneği bir gün ileri taşırsanız, geri dönüp orijinal randevunun saatini düzenlerseniz, orijinal güne başka bir tane ekleyip taşınan bir tanesini bırakırsanız ne olur? Taşınan kişinin bağlantısı kaldırılsın mı? Taşınan olanı uygun şekilde değiştirmeye çalışın mı?

Tabii ki, istisnalar yapmayacaksanız, her iki çözüm de iyi olmalı ve temel olarak bir zaman / uzay ticareti senaryosu arasından seçim yapmalısınız.


36
Bitiş tarihi olmayan yinelenen bir randevunuz varsa ne olur? Alan kadar ucuz, sonsuz alanınız yok, bu yüzden Çözüm 2 orada bir başlangıç ​​değil ...
Shaul Behr

13
1. çözüm aslında istisnaları kaldırabilir. Örneğin, RFC5545 bunların şu şekilde depolanmasını önerir: a) hariç tutulan tarihlerin listesi (bir örneği sildiğinizde); b) prototipe yapılan referanslarla "gerçekleşen" olaylar (bir olayı taşıdığınızda).
Andy Mikhaylenko

@Andy, Lasse'nin cevabına bazı ilginç eklemeler. Bunları deneyeceğim.
Jonathan Wilson

1
@Shaul: Bunun bir başlangıç ​​olmadığını düşünmüyorum. SO konusunda oldukça saygın olan John Skeet, oluşturulan örnekleri temel olarak aynı soruya cevabında saklamayı önermektedir: stackoverflow.com/a/10151804/155268
Kullanıcı

1
@Kullanıcı - teşekkür, teşekkür ederim. Çok garip - Yorumumu 4 yıl önce yaptım ve o zamandan beri bu sorunla başa çıkmak zorunda kalmadım. Daha dün yinelenen randevuları içeren yeni bir modül tasarladım ve bunları nasıl ele alacağımı merak ediyordum. Ve sonra - Bu sabah yorumunuzla ilgili bir SO bildirimi aldım. Ciddi ürkütücü! Ama teşekkür ederim! :-)
Shaul Behr

21

Birden çok takvim tabanlı uygulama geliştirdim ve ayrıca yinelemeyi destekleyen bir dizi yeniden kullanılabilir JavaScript takvim bileşeni yazdım. Birine yardımcı olabilecek nüks için nasıl tasarlanacağına dair bir genel bakış yazdım . Yazdığım kütüphaneye özgü birkaç bit olsa da, sunulan tavsiyenin büyük çoğunluğu herhangi bir takvim uygulaması için geneldir.

Önemli noktalardan bazıları:

  • İCal RRULE biçimini kullanarak yinelemeyi depolayın - bu gerçekten yeniden keşfetmek istemediğiniz tek bir tekerlek
  • Tek tek yinelenen olay örneklerini veritabanınızda satır olarak saklamayın ! Her zaman bir yineleme deseni saklayın.
  • Etkinlik / istisna şemanızı tasarlamanın birçok yolu vardır, ancak temel bir başlangıç ​​noktası örneği sağlanır
  • Tüm tarih / saat değerleri UTC'de saklanmalı ve görüntüleme için yerel olarak dönüştürülmelidir
  • Yinelenen bir etkinlik için depolanan bitiş tarihi her zaman yineleme aralığının bitiş tarihi olmalıdır (veya tekrar tekrar "sonsuza kadar" yineleniyorsa platformunuzun "maksimum tarihi") ve etkinlik süresi ayrı olarak saklanmalıdır. Bu, olayları daha sonra sorgulamak için akılcı bir yol sağlamaktır.
  • Etkinlik örnekleri oluşturma ve yineleme düzenleme stratejileri hakkında bazı tartışmalar dahil edilmiştir

Bu, uygulamayı uygulamak için birçok geçerli yaklaşımla gerçekten karmaşık bir konudur. Gerçekten birkaç kez başarılı bir şekilde nüks uyguladığımı söyleyeceğim ve bunu yapmayan herkesten bu konuda tavsiye almaya dikkat ediyorum.


Belki de yinelemeleri gerçekleştiklerinde etkinlik olarak depolayın, böylece takvim geçmişiniz doğru
Richard Haven

@RichardHaven Bunu asla yapmam. Her zaman tutarlı, geçmiş, şimdiki veya gelecekteki RRULE modellerinden örnekler oluşturmalısınız. Tarihsel olaylar için farklı bir şey yapmak için hiçbir sebep olmazdı. Mantık, bir RRULE'u herhangi bir rastgele tarih aralığına göre değerlendirmeli ve eşleşen olay örneklerini döndürmelidir.
Brian Moeskau

@BrianMoeskau güzel ve yararlı bir genel bakış!
Przemek Nowak

@BrianMoeskau Ancak, bazı olaylar gerçekleştikten sonra birisi RRULE'u düzenlediğinde takviminizin geçmiş görünümleri yanlış bilgiler göstermez mi? Ya da belki de bu durumda RRULE'u "dallar" ve RRULE modellerinin gerçek geçmiş olayları temsil eden değiştirilmiş versiyonlarını saklarsınız?
christian

1
@christian Çoğu takvimlerde bir yineleme kuralını güncellediğinizde, genellikle kullanıcının "davranışları seçmesine izin veren" tüm etkinlikleri veya yalnızca bu etkinliği düzenleyin veya yalnızca geleceği düzenleyin "gibi uyarırlar. Çoğu durumda, kullanıcı muhtemelen "ileriye doğru değiştir" anlamına gelir, ancak yine, yazılımınızın nasıl çalıştığına ve kullanıcıya hangi seçenekleri verdiğinize karar vermek size bağlıdır.
Brian Moeskau

19

İCalendar yazılım uygulamalarına veya standardın kendisine bakmak isteyebilirsiniz ( RFC 2445 RFC 5545 ). Hızlıca akla gelenler Mozilla projeleri http://www.mozilla.org/projects/calendar/ Hızlı bir arama http://icalendar.rubyforge.org/ da ortaya çıkıyor .

Etkinlikleri nasıl saklayacağınıza bağlı olarak diğer seçenekler de düşünülebilir. Kendi veritabanı şemanızı oluşturuyor musunuz? İCalendar tabanlı bir şey mi kullanıyorsunuz?


eğer bunlardan birine link verebilseydiniz yazınız mükemmel olurdu
Jean

7
Görünüşe göre RFC2445, RFC5545 tarafından kullanılmıyor ( tools.ietf.org/html/rfc5545 )
Eric Freese

16

Aşağıdakilerle çalışıyorum:

ve bir form.schedule :as => :recurringiCal benzeri arayüz oluşturan bir formtastic'i genişleten ve sürekli before_filterolarak bir IceCubenesneye serileştiren ghetto-ly gibi formtastik olan devam eden bir mücevher .

Benim fikrim, bir modele yinelenen özellikler eklemeyi ve görünümde kolayca bağlamayı inanılmaz hale getirmek. Hepsi birkaç satırda.


Peki bu bana ne veriyor? Dizine alınmış, Düzenlenebilir, Yinelenen özellikler.

eventsTek bir günde örneği ve takvim görünümünde kullanılan depolar / yardımcı söylemek task.scheduledepolar yaml'd IceCubesizin gibi aramaları yapabilir, böylece nesne: task.schedule.next_suggestion.

Özet: Takvim ekranı için bir daire ve işlevsellik için bir özellik olmak üzere iki model kullanıyorum.


Ne bulduğunuzu görmek isterdim. Herhangi bir yere git / blog / kavram kanıtı var mı? Teşekkürler!
montrealmike

Ben de benzer bir şey üzerinde çalışıyorum. Uygulamanızı görmek isterdim
thoughtpunch


5
  1. Bir yineleme kuralını takip edin (muhtemelen iCalendar'e göre, @ @ K.'ya göre ). Bu bir örüntü ve aralık içerecektir (Her üçüncü Salı, 10 kez).
  2. Eğer düzenlemek istediğinizde için / belli olayı silmek yukarıdaki tekrarlama kuralı (etkinlik tarihlerini için istisna tarihlerini takip etmez kural belirttiği olarak gerçekleşir).
  3. Sildiyseniz, ihtiyacınız olan tek şey budur, düzenlediyseniz başka bir etkinlik oluşturun ve bu etkinliğe ana etkinliğe ayarlanmış bir üst kimlik verin. Ana etkinliğin tüm bilgilerini bu kayda dahil edip etmeyeceğinizi veya yalnızca değişiklikleri tutup değişmeyen her şeyi miras alacağını seçebilirsiniz.

Bitmeyen tekrarlama kurallarına izin verirseniz, şimdi sonsuz miktarda bilginizi nasıl görüntüleyeceğinizi düşünmeniz gerektiğini unutmayın.

Umarım yardımcı olur!


4

Tarih kütüphanesinin gücünü ve ruby ​​aralık modülünün semantiğini kullanmanızı tavsiye ederim. Yinelenen bir etkinlik gerçekten bir saat, bir tarih aralığı (başlangıç ​​ve bitiş) ve genellikle haftanın tek bir günüdür. Tarih ve aralığı kullanarak herhangi bir soruyu cevaplayabilirsiniz:

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

Artık yıl da dahil olmak üzere etkinliğin tüm günlerini üretir !

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

2
Bu çok esnek değil. Yinelenen bir etkinlik modeli genellikle tekrarlama süresini (saatlik, haftalık, iki haftada bir vb.) Belirtmeyi gerektirir. Ayrıca, yineleme toplam sayı ile nitelenmeyebilir, son gerçekleşme için bir bitiş tarihi olabilir
Bo Jeanes

Bu sadece bir sınırlı kullanım durumdur "Bir tekrarlanan olay genellikle haftanın tek bir gün [..] şeklindedir" ve vb her ayın" nin '5 gün gibi birçok diğerleri işlemez
theraven

3

Bu cevaplardan bir çözüm buldum. Bağlantı kavramı fikrini gerçekten seviyorum. Yinelenen olaylar, kuyrukun yineleme kuralını bilmesi ile bağlantılı bir liste olabilir. Bir etkinliğin değiştirilmesi daha kolay olacaktır, çünkü bağlantılar yerinde kalır ve bir etkinliği silmek de kolaydır - bir etkinliğin bağlantısını kaldırır, siler ve etkinliğin öncesinde ve sonrasında yeniden bağlanırsınız. Hala yeni bir zaman dilimine bakıldığında tekrarlanan olayları sorgulamanız gerekir.


2

Olayları tekrarlanan olarak saklayabilirsiniz ve belirli bir örnek düzenlenmişse, aynı olay kimliğine sahip yeni bir olay oluşturun. Ardından etkinliği ararken, tüm bilgileri almak için aynı etkinlik kimliğine sahip tüm etkinlikleri arayın. Kendi etkinlik kitaplığınızı yuvarladığınızdan ya da var olan bir kütüphaneyi kullanıp kullanmadığınızdan emin değilim.


Bu çözümü bir kez kullandım. Modifiye edilmiş bir örneği, annesinin kim olduğunu bilen yeni bir kerelik bir etkinlik olarak saklama ilkesini seviyorum. Bu şekilde, child olayı için farklı olanlar dışındaki tüm alanları boş bırakabilirsiniz. Bu annenin hangi çocuğunu düzenlediğinizi belirten fazladan bir alanınızın olması gerektiğini unutmayın.
Wytze


1

Javascript dilinde:

Yinelenen programları işleme: http://bunkat.github.io/later/

Bu programlar arasındaki karmaşık olayları ve bağımlılıkları ele alma: http://bunkat.github.io/schedule/

Temel olarak, kuralları oluşturursunuz ve daha sonra lib'den bir sonraki N tekrarlayan olayı hesaplamasını istersiniz (bir tarih aralığı belirleyip belirtmeyin). Kurallar, modelinize kaydetmek için ayrıştırılabilir / serileştirilebilir.

Yinelenen bir etkinliğiniz varsa ve yalnızca bir yinelemeyi değiştirmek istiyorsanız, belirli bir günü kapatmak ve daha sonra bu girdi için yeni bir değiştirilmiş etkinlik eklemek için hariç () işlevini kullanabilirsiniz.

Lib, çok karmaşık desenleri, zaman dilimleri ve hatta croning olaylarını destekler.


0

Olayları yinelenen olarak saklayın ve dinamik olarak görüntüleyin, ancak yinelenen olayın belirli bir günde varsayılan bilgileri geçersiz kılabilecek belirli olayların bir listesini içermesine izin verin.

Yinelenen olayı sorguladığınızda, o gün için belirli bir geçersiz kılma olup olmadığını denetleyebilir.

Bir kullanıcı değişiklik yaparsa, tüm örnekler (varsayılan ayrıntılar) veya yalnızca o gün (yeni bir etkinlik yapın ve listeye ekleyin) güncellemek isteyip istemediğini sorabilirsiniz.

Bir kullanıcı bu etkinliğin tüm yinelemelerini silmeyi isterse, elinizin altında bulunan özelliklerin bir listesine sahip olursunuz ve bunları kolayca kaldırabilirsiniz.

Tek sorunlu durum, kullanıcının bu etkinliği ve gelecekteki tüm etkinlikleri güncellemek istemesi olabilir. Bu durumda yinelenen etkinliği ikiye bölmeniz gerekir. Bu noktada, yinelenen etkinlikleri bir şekilde birbirine bağlamayı düşünebilirsiniz; böylece tümünü silebilirsiniz.


0

Bazı lisans ücretlerini ödemeye hazırlanan .NET programcıları için Aspose.Network'ü faydalı bulabilirsiniz ... yinelenen randevular için iCalendar uyumlu bir kitaplık içerir.


0

Olayları doğrudan iCalendar biçiminde depolarsınız, bu da açık uçlu tekrarlamaya, saat dilimi yerelleştirmesine vb. Olanak tanır.

Bunları bir CalDAV sunucusunda saklayabilirsiniz ve daha sonra olayları görüntülemek istediğinizde, sunucudan görüntülenen süre boyunca yinelenen olayların genişletilmesini yapmasını istemek için CalDAV'da tanımlanan raporun seçeneğini kullanabilirsiniz.

Ya da bunları kendiniz bir veritabanında saklayabilir ve bir arka uç CalDAV sunucusuyla konuşmak için PUT / GET / REPORT'a gerek kalmadan genişletmeyi yapmak için bir çeşit iCalendar ayrıştırma kütüphanesi kullanabilirsiniz. Bu muhtemelen daha fazla iş - eminim CalDAV sunucuları bir yerde karmaşıklığı gizlemek.

Olayların iCalendar biçiminde olması, muhtemelen uzun vadede işleri daha basit hale getirecektir, çünkü insanlar her zaman yine de diğer yazılımlara koymak için dışa aktarılmasını isteyecektir.


0

Bu özelliği basitçe uyguladım! Mantık aşağıdaki gibidir, önce iki tabloya ihtiyacınız vardır. RuleTable genel depolayın veya baba olaylarını geri dönüştürün. ItemTable, saklanan döngü olaylarıdır. Örneğin, döngüsel bir etkinlik oluşturduğunuzda, 6 Kasım 2015 için başlangıç ​​zamanı, 6 Aralık (veya sonsuza kadar) için bitiş zamanı, bir haftalık döngü. Bir RuleTable'a veri eklersiniz, alanlar aşağıdaki gibidir:

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.

Şimdi 20 Kasım - 20 Aralık verilerini sorgulamak istiyorsunuz. Başlangıç ​​ve bitiş zamanına bağlı olarak RecurringEventBE (uzun başlangıç, uzun uç) işlevi yazabilirsiniz, WeekLy, istediğiniz koleksiyonu hesaplayabilirsiniz, <cycleA11.20, cycleA 11.27, cycleA 12.4 ......>. 6 Kasım'a ve geri kalanına ek olarak ona sanal bir olay dedim. Kullanıcı bir sanal olayın adını (örneğin cycleA11.27) sonra değiştirdiğinde, bir ItemTable'a veri eklersiniz. Alanlar aşağıdaki gibidir:

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

RecurringEventBE işlevinde (uzun başlangıç, uzun uç), İngilizcem için üzgünüm, sanal olayı (cycleB11.27) kapsayan bu verileri kullanıyorsunuz, denedim.

Bu benim YinelenenEtkinlikBE :

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); //RuleTablejust 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;
}   

-5

Bitiş tarihi olmayan yinelenen bir randevunuz varsa ne olur? Alan kadar ucuz, sonsuz alanınız yok, bu yüzden Çözüm 2 orada bir başlangıç ​​değil ...

"Hiçbir bitiş tarihinin" yüzyılın sonunda bir bitiş tarihine çözümlenemeyeceğini önerebilir miyim. Günlük bir olay için bile alan miktarı ucuz kalır.


7
Y2k'nin derslerini ne kadar sürede unutuyoruz ... :)
Ian Mercer

10
Her biri birkaç günlük etkinliğe sahip 1000 kullanıcımız olduğunu varsayalım. 3 etkinlik × 1000 kullanıcı × 365 gün × (2100-2011 = 89 yıl) = 97,5 milyon kayıt. 3000 "plan" yerine. Um ...
Andy Mikhaylenko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.