วิธีที่ดีที่สุดในการจำลองเหตุการณ์ที่เกิดขึ้นในแอปพลิเคชันปฏิทินคืออะไร


224

ฉันกำลังสร้างแอปพลิเคชันปฏิทินกลุ่มที่ต้องการสนับสนุนกิจกรรมที่เกิดซ้ำ แต่โซลูชันทั้งหมดที่ฉันใช้เพื่อจัดการเหตุการณ์เหล่านี้ดูเหมือนจะเป็นแฮ็ค ฉันสามารถ จำกัด ว่าจะดูได้ไกลแค่ไหนแล้วสร้างกิจกรรมทั้งหมดในครั้งเดียว หรือฉันสามารถจัดเก็บกิจกรรมเป็นการทำซ้ำและแสดงแบบไดนามิกเมื่อมองไปข้างหน้าในปฏิทิน แต่ฉันจะต้องแปลงให้เป็นกิจกรรมปกติหากมีคนต้องการเปลี่ยนรายละเอียดในกรณีเฉพาะของเหตุการณ์

ฉันแน่ใจว่ามีวิธีที่ดีกว่าในการทำเช่นนี้ แต่ฉันยังไม่พบ วิธีที่ดีที่สุดในการจำลองเหตุการณ์ที่เกิดขึ้นซ้ำซึ่งคุณสามารถเปลี่ยนรายละเอียดหรือลบอินสแตนซ์ของเหตุการณ์ได้คืออะไร

(ฉันใช้ Ruby แต่โปรดอย่าปล่อยให้สิ่งนั้น จำกัด คำตอบของคุณหากมีไลบราเฉพาะของ Ruby หรือบางอย่างมันก็เป็นการดีที่จะรู้)

คำตอบ:


93

ฉันจะใช้แนวคิด 'ลิงก์' สำหรับกิจกรรมที่เกิดซ้ำในอนาคตทั้งหมด พวกเขาจะแสดงแบบไดนามิกในปฏิทินและลิงค์กลับไปยังวัตถุอ้างอิงเดียว เมื่อมีเหตุการณ์เกิดขึ้นลิงค์จะเสียหายและเหตุการณ์จะกลายเป็นอินสแตนซ์แบบสแตนด์อโลน หากคุณพยายามแก้ไขเหตุการณ์ที่เกิดซ้ำให้แจ้งให้เปลี่ยนรายการในอนาคตทั้งหมด (เช่นเปลี่ยนการอ้างอิงลิงก์เดียว) หรือเปลี่ยนเฉพาะอินสแตนซ์นั้น (ในกรณีนี้ให้แปลงเป็นอินสแตนซ์แบบสแตนด์อโลนแล้วทำการเปลี่ยนแปลง) กรณีหลังมีปัญหาเล็กน้อยตามที่คุณต้องการติดตามในรายการที่เกิดขึ้นประจำของเหตุการณ์ในอนาคตทั้งหมดที่ถูกแปลงเป็นอินสแตนซ์เดียว แต่ทั้งหมดนี้ไม่สามารถทำได้

โดยพื้นฐานแล้วมีเหตุการณ์ 2 ระดับ - อินสแตนซ์เดียวและเหตุการณ์ที่เกิดซ้ำ


ชอบแนวคิดของคุณในการเชื่อมโยงและแปลงกิจกรรมเป็นแบบสแตนด์อโลนหลังจากผ่านไปแล้ว สองคำถาม: - ทำไมแปลงเป็นอินสแตนซ์คงที่แบบสแตนด์อโลนเลย? ทำไมไม่ปล่อยให้พวกมันมีพลังอย่างสมบูรณ์ล่ะ? - คุณสามารถแบ่งปันการอ้างอิงสำหรับแนวคิดลิงก์ที่เสนอ! ขอบคุณล่วงหน้า!
rtindru

