कैलेंडर एप्लिकेशन में आवर्ती घटनाओं को मॉडल करने का सबसे अच्छा तरीका क्या है?


224

मैं एक समूह कैलेंडर एप्लिकेशन का निर्माण कर रहा हूं, जिसे आवर्ती घटनाओं का समर्थन करने की आवश्यकता है, लेकिन इन घटनाओं को संभालने के लिए मैं जो भी समाधान लेकर आया हूं वह एक हैक की तरह प्रतीत होता है। मैं सीमित कर सकता हूं कि कोई कितना आगे देख सकता है, और फिर एक बार में सभी घटनाओं को उत्पन्न कर सकता है। या मैं घटनाओं को दोहराते हुए स्टोर कर सकता हूं और जब कोई कैलेंडर पर आगे देखता है तो डायनामिक रूप से प्रदर्शित कर सकता है, लेकिन अगर किसी व्यक्ति को ईवेंट के किसी विशेष उदाहरण पर विवरण बदलना हो तो मुझे उसे एक सामान्य ईवेंट में बदलना होगा।

मुझे यकीन है कि ऐसा करने का एक बेहतर तरीका है, लेकिन मैंने इसे अभी तक नहीं पाया है। आवर्ती घटनाओं को मॉडल करने का सबसे अच्छा तरीका क्या है, जहां आप किसी विशेष घटना के विवरण को बदल सकते हैं या हटा सकते हैं?

(मैं रूबी का उपयोग कर रहा हूं, लेकिन कृपया अपना उत्तर न दें। यदि कोई रूबी-विशिष्ट पुस्तकालय या कुछ है, हालांकि, यह जानना अच्छा है।)

जवाबों:


93

मैं भविष्य की सभी आवर्ती घटनाओं के लिए एक 'लिंक' अवधारणा का उपयोग करूंगा। वे गतिशील रूप से कैलेंडर में प्रदर्शित होते हैं और एक ही संदर्भ वस्तु पर वापस लिंक करते हैं। जब घटनाएं हुई हैं तो लिंक टूट गया है और घटना एक स्टैंडअलोन उदाहरण बन गई है। यदि आप किसी आवर्ती घटना को संपादित करने का प्रयास करते हैं, तो भविष्य की सभी वस्तुओं को बदलने (यानी एकल लिंक किए गए संदर्भ को बदलने) या बस उस उदाहरण को बदलने के लिए (जिस स्थिति में इसे स्टैंडअलोन उदाहरण में परिवर्तित करें और फिर परिवर्तन करें)। उत्तरार्द्ध आवरण थोड़ा समस्याग्रस्त है क्योंकि आपको भविष्य की सभी घटनाओं की अपनी आवर्ती सूची में ट्रैक रखने की आवश्यकता है जो एकल उदाहरण में परिवर्तित हो गए थे। लेकिन, यह पूरी तरह से सक्षम है।

तो, संक्षेप में, घटनाओं के 2 वर्ग हैं - एकल उदाहरण और आवर्ती घटनाएं।


वास्तव में घटनाओं को जोड़ने और बदलने के बाद उन्हें स्टैंडअलोन में परिवर्तित करने के आपके विचार की तरह। दो सवाल: - क्यों उन्हें स्टैंडअलोन निश्चित उदाहरणों में परिवर्तित करें? उन्हें पूरी तरह से गतिशील क्यों नहीं छोड़ते? - आप प्रस्तावित लिंक अवधारणा के लिए संदर्भ साझा कर सकते हैं! अग्रिम में धन्यवाद!
rtindru

@rtindru एक उपयोग का मामला जो मैंने ईवेंट्स को स्टैंडअलोन में परिवर्तित करने के लिए पाया है, जब आपको अपने डेटाबेस में अन्य मॉडलों के साथ ईवेंट मॉडल का उपयोग करना होगा । उदाहरण के लिए, किसी ईवेंट के लिए उपस्थिति की जाँच करने के लिए, आप उपयोगकर्ताओं को एक वास्तविक ईवेंट के साथ जोड़ना चाहेंगे जो हुआ (या होगा)।
क्लिंटन येओबा

60

मार्टिन फाउलर - कैलेंडर्स के लिए आवर्ती घटनाओं में कुछ दिलचस्प अंतर्दृष्टि और पैटर्न शामिल हैं।