@rtindru กรณีการใช้งานที่ฉันพบสำหรับการแปลงเหตุการณ์เป็นแบบสแตนด์อโลนคือเมื่อคุณต้องใช้โมเดลเหตุการณ์กับโมเดลอื่น ๆ ในฐานข้อมูลของคุณ ตัวอย่างเช่นสำหรับการตรวจสอบการเข้าร่วมสำหรับกิจกรรมคุณจะต้องเชื่อมโยงผู้ใช้กับเหตุการณ์จริงที่เกิดขึ้น (หรือจะเกิดขึ้น)
Clinton Yeboah

60

Martin Fowler - กิจกรรมที่เกิดซ้ำสำหรับปฏิทินมีข้อมูลเชิงลึกและรูปแบบที่น่าสนใจ

อัญมณีRuntใช้รูปแบบนี้


ตัวอย่างโค้ดที่ดีกว่าน่าจะดีใคร ๆ ก็รู้ว่ามีโครงการที่ใช้สิ่งนี้
Tadeu Maia

33

อาจมีปัญหามากมายเกี่ยวกับเหตุการณ์ที่เกิดขึ้นให้ฉันเน้นบางอย่างที่ฉันรู้

โซลูชันที่ 1 - ไม่มีอินสแตนซ์

เก็บข้อมูลการนัดหมาย + การเกิดซ้ำดั้งเดิมอย่าเก็บอินสแตนซ์ทั้งหมด

ปัญหา:

  • คุณจะต้องคำนวณอินสแตนซ์ทั้งหมดในหน้าต่างวันที่เมื่อคุณต้องการ
  • ไม่สามารถจัดการข้อยกเว้น (เช่นคุณลบหนึ่งในอินสแตนซ์หรือย้ายมันหรือคุณไม่สามารถทำได้ด้วยวิธีนี้)

โซลูชันที่ 2 - เก็บอินสแตนซ์

เก็บทุกอย่างจาก 1 แต่รวมถึงอินสแตนซ์ทั้งหมดซึ่งเชื่อมโยงกลับไปที่การนัดหมายดั้งเดิม

ปัญหา:

  • ใช้พื้นที่จำนวนมาก (แต่พื้นที่มีราคาถูก, เล็กมาก)
  • ต้องจัดการข้อยกเว้นอย่างสง่างามโดยเฉพาะถ้าคุณย้อนกลับไปแก้ไขการนัดหมายเดิมหลังจากทำการยกเว้น ตัวอย่างเช่นหากคุณย้ายอินสแตนซ์ที่สามไปข้างหน้าหนึ่งวันจะเกิดอะไรขึ้นถ้าคุณย้อนกลับไปและแก้ไขเวลาของการนัดหมายเดิมให้แทรกอีกครั้งในวันเดิมและปล่อยให้วันที่ย้ายไปอยู่ใหม่ ยกเลิกการเชื่อมโยงย้ายหรือไม่ ลองเปลี่ยนชิ้นที่เคลื่อนไหวอย่างเหมาะสมหรือไม่?

แน่นอนว่าถ้าคุณไม่ได้ทำข้อยกเว้นการแก้ปัญหาก็ควรจะดีและโดยทั่วไปคุณเลือกจากสถานการณ์การแลกเปลี่ยนเวลา / อวกาศ


36
ถ้าคุณมีการนัดหมายซ้ำโดยไม่มีวันที่สิ้นสุด ถูกเป็นพื้นที่คือคุณไม่ได้มีพื้นที่ที่ไม่มีที่สิ้นสุดเพื่อแก้ปัญหาที่ 2 คือไม่เริ่มต้นมี ...
ชาอู Behr

13
โซลูชัน # 1 สามารถจัดการกับข้อยกเว้นได้จริง ตัวอย่างเช่น RFC5545 แสดงให้เห็นว่ามันถูกเก็บไว้เป็น: a) รายการวันที่ที่ถูกแยกออก (เมื่อคุณลบสิ่งที่เกิดขึ้น); b) เหตุการณ์ "materialized" ที่อ้างอิงถึงต้นแบบ (เมื่อคุณย้ายเหตุการณ์)
Andy Mikhaylenko

@Andy เพิ่มเติมที่น่าสนใจบางคำตอบของ Lasse จะลองทำดู
Jonathan Wilson

1
@Shaul: ฉันไม่คิดว่ามันไม่ใช่ผู้เริ่มต้น John Skeet ผู้ซึ่งได้รับการยอมรับอย่างดีจาก SO แนะนำการจัดเก็บอินสแตนซ์ที่สร้างขึ้นในคำตอบของเขาสำหรับคำถามเดียวกันโดยทั่วไป: stackoverflow.com/a/10151804/155268
ผู้ใช้

1
@ ผู้ใช้ - ตอบรับแล้วขอบคุณ มันแปลกมาก - ฉันแสดงความคิดเห็นเมื่อ 4 ปีที่แล้วและฉันก็ไม่จำเป็นต้องจัดการกับปัญหานี้ตั้งแต่นั้นมา เมื่อวานนี้ฉันได้ออกแบบโมดูลใหม่ที่เกี่ยวข้องกับการนัดหมายซ้ำและฉันก็สงสัยว่าจะจัดการกับมันอย่างไร แล้ว - ฉันได้รับการแจ้งเตือนความคิดเห็นของคุณในเช้านี้ เหมือนผีอย่างจริงจัง! แต่ขอบคุณ! :-)
Shaul Behr

20

ฉันได้พัฒนาแอพพลิเคชั่นที่ใช้ปฏิทินเป็นจำนวนมากและยังได้สร้างชุดของส่วนประกอบปฏิทิน JavaScript ที่สามารถใช้ซ้ำได้ซึ่งสนับสนุนการเกิดซ้ำ ฉันเขียนภาพรวมของวิธีการออกแบบสำหรับการเกิดซ้ำที่อาจเป็นประโยชน์กับใครบางคน ในขณะที่มีสองสามบิตที่เฉพาะเจาะจงกับไลบรารีที่ฉันเขียนส่วนใหญ่ของคำแนะนำที่มีให้นั้นเป็นเรื่องทั่วไปสำหรับการนำปฏิทินไปใช้

ประเด็นสำคัญบางประการ:

  • จัดเก็บซ้ำโดยใช้รูปแบบ iCal RRULEซึ่งเป็นวงล้อเดียวที่คุณไม่ต้องการนำกลับมาใช้ใหม่
  • อย่าเก็บอินสแตนซ์เหตุการณ์ที่เกิดซ้ำเป็นรายบุคคลเป็นแถวในฐานข้อมูลของคุณ! เก็บรูปแบบการเกิดซ้ำเสมอ
  • มีหลายวิธีในการออกแบบสคีมาเหตุการณ์ / ข้อยกเว้น แต่มีตัวอย่างจุดเริ่มต้นพื้นฐานไว้ให้
  • ค่าวันที่ / เวลาทั้งหมดควรเก็บไว้ใน UTC และแปลงเป็น local เพื่อแสดงผล
  • วันที่สิ้นสุดที่เก็บไว้สำหรับเหตุการณ์ที่เกิดขึ้นควรเป็นวันที่สิ้นสุดของช่วงการเกิดซ้ำ (หรือ "วันที่สูงสุด" ของแพลตฟอร์มของคุณหากการเกิดซ้ำ "ถาวร") และระยะเวลาเหตุการณ์ควรจัดเก็บแยกต่างหาก นี่คือเพื่อให้แน่ใจว่าวิธีการสอบถามอย่างมีเหตุผลสำหรับเหตุการณ์ในภายหลัง
  • มีการอภิปรายเกี่ยวกับการสร้างอินสแตนซ์ของเหตุการณ์และกลยุทธ์การแก้ไขที่เกิดซ้ำ

มันเป็นหัวข้อที่ซับซ้อนมากที่มีวิธีการที่ถูกต้องมากมายในการนำไปใช้ ฉันจะบอกว่าฉันใช้การเกิดขึ้นจริงหลายครั้งสำเร็จแล้วและฉันก็ระวังที่จะรับคำแนะนำในเรื่องนี้จากใครก็ตามที่ยังไม่ได้ทำ


อาจจัดเก็บการเกิดซ้ำเป็นเหตุการณ์เมื่อพวกเขาเกิดขึ้นเพื่อให้ประวัติปฏิทินของคุณถูกต้อง
Richard Haven