रंट रत्न इस पैटर्न को लागू करता है।


कुछ बेहतर कोड उदाहरण अच्छे होंगे, किसी को भी इस परियोजना को लागू करने का पता है?
तदेउ मया

33

आवर्ती घटनाओं के साथ कई समस्याएं हो सकती हैं, मुझे कुछ को उजागर करना चाहिए जो मुझे पता है।

समाधान 1 - कोई उदाहरण नहीं

मूल नियुक्ति + पुनरावृत्ति डेटा स्टोर करें, सभी उदाहरणों को संग्रहीत न करें।

समस्या:

  • जब आपको उनकी आवश्यकता होगी, तो आपको एक तारीख विंडो में सभी उदाहरणों की गणना करना होगा
  • अपवादों को संभालने में असमर्थ (यानी। आप किसी एक इंस्टेंस को हटा दें, या उसे स्थानांतरित कर दें, या इसके बजाय, आप इस समाधान के साथ ऐसा नहीं कर सकते)

समाधान 2 - दुकान उदाहरण

1 से सब कुछ स्टोर करें, लेकिन सभी उदाहरण भी, मूल नियुक्ति से जुड़े हुए हैं।

समस्या:

  • बहुत जगह लेता है (लेकिन अंतरिक्ष सस्ता है, इसलिए मामूली है)
  • अपवादों को इनायत से संभाला जाना चाहिए, खासकर यदि आप वापस जाते हैं और अपवाद बनाने के बाद मूल नियुक्ति को संपादित करते हैं। उदाहरण के लिए, यदि आप एक दिन आगे तीसरे उदाहरण को स्थानांतरित करते हैं, तो क्या होगा यदि आप मूल नियुक्ति के समय को संपादित करते हैं, मूल दिन पर एक और फिर से डालें और स्थानांतरित एक को छोड़ दें? एक स्थानांतरित करें? स्थानांतरित एक को उचित रूप से बदलने का प्रयास करें?

बेशक, अगर आप अपवाद नहीं करने जा रहे हैं, तो या तो समाधान ठीक होना चाहिए, और आप मूल रूप से एक समय / अंतरिक्ष व्यापार से परिदृश्य का चयन करते हैं।


36
यदि आपके पास कोई समाप्ति तिथि नहीं है तो क्या होगा? अंतरिक्ष जितना सस्ता है, आपके पास अनंत स्थान नहीं है, इसलिए समाधान 2 वहां एक गैर-स्टार्टर है ...
शूल बहर

13
समाधान # 1 वास्तव में अपवादों को संभाल सकता है। उदाहरण के लिए, RFC5545 का सुझाव है कि वे इस प्रकार संग्रहीत हैं: क) बहिष्कृत तिथियों की एक सूची (जब आप कोई घटना हटाते हैं); बी) प्रोटोटाइप के संदर्भ में "भौतिककृत" घटनाएं (जब आप एक घटना को आगे बढ़ाते हैं)।
एंडी मिखाइलेंको

@ और, लास के जवाब में कुछ दिलचस्प जोड़। गोना उन लोगों को एक कोशिश दे।
जोनाथन विल्सन

1
@ शुल: मुझे नहीं लगता कि यह एक गैर स्टार्टर है। जॉन स्कीट, जो एसओ पर बहुत सम्मानित हैं, का सुझाव है कि मूल रूप से एक ही सवाल के जवाब में उत्पन्न उदाहरणों को संग्रहीत करना: stackoverflow.com/a/10151804/155268
उपयोगकर्ता

1
@ उपयोगकर्ता - स्वीकार किया, धन्यवाद। यह बहुत अजीब है - मैंने 4 साल पहले अपनी टिप्पणी की थी, और तब से इस मुद्दे से निपटने के लिए मुझे वास्तव में ज़रूरत नहीं है। कल ही मैं एक नया मॉड्यूल डिजाइन कर रहा हूं जिसमें आवर्ती नियुक्तियों को शामिल किया गया है, और मैं सोच रहा था कि उन्हें कैसे संभालना है। और फिर - मुझे आज सुबह आपकी टिप्पणी का SO सूचना मिली। गंभीर रूप से डरावना! लेकिन आपका शुक्रिया! :-)
शूल बेहार