@ RichardHaven ฉันจะไม่ทำเช่นนั้น คุณควรสร้างอินสแตนซ์จากรูปแบบ RRULE เสมออย่างสม่ำเสมออดีตปัจจุบันหรืออนาคต ไม่มีเหตุผลที่จะทำสิ่งที่แตกต่างสำหรับเหตุการณ์ในประวัติศาสตร์ ตรรกะของคุณควรประเมิน RRULE เทียบกับช่วงวันที่ใดก็ได้และส่งคืนอินสแตนซ์เหตุการณ์ที่ตรงกัน
Brian Moeskau

@BrianMoeskau ภาพรวมที่ดีและเป็นประโยชน์!
Przemek Nowak

@BrianMoeskau แต่จะไม่ให้มุมมองที่ผ่านมาในปฏิทินของคุณแสดงข้อมูลที่ไม่ถูกต้องเมื่อมีคนแก้ไข RRULE หลังจากเกิดเหตุการณ์บางอย่างแล้ว? หรือบางทีในกรณีนี้คุณจะ "แตกสาขา" RRULE และเก็บรูปแบบ RRULE ที่แก้ไขแล้วซึ่งแสดงถึงเหตุการณ์ในอดีตที่เกิดขึ้นจริงหรือไม่
คริสเตียน

1
@christian เมื่อคุณอัปเดตกฎการเกิดซ้ำในปฏิทินส่วนใหญ่พวกเขามักจะแจ้งเช่น "แก้ไขเหตุการณ์ทั้งหมดหรือสิ่งนี้เท่านั้นหรือในอนาคตเท่านั้น" ทำให้ผู้ใช้สามารถเลือกพฤติกรรมได้ ในกรณีส่วนใหญ่ผู้ใช้อาจหมายถึง "เปลี่ยนไปข้างหน้า" แต่อีกครั้งมันขึ้นอยู่กับคุณที่จะตัดสินใจว่าซอฟต์แวร์ของคุณทำงานอย่างไรและตัวเลือกใดที่คุณให้กับผู้ใช้
Brian Moeskau

19

คุณอาจต้องการดูการใช้งานซอฟต์แวร์ iCalendar หรือมาตรฐานของตัวเอง ( RFC 2445 RFC 5545 ) คนที่จะเข้ามาในใจอย่างรวดเร็วเป็นโครงการ Mozilla http://www.mozilla.org/projects/calendar/ การค้นหาอย่างรวดเร็วเปิดเผยhttp://icalendar.rubyforge.org/เช่นกัน

ตัวเลือกอื่น ๆ อาจได้รับการพิจารณาขึ้นอยู่กับว่าคุณจะจัดเก็บเหตุการณ์อย่างไร คุณสร้างสคีมาฐานข้อมูลของคุณเองหรือ ใช้สิ่งที่ใช้ iCalendar หรือไม่?


หากคุณสามารถให้ลิงค์ไปยังหนึ่งในโพสต์เหล่านี้ของคุณจะสมบูรณ์แบบ
Jean

7
ดูเหมือนว่า RFC2445 ถูกทำให้ล้าสมัยโดย RFC5545 ( tools.ietf.org/html/rfc5545 )
Eric Freese

16

ฉันกำลังทำงานกับสิ่งต่อไปนี้:

และอัญมณีที่กำลังดำเนินการซึ่งขยาย formtastic ด้วยประเภทอินพุต: เกิดซ้ำ ( form.schedule :as => :recurring) ซึ่งแสดงอินเตอร์เฟส iCal และbefore_filterให้ลำดับมุมมองเป็นIceCubeวัตถุอีกครั้ง ghetto-ly

ความคิดของฉันคือทำให้มันง่ายอย่างไม่น่าเชื่อที่จะเพิ่มแอตทริบิวต์ที่เกิดซ้ำให้กับโมเดลและเชื่อมต่อได้อย่างง่ายดายในมุมมอง ทั้งหมดในสองสามบรรทัด


แล้วนี่ให้อะไรฉัน แอททริบิวที่สร้างดัชนีสามารถแก้ไขได้เกิดซ้ำ

eventsร้านค้าเช่นวันเดียวและใช้ในมุมมองปฏิทิน / ผู้ช่วยบอกว่าtask.scheduleร้านค้า yaml'd วัตถุเพื่อให้คุณสามารถทำสายที่ชอบ:IceCubetask.schedule.next_suggestion

สรุป: ฉันใช้สองรุ่นแบนหนึ่งสำหรับการแสดงปฏิทินและหนึ่งคุณลักษณะสำหรับการทำงาน


ฉันสนใจที่จะเห็นสิ่งที่คุณคิดขึ้นมา คุณมีคอมไพล์ / บล็อก / พิสูจน์แนวคิดได้ทุกที่หรือไม่? ขอบคุณ!
montrealmike

ฉันกำลังทำงานกับสิ่งที่คล้ายกันเช่นกัน ชอบที่จะเห็นการดำเนินการของคุณ
thinkpunch


5
  1. ติดตามกฎการเกิดซ้ำ (อาจขึ้นอยู่กับ iCalendar ต่อ @ Kris K. ) ซึ่งจะรวมถึงรูปแบบและช่วง (ทุก ๆ วันอังคารที่สามเป็นเวลา 10 ครั้ง)
  2. สำหรับเมื่อคุณต้องการแก้ไข / ลบเหตุการณ์ที่เกิดขึ้นโดยเฉพาะให้ติดตามวันที่ยกเว้นสำหรับกฎการเกิดซ้ำข้างต้น (วันที่เหตุการณ์ไม่เกิดขึ้นตามที่ระบุไว้ในกฎ)
  3. หากคุณลบนั่นคือทั้งหมดที่คุณต้องการถ้าคุณแก้ไขสร้างเหตุการณ์อื่นและตั้ง ID หลักให้กับเหตุการณ์หลัก คุณสามารถเลือกได้ว่าจะรวมข้อมูลทั้งหมดของกิจกรรมหลักไว้ในบันทึกนี้หรือไม่หรือจะเก็บเฉพาะการเปลี่ยนแปลงและสืบทอดทุกสิ่งที่ไม่เปลี่ยนแปลง

โปรดทราบว่าหากคุณอนุญาตให้กฎการเกิดซ้ำที่ไม่สิ้นสุดคุณต้องคิดเกี่ยวกับวิธีการแสดงข้อมูลจำนวนอนันต์ของคุณ

หวังว่าจะช่วย!


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
มันไม่ยืดหยุ่นมาก รูปแบบกิจกรรมที่เกิดซ้ำมักจะต้องระบุระยะเวลาการทำซ้ำ (รายชั่วโมงรายสัปดาห์รายปักษ์ ฯลฯ ) นอกจากนี้การเกิดซ้ำอาจจะไม่ได้รับการรับรองโดยจำนวนค่อนข้างวันที่สิ้นสุดการเกิดที่ผ่านมา
บ่อ Jeanes

"เหตุการณ์ที่เกิดซ้ำคือ [.. ] มักจะเป็นวันเดียวของสัปดาห์" นี่เป็นกรณีการใช้งานที่ จำกัด เพียงกรณีเดียวและไม่ได้จัดการกับคนอื่น ๆ เช่น 'วันที่ 5 ของทุกเดือน "เป็นต้น
theraven

3

จากคำตอบเหล่านี้ฉันได้แยกทางออกออกมา ฉันชอบแนวคิดของลิงก์แนวคิดจริงๆ เหตุการณ์ที่เกิดขึ้นอาจเป็นรายการที่เชื่อมโยงกับหางรู้กฎการเกิดซ้ำ การเปลี่ยนเหตุการณ์หนึ่งนั้นจะเป็นเรื่องง่ายเพราะลิงก์ยังคงอยู่และการลบกิจกรรมนั้นง่ายเช่นกัน - คุณเพียงแค่ยกเลิกการเชื่อมโยงกิจกรรมลบกิจกรรมและเชื่อมโยงเหตุการณ์ก่อนและหลังอีกครั้ง คุณยังต้องค้นหากิจกรรมที่เกิดซ้ำทุกครั้งที่มีคนดูช่วงเวลาใหม่ที่ไม่เคยดูมาก่อนในปฏิทิน แต่ไม่เช่นนั้นจะค่อนข้างสะอาด