20

मैंने कई कैलेंडर-आधारित एप्लिकेशन विकसित किए हैं, और पुन: प्रयोज्य जावास्क्रिप्ट कैलेंडर घटकों का एक सेट भी लिखा है जो पुनरावृत्ति का समर्थन करते हैं। मैंने पुनरावृत्ति के लिए डिज़ाइन करने के तरीके का अवलोकन किया जो किसी के लिए उपयोगी हो सकता है। जबकि कुछ बिट्स हैं जो मेरे द्वारा लिखी गई लाइब्रेरी के लिए विशिष्ट हैं, प्रस्तावित सलाह का अधिकांश हिस्सा किसी भी कैलेंडर कार्यान्वयन के लिए सामान्य है।

कुछ प्रमुख बिंदु:

  • ICal RRULE प्रारूप का उपयोग करके स्टोर पुनरावृत्ति - वह एक पहिया है जिसे आप वास्तव में सुदृढ़ नहीं करना चाहते हैं
  • अपने डेटाबेस में पंक्तियों के रूप में व्यक्तिगत आवर्ती घटना उदाहरणों को संग्रहीत न करें! हमेशा एक पुनरावृत्ति पैटर्न स्टोर करें।
  • अपने ईवेंट / अपवाद स्कीमा को डिज़ाइन करने के कई तरीके हैं, लेकिन एक मूल प्रारंभिक बिंदु उदाहरण प्रदान किया गया है
  • सभी दिनांक / समय मानों को UTC में संग्रहीत किया जाना चाहिए और प्रदर्शन के लिए स्थानीय में परिवर्तित किया जाना चाहिए
  • किसी आवर्ती घटना के लिए संग्रहीत अंतिम तिथि हमेशा आवर्ती श्रेणी की अंतिम तिथि होनी चाहिए (या आपके प्लेटफ़ॉर्म की "अधिकतम तिथि" यदि आवर्ती "हमेशा") और घटना की अवधि अलग से संग्रहीत की जानी चाहिए। यह बाद में घटनाओं के लिए क्वेरी करने का एक सुनिश्चित तरीका है।
  • घटना की घटनाओं को उत्पन्न करने और पुनरावृत्ति संपादन रणनीतियों के आसपास कुछ चर्चा शामिल है

यह एक बहुत ही जटिल विषय है जिसमें इसे लागू करने के लिए कई मान्य दृष्टिकोण हैं। मैं कहूंगा कि मैंने वास्तव में सफलतापूर्वक कई बार पुनरावृत्ति को लागू किया है, और मैं इस विषय पर किसी ऐसे व्यक्ति से सलाह लेने से सावधान रहूंगा, जिसने वास्तव में ऐसा नहीं किया है।


शायद घटनाओं के रूप में पुनरावृत्ति को स्टोर करें जब वे होते हैं तो आपका कैलेंडर इतिहास सटीक होता है
रिचर्ड हैवेन

@ ऋचाधवन मैं ऐसा कभी नहीं करूंगा। आपको हमेशा RRULE पैटर्न से लगातार, अतीत, वर्तमान या भविष्य में उदाहरण उत्पन्न करने चाहिए। ऐतिहासिक घटनाओं के लिए कुछ अलग करने का कोई कारण नहीं होगा। आपके तर्क को किसी भी मनमानी तिथि सीमा के खिलाफ एक RRULE का मूल्यांकन करना चाहिए और मिलान घटना उदाहरणों को वापस करना चाहिए।
ब्रायन Moeskau

@BrianMoeskau अच्छा और उपयोगी अवलोकन!
प्रेज़ेमेक नाऊक

@BrianMoeskau लेकिन तब आपके कैलेंडर के विचारों को गलत नहीं दिखाएगा जब कोई व्यक्ति पहले से ही कुछ घटनाओं के बाद RRULE का संपादन करता है? या शायद उस स्थिति में आप RRULE को "ब्रांच" करेंगे और वास्तविक वास्तविक घटनाओं का प्रतिनिधित्व करने वाले RRULE पैटर्न के संशोधित संस्करण रखेंगे?
क्रिश्चियन