2

คุณสามารถจัดเก็บกิจกรรมเป็นการทำซ้ำและหากมีการแก้ไขอินสแตนซ์เฉพาะให้สร้างกิจกรรมใหม่ด้วยรหัสเหตุการณ์เดียวกัน จากนั้นเมื่อค้นหากิจกรรมให้ค้นหากิจกรรมทั้งหมดด้วยรหัสเหตุการณ์เดียวกันเพื่อรับข้อมูลทั้งหมด ฉันไม่แน่ใจว่าคุณรีดไลบรารีเหตุการณ์ของคุณเองหรือถ้าคุณใช้ไลบรารีที่มีอยู่แล้วดังนั้นจึงอาจเป็นไปไม่ได้


ฉันใช้วิธีนี้ครั้งเดียว ฉันชอบหลักการของการจัดเก็บอินสแตนซ์ที่ปรับเปลี่ยนเป็นเหตุการณ์แบบครั้งเดียวใหม่ที่รู้ว่าใครเป็นแม่ ด้วยวิธีนี้คุณสามารถปล่อยให้ฟิลด์ทั้งหมดว่างเปล่ายกเว้นฟิลด์ที่แตกต่างกันสำหรับเหตุการณ์ย่อย โปรดทราบว่าคุณจะต้องมีฟิลด์พิเศษเพื่อระบุว่าคุณกำลังแก้ไขลูกคนใดของแม่นี้
Wytze

2

ตรวจสอบบทความด้านล่างเพื่อรับไลบรารี ruby ​​date / time สามแห่ง โดยเฉพาะ ice_cube ดูเหมือนจะเป็นตัวเลือกที่ดีสำหรับกฎการเกิดซ้ำและสิ่งอื่น ๆ ที่ปฏิทินกิจกรรมต้องการ 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/

โดยทั่วไปคุณสร้างกฎจากนั้นให้คุณ lib เพื่อคำนวณเหตุการณ์ที่เกิดซ้ำ N ครั้งถัดไป (ระบุช่วงวันที่หรือไม่) กฎสามารถแยกวิเคราะห์ / ต่อเนื่องเพื่อบันทึกลงในแบบจำลองของคุณ

หากคุณมีเหตุการณ์ที่เกิดขึ้นซ้ำและต้องการแก้ไขการเกิดซ้ำเพียงครั้งเดียวคุณสามารถใช้ฟังก์ชันยกเว้น ()เพื่อยกเลิกวันใดวันหนึ่งจากนั้นเพิ่มกิจกรรมที่แก้ไขใหม่สำหรับรายการนี้

lib สนับสนุนรูปแบบที่ซับซ้อนมากเขตเวลาและเหตุการณ์ croning


0

จัดเก็บเหตุการณ์เป็นการทำซ้ำและแสดงแบบไดนามิกอย่างไรก็ตามอนุญาตให้เหตุการณ์ที่เกิดขึ้นซ้ำมีรายการของเหตุการณ์เฉพาะที่สามารถแทนที่ข้อมูลเริ่มต้นในวันที่ระบุ

เมื่อคุณทำการค้นหาเหตุการณ์ที่เกิดซ้ำมันสามารถตรวจสอบการแทนที่เฉพาะสำหรับวันนั้น

หากผู้ใช้ทำการเปลี่ยนแปลงคุณสามารถถามได้ว่าเขาต้องการอัปเดตทุกกรณี (รายละเอียดเริ่มต้น) หรือแค่วันนั้น (สร้างเหตุการณ์เฉพาะใหม่และเพิ่มลงในรายการ)

หากผู้ใช้ขอให้ลบการเกิดซ้ำทั้งหมดของเหตุการณ์นี้คุณจะมีรายการเฉพาะที่จะส่งและสามารถลบออกได้อย่างง่ายดาย