1
@christian जब आप अधिकांश कैलेंडर में एक पुनरावृत्ति नियम को अपडेट करते हैं, तो वे आमतौर पर "सभी घटनाओं को संपादित करें, या यह केवल या भविष्य में केवल" उपयोगकर्ता को व्यवहार का चयन करने की अनुमति देते हैं। ज्यादातर मामलों में, उपयोगकर्ता का अर्थ है कि "इसे आगे बढ़ाना बदल दें" लेकिन फिर, यह आपके लिए तय करना है कि आपका सॉफ़्टवेयर कैसे काम करता है और उपयोगकर्ता को क्या विकल्प देता है।
ब्रायन Moeskau

19

आप iCalendar सॉफ़्टवेयर कार्यान्वयन या मानक स्वयं देखना चाहते हैं ( RFC 2445 RFC 5545 )। जल्दी से दिमाग में आने के लिए, मोज़िला परियोजनाएं हैं http://www.mozilla.org/projects/calendar/ एक त्वरित खोज http://icalendar.rubyforge.org/ भी बताती है ।

अन्य विकल्पों पर विचार किया जा सकता है कि आप घटनाओं को कैसे स्टोर करते हैं। क्या आप अपना खुद का डेटाबेस स्कीमा बना रहे हैं? कुछ का उपयोग कर iCalendar- आधारित, आदि?


यदि आप इनमें से किसी एक को एक लिंक प्रदान कर सकते हैं तो आपकी पोस्ट परिपूर्ण होगी
जीन

7
RFC2445 की तरह लग रहा है RFC5545 ( tools.ietf.org/html/rfc5545 ) द्वारा अप्रचलित कर दिया गया है
एरिक

16

मैं निम्नलिखित के साथ काम कर रहा हूँ:

और प्रगति में एक मणि जो एक इनपुट प्रकार के साथ फॉर्मटैस्टिक का विस्तार करता है: आवर्ती ( form.schedule :as => :recurring), जो एक iCal- जैसे इंटरफ़ेस को प्रस्तुत करता है और एक वस्तु को फिर से before_filterदेखने के लिए IceCube, यहूदी बस्ती- गीत को अनुक्रमित करता है ।

मेरा विचार एक मॉडल में आवर्ती विशेषताओं को जोड़ना और इसे आसानी से दृश्य में कनेक्ट करना अविश्वसनीयता बनाना आसान है। सभी लाइनों के एक जोड़े में।


तो यह मुझे क्या देता है? अनुक्रमित, संपादन-सक्षम, आवर्ती विशेषताएँ।

eventsएक ही दिन के उदाहरणों को संग्रहीत करता है, और कैलेंडर दृश्य में उपयोग किया जाता है / सहायक कहते task.scheduleहैं कि यामलैड IceCubeऑब्जेक्ट को स्टोर करता है , इसलिए आप कॉल जैसे कर सकते हैं task.schedule.next_suggestion:।

रिकैप: मैं कैलेंडर प्रदर्शन के लिए दो मॉडल, एक फ्लैट, और कार्यक्षमता के लिए एक विशेषता का उपयोग करता हूं।


मुझे यह देखने में दिलचस्पी होगी कि आप क्या लेकर आए हैं। क्या आपके पास एक git / ब्लॉग / अवधारणा का प्रमाण कहीं भी है? धन्यवाद!
मॉन्ट्रियलमाइक

मैं भी कुछ इसी तरह काम कर रहा हूं। अपने कार्यान्वयन को देखने के लिए प्यार करोगे
thoughtpunch

6

मैं पुनरावृत्ति मापदंडों को संग्रहीत करने के लिए नीचे वर्णित डेटाबेस स्कीमा का उपयोग कर रहा हूं

http://github.com/bakineggs/recurring_events_for

फिर मैं तारीखों को गतिशील रूप से गणना करने के लिए रनट का उपयोग करता हूं।

https://github.com/mlipper/runt