กรณีที่เป็นปัญหาเท่านั้นคือหากผู้ใช้ต้องการอัปเดตเหตุการณ์นี้และเหตุการณ์ในอนาคตทั้งหมด ในกรณีนี้คุณจะต้องแบ่งกิจกรรมที่เกิดซ้ำเป็นสองเหตุการณ์ ณ จุดนี้คุณอาจต้องการพิจารณาเชื่อมโยงกิจกรรมที่เกิดซ้ำด้วยวิธีใดวิธีหนึ่งเพื่อให้คุณสามารถลบกิจกรรมทั้งหมดได้


0

สำหรับโปรแกรมเมอร์. NET ที่พร้อมจ่ายค่าธรรมเนียมใบอนุญาตบางอย่างคุณอาจพบว่าAspose.Networkมีประโยชน์ ... โดยมีไลบรารี่ที่เข้ากันได้กับ iCalendar สำหรับการนัดหมายซ้ำ


0

คุณจัดเก็บเหตุการณ์ในรูปแบบ iCalendar โดยตรงซึ่งอนุญาตให้ทำซ้ำปลายเปิดการแปลตามโซนเวลาและอื่น ๆ

คุณสามารถจัดเก็บสิ่งเหล่านี้ในเซิร์ฟเวอร์ CalDAV และเมื่อคุณต้องการแสดงเหตุการณ์คุณสามารถใช้ตัวเลือกของรายงานที่กำหนดใน CalDAV เพื่อขอให้เซิร์ฟเวอร์ทำการขยายกิจกรรมที่เกิดซ้ำในช่วงเวลาที่ดู

หรือคุณสามารถเก็บไว้ในฐานข้อมูลด้วยตัวเองและใช้ไลบรารีการแยกวิเคราะห์ iCalendar บางอย่างเพื่อทำการขยายโดยไม่ต้องใช้ PUT / GET / REPORT เพื่อพูดคุยกับเซิร์ฟเวอร์ CalDAV แบ็กเอนด์ นี่อาจใช้งานได้มากกว่า - ฉันแน่ใจว่าเซิร์ฟเวอร์ CalDAV ซ่อนความซับซ้อนไว้ที่ใดที่หนึ่ง

การมีเหตุการณ์ในรูปแบบ iCalendar อาจทำให้สิ่งต่าง ๆ ง่ายขึ้นในระยะยาวเนื่องจากผู้คนจะต้องการส่งออกเพื่อใส่ซอฟต์แวร์อื่น ๆ อยู่เสมอ


0

ฉันเพิ่งใช้งานคุณสมบัตินี้! ตรรกะมีดังนี้ก่อนอื่นคุณต้องมีสองตาราง RuleTable จัดเก็บข้อมูลทั่วไปหรือรีไซเคิลกิจกรรมของบิดา ItemTable ถูกจัดเก็บเหตุการณ์รอบ ตัวอย่างเช่นเมื่อคุณสร้างเหตุการณ์แบบวนรอบเวลาเริ่มต้นของวันที่ 6 พฤศจิกายน 2558 เวลาสิ้นสุดของวันที่ 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); //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

ถ้าคุณมีการนัดหมายซ้ำโดยไม่มีวันที่สิ้นสุด ราคาถูกเท่ากับพื้นที่คุณไม่มีพื้นที่ จำกัด ดังนั้น Solution 2 จึงไม่ใช่ผู้เริ่มต้น ...

ฉันขอแนะนำว่า "ไม่มีวันที่สิ้นสุด" สามารถแก้ไขเป็นวันที่สิ้นสุดในตอนท้ายของศตวรรษ แม้เหตุการณ์รายวันจำนวนพื้นที่ยังคงถูก


7
เราจะลืมบทเรียนของ y2k เร็ว ๆ นี้ ... :)
Ian Mercer

10
สมมติว่าเรามีผู้ใช้ 1,000 คนโดยแต่ละเหตุการณ์มีสองวัน 3 เหตุการณ์×ผู้ใช้ 1,000 คน× 365 วัน× (2100-2011 = 89 ปี) = 97.5 ล้านบันทึก แทน 3000 "แผน" อืม ...
Andy Mikhaylenko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.