5
  1. एक पुनरावृत्ति नियम का ध्यान रखें (शायद iCalendar पर आधारित, प्रति @ Kris K. )। इसमें एक पैटर्न और एक सीमा (प्रत्येक तीसरे मंगलवार को 10 घटनाओं के लिए) शामिल होगी।
  2. जब आप किसी विशिष्ट घटना को संपादित / हटाना चाहते हैं, तो उपरोक्त पुनरावृत्ति नियम के लिए अपवाद तिथियों का ट्रैक रखें (वे तारीखें जहाँ घटना घटती नहीं है जैसा नियम निर्दिष्ट करता है)।
  3. यदि आपने हटा दिया है, तो आपको इसकी आवश्यकता है, यदि आपने संपादित किया है, तो एक और ईवेंट बनाएं, और इसे मुख्य इवेंट के लिए एक पैरेंट आईडी सेट करें। आप चुन सकते हैं कि इस रिकॉर्ड में मुख्य घटना की सभी जानकारी शामिल है, या यदि यह केवल परिवर्तन रखती है और जो कुछ भी नहीं बदलती है, उसे विरासत में मिली है।

ध्यान दें कि यदि आप पुनरावृत्ति नियमों की अनुमति देते हैं जो समाप्त नहीं होते हैं, तो आपको यह सोचना होगा कि आपकी अब की अनंत जानकारी को कैसे प्रदर्शित किया जाए।

उम्मीद है की वो मदद करदे!


4

मैं तिथि पुस्तकालय की शक्ति और माणिक के रेंज मॉड्यूल के शब्दार्थ का उपयोग करने की सलाह दूंगा। आवर्ती घटना वास्तव में एक समय, एक तिथि सीमा (एक शुरुआत और अंत) और आमतौर पर सप्ताह का एक दिन होता है। दिनांक और सीमा का उपयोग करके आप किसी भी प्रश्न का उत्तर दे सकते हैं:

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

2
यह बहुत लचीला नहीं है। आवर्ती ईवेंट मॉडल को अक्सर पुनरावृत्ति अवधि (प्रति घंटा, साप्ताहिक, पाक्षिक, आदि) निर्दिष्ट करने की आवश्यकता होती है। इसके अतिरिक्त पुनरावृत्ति कुल संख्या द्वारा योग्य नहीं हो सकती है, बल्कि अंतिम घटना के लिए अंतिम तिथि
बो जीनस

"एक आवर्ती घटना है [..] आम तौर पर सप्ताह का एक दिन", यह सिर्फ एक सीमित उपयोग का मामला है और कई अन्य को संभाल नहीं करता है जैसे कि 'हर महीने का 5 वां दिन' आदि
theraven

3

इन उत्तरों से, मैंने एक समाधान निकाला है। मैं वास्तव में लिंक अवधारणा के विचार को पसंद करता हूं। आवर्ती घटना एक पुनरावर्ती नियम हो सकती है, पूंछ के साथ इसकी पुनरावृत्ति नियम को जान सकती है। किसी एक ईवेंट को बदलना तब आसान होगा, क्योंकि लिंक यथावत बने रहते हैं, और किसी ईवेंट को हटाना भी आसान होता है - आप किसी ईवेंट को अनलिंक करते हैं, उसे हटाते हैं, और ईवेंट को उसके पहले और बाद में पुनः लिंक करते हैं। आपको अभी भी आवर्ती घटनाओं को क्वेरी करना होगा जब भी कोई व्यक्ति किसी नए समय अवधि को देखता है उसे कैलेंडर पर पहले कभी नहीं देखा गया है, लेकिन अन्यथा यह बहुत साफ है।


2

आप दोहराव के रूप में घटनाओं को संग्रहीत कर सकते हैं, और यदि कोई विशेष उदाहरण संपादित किया गया था, तो उसी ईवेंट आईडी के साथ एक नया ईवेंट बनाएं। फिर जब घटना को देखते हैं, तो सभी जानकारी प्राप्त करने के लिए एक ही घटना आईडी के साथ सभी घटनाओं की खोज करें। मुझे यकीन नहीं है कि आपने अपनी खुद की ईवेंट लाइब्रेरी को रोल किया है, या यदि आप किसी मौजूदा का उपयोग कर रहे हैं तो यह संभव नहीं हो सकता है।


मैंने एक बार इस घोल का इस्तेमाल किया। मुझे एक नई एक बार की घटना के रूप में एक संशोधित उदाहरण संग्रहीत करने का सिद्धांत पसंद है जो जानता है कि इसका मामा कौन है। इस तरह से आप उन सभी क्षेत्रों को खाली छोड़ सकते हैं, जो बाल घटना के लिए अलग हैं। ध्यान दें कि आपको एक अतिरिक्त फ़ील्ड निर्दिष्ट करना होगा जो इस माँ के बच्चे को आप संपादित कर रहे हैं।
Wytze

2

तीन अच्छी रूबी तिथि / समय पुस्तकालयों के लिए नीचे दिए गए लेख की जाँच करें। विशेष रूप से आइस_ क्यूब पुनरावृत्ति नियमों और अन्य सामानों के लिए एक ठोस विकल्प लगता है जो एक इवेंट कैलेंडर की आवश्यकता होगी। http://www.rubyinside.com/3-new-date-and-time-libraries-for-rubyists-3238.html


1

जावास्क्रिप्ट में:

आवर्ती अनुसूची को संभालना: http://bunkat.github.io/later/

उन कार्यक्रमों के बीच जटिल घटनाओं और निर्भरता को संभालना: http://bunkat.github.io/schedule/

मूल रूप से, आप नियम बनाते हैं, फिर आप अगले N आवर्ती घटनाओं की गणना करने के लिए कार्यविधि पूछते हैं (एक तिथि सीमा या नहीं निर्दिष्ट करते हुए)। उन्हें आपके मॉडल में सहेजने के लिए नियमों को पार्स / क्रमबद्ध किया जा सकता है।

यदि आपके पास पुनरावर्ती ईवेंट है और आप केवल एक पुनरावृत्ति को संशोधित करना चाहते हैं, तो आप किसी विशेष दिन को खारिज करने के लिए () फ़ंक्शन का उपयोग कर सकते हैं और फिर इस प्रविष्टि के लिए एक नया संशोधित ईवेंट जोड़ सकते हैं।

लिब बहुत जटिल पैटर्न, टाइमज़ोन और यहां तक ​​कि क्रोनिंग घटनाओं का समर्थन करता है।


0

घटनाओं को दोहराए जाने के रूप में संग्रहीत करें और गतिशील रूप से उन्हें प्रदर्शित करें, हालांकि आवर्ती घटना को उन विशिष्ट घटनाओं की सूची में शामिल करने की अनुमति देता है जो किसी विशिष्ट दिन पर डिफ़ॉल्ट जानकारी को ओवरराइड कर सकते हैं।

जब आप पुनरावर्ती ईवेंट की क्वेरी करते हैं तो यह उस दिन के लिए एक विशिष्ट ओवरराइड के लिए जाँच कर सकता है।

यदि कोई उपयोगकर्ता परिवर्तन करता है, तो आप पूछ सकते हैं कि क्या वह सभी उदाहरणों के लिए (डिफ़ॉल्ट विवरण) अपडेट करना चाहता है या बस उस दिन (एक नई विशिष्ट घटना बनाएं और इसे सूची में जोड़ें)।

यदि कोई उपयोगकर्ता इस घटना के सभी पुनरावृत्ति को हटाने के लिए कहता है, तो आपके पास हाथ करने के लिए बारीकियों की सूची भी है और उन्हें आसानी से हटा सकते हैं।

केवल समस्याग्रस्त मामला होगा यदि उपयोगकर्ता इस घटना और भविष्य की सभी घटनाओं को अपडेट करना चाहता है। किस स्थिति में आपको आवर्ती घटना को दो में विभाजित करना होगा। इस बिंदु पर आप किसी तरह से आवर्ती घटनाओं को जोड़ने पर विचार कर सकते हैं ताकि आप उन सभी को हटा सकें।


0

.NET प्रोग्रामर जो कुछ लाइसेंस फीस देने के लिए तैयार हैं, के लिए आपको Aspose.Network उपयोगी मिल सकता है ... इसमें पुनरावर्ती नियुक्तियों के लिए iCalendar संगत लाइब्रेरी शामिल है।


0

आप सीधे iCalendar प्रारूप में घटनाओं को संग्रहीत करते हैं, जो ओपन-एंडेड पुनरावृत्ति, समय-क्षेत्र स्थानीयकरण और इसके आगे की अनुमति देता है।

आप इन्हें एक CalDAV सर्वर में संग्रहीत कर सकते हैं और फिर जब आप उन घटनाओं को प्रदर्शित करना चाहते हैं जो आप CalDAV में परिभाषित रिपोर्ट के विकल्प का उपयोग कर सकते हैं, तो सर्वर से पूछ सकते हैं कि वह आवर्ती घटनाओं की पुनरावृत्ति की घटनाओं का विस्तार कर सके।

या आप एक डेटाबेस में खुद को स्टोर कर सकते हैं और बैकएंड CalDAV सर्वर से बात करने के लिए PUT / GET / REPORT की आवश्यकता के बिना विस्तार करने के लिए किसी तरह के iCalendar पार्सिंग लाइब्रेरी का उपयोग कर सकते हैं। यह शायद अधिक काम है - मुझे यकीन है कि CalDAV सर्वर कहीं जटिलता छिपाते हैं।

ICalendar प्रारूप में घटनाओं के होने से संभवत: लंबे समय में चीजें सरल हो जाएंगी क्योंकि लोग हमेशा चाहेंगे कि उन्हें किसी अन्य सॉफ़्टवेयर में डालने के लिए निर्यात किया जाए।


0

मैंने बस इस सुविधा को लागू किया है! लॉजिक निम्नानुसार है, पहले आपको दो तालिकाओं की आवश्यकता है। RuleTable स्टोर सामान्य या पितृ घटनाओं को रीसायकल करता है। ItemTable चक्र घटनाओं को संग्रहीत किया जाता है। उदाहरण के लिए, जब आप चक्रीय घटना बनाते हैं, तो 6 नवंबर 2015 का प्रारंभ समय, 6 दिसंबर का अंतिम समय (या हमेशा के लिए), एक सप्ताह के लिए चक्र। आप एक नियम में डेटा सम्मिलित करते हैं, फ़ील्ड निम्नानुसार हैं:

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 (लंबी शुरुआत, लंबी समाप्ति), शुरुआती और समाप्ति समय के आधार पर, वीकली, आप अपने इच्छित संग्रह की गणना कर सकते हैं, <cycleA11.20, cycleA 11.27, cycleA 12.4 ......>। 6 नवंबर के अलावा, और बाकी मैंने उसे एक आभासी घटना कहा। जब उपयोगकर्ता एक वर्चुअल ईवेंट का नाम (उदाहरण के लिए cycleA11.27) बदलता है, तो आप एक आइटम को एक आइटम में सम्मिलित करते हैं। फ़ील्ड इस प्रकार हैं:

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

RecurringEventBE (लॉन्ग स्टार्ट, लॉन्ग एंड) फंक्शन में, आप इस डेटा को वर्चुअल ईवेंट को कवर करने के लिए इस्तेमाल करते हैं (साइकल बी 11.27) माई इंग्लिश के बारे में क्षमा करें, मैंने कोशिश की।

यह मेरा आवर्ती है BE

public static List<Map<String, Object>> recurringData(Context context,
        long start, long end) { // 重复事件的模板处理,生成虚拟事件(根据日期段)
     long a = System.currentTimeMillis();
    List<Map<String, Object>> finalDataList = new ArrayList<Map<String, Object>>();

    List<Map<String, Object>> tDataList = BillsDao.selectTemplateBillRuleByBE(context); //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

यदि आपके पास कोई समाप्ति तिथि नहीं है तो क्या होगा? अंतरिक्ष जितना सस्ता है, आपके पास अनंत स्थान नहीं है, इसलिए समाधान 2 वहां एक गैर-स्टार्टर है ...

मई मैं सुझाव देता हूं कि "कोई समाप्ति तिथि" सदी के अंत में अंतिम तिथि तक नहीं सुलझाई जा सकती। यहां तक ​​कि एक दिन की घटना के लिए अंतरिक्ष की मात्रा सस्ती रहती है।


7
कितनी जल्दी हम y2k के पाठ को भूल जाते हैं ... :)
इयान मर्सर

10
मान लें कि हमारे पास 1000 उपयोगकर्ता हैं, जिनमें से प्रत्येक दैनिक घटनाओं में से एक है। 3 घटनाएं × 1000 उपयोगकर्ता × 365 दिन × (2100-2011 = 89 वर्ष) = 97.5 मिलियन रिकॉर्ड। 3000 "योजनाओं" के बजाय। उम ...
एंडी मिखाइलेंको
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.