Second attempt at fixing weekly recurrences with several incidences per week.
authorGunnar Wrobel <p@rdus.de>
Mon, 9 Nov 2009 13:09:21 +0000 (14:09 +0100)
committerGunnar Wrobel <p@rdus.de>
Mon, 9 Nov 2009 13:09:21 +0000 (14:09 +0100)
Added a few unit tests as the original variant did not cover all corner cases.

framework/Date/lib/Horde/Date/Recurrence.php
framework/Date/test/Horde/Date/RecurrenceTest.php

index 7231669..b6e83ea 100644 (file)
@@ -401,50 +401,20 @@ class Horde_Date_Recurrence
 
             $diff = $start_week->diff($after_week);
             $recur = $diff + ($diff % ($this->recurInterval * 7));
-            if ($this->recurCount &&
-                ceil($recur / 7) / $this->recurInterval >= $this->recurCount) {
-                return false;
-            }
 
-            if (!$this->hasRecurEnd() && $this->recurCount > 0) {
-                $start_week_end = clone $start_week;
-                $start_week_end->mday += 7;
+            if ($this->hasRecurCount()) {
                 $recurrences = 0;
-                $total_recurrences_per_week = 0;
-                $next = clone $start_week;
-                while ($next->compareDateTime($start_week_end) < 0
-                    && $next->compareDateTime($after) < 0) {
-
-                    if ($this->recurOnDay((int)pow(2, $next->dayOfWeek()))) {
-                        $total_recurrences_per_week++;
-                        if ($next->compareDateTime($this->start) >= 0)
-                            $recurrences++;
-                    }
-                    ++$next->mday;
-                }
-                if ($recurrences >= $this->recurCount)
-                    return false;
-                if ($diff >= $this->recurInterval * 7) {
-                    // calculate the number of weeks between the start and the
-                    // end week
-                    $recurrences += ((int)floor(($diff/($this->recurInterval * 7))) - 1) * $total_recurrences_per_week;
-                    if ($recurrences >= $this->recurCount)
-                        return false;
-                    if ($diff % ($this->recurInterval * 7) == 0) {
-                        // the last week is in the interval - therefore the
-                        // recurrences in the week have to be counted also
-                        $next = clone $after_week;
-                        while ($next->compareDateTime($after_week_end) < 0
-                            && $next->compareDateTime($after) < 0) {
-                            if ($this->recurOnDay((int)pow(2, $next->dayOfWeek()))) {
-                                $recurrences++;
-                            }
-                            ++$next->mday;
+                if ($recur > 0) {
+                    $weekdays = $this->recurData;
+                    $total_recurrences_per_week = 0;
+                    while ($weekdays > 0) {
+                        if ($weekdays % 2) {
+                            $total_recurrences_per_week++;
                         }
+                        $weekdays = ($weekdays - ($weekdays % 2)) / 2;
                     }
+                    $recurrences += $total_recurrences_per_week * ($recur / ($this->recurInterval * 7));
                 }
-                if ($recurrences >= $this->recurCount)
-                    return false;
             }
 
             $next = clone $start_week;
@@ -452,6 +422,15 @@ class Horde_Date_Recurrence
             while ($next->compareDateTime($after) < 0 &&
                    $next->compareDateTime($after_week_end) < 0) {
                 ++$next->mday;
+                if ($this->hasRecurCount()
+                    && $next->compareDateTime($this->start) >= 0
+                    && $this->recurOnDay((int)pow(2, $next->dayOfWeek()))) {
+                    $recurrences++;
+                }
+            }
+            if ($this->hasRecurCount() &&
+                $recurrences >= $this->recurCount) {
+                return false;
             }
             if (!$this->hasRecurEnd() ||
                 $next->compareDateTime($this->recurEnd) <= 0) {
index 0342bdb..3cc11fb 100644 (file)
@@ -137,6 +137,21 @@ class Horde_Date_RecurrenceTest extends PHPUnit_Framework_TestCase
                             $this->_getRecurrences($r));
     }
 
+    public function testWeeklyCountWithMultipleIncidencesPerWeekIfTheFirstIncidenceInThisWeekHasAlreadyPassed()
+    {
+        $r = new Horde_Date_Recurrence('2007-02-27 10:00:00');
+        $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
+        $r->setRecurOnDay(Horde_Date::MASK_MONDAY | Horde_Date::MASK_SATURDAY);
+        $r->setRecurInterval(1);
+        $r->setRecurCount(3);
+        $this->assertEquals('W1 MO SA #3', $r->toRRule10($this->ical));
+        $this->assertEquals('FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,SA;COUNT=3', $r->toRRule20($this->ical));
+        $this->assertEquals(array('2007-03-03 10:00:00',
+                                  '2007-03-05 10:00:00',
+                                  '2007-03-10 10:00:00',),
+                            $this->_getRecurrences($r));
+    }
+
     public function testWeeklyCountWithMultipleIncidencesPerWeek()
     {
         $r = new Horde_Date_Recurrence('2007-03-01 10:00:00');
@@ -152,6 +167,50 @@ class Horde_Date_RecurrenceTest extends PHPUnit_Framework_TestCase
                             $this->_getRecurrences($r));
     }
 
+    public function testWeeklyCountWithMultipleIncidencesPerWeekAndIntervalLargerOne()
+    {
+        $r = new Horde_Date_Recurrence('2007-03-01 10:00:00');
+        $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
+        $r->setRecurOnDay(Horde_Date::MASK_THURSDAY | Horde_Date::MASK_SATURDAY);
+        $r->setRecurInterval(2);
+        $r->setRecurCount(3);
+        $this->assertEquals('W2 TH SA #3', $r->toRRule10($this->ical));
+        $this->assertEquals('FREQ=WEEKLY;INTERVAL=2;BYDAY=TH,SA;COUNT=3', $r->toRRule20($this->ical));
+        $this->assertEquals(array('2007-03-01 10:00:00',
+                                  '2007-03-03 10:00:00',
+                                  '2007-03-15 10:00:00',),
+                            $this->_getRecurrences($r));
+    }
+
+    public function testWeeklyCountWithMultipleIncidencesPerWeekAndLastWeekIsComplete()
+    {
+        $r = new Horde_Date_Recurrence('2007-03-01 10:00:00');
+        $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
+        $r->setRecurOnDay(Horde_Date::MASK_THURSDAY | Horde_Date::MASK_SATURDAY);
+        $r->setRecurInterval(1);
+        $r->setRecurCount(4);
+        $this->assertEquals('W1 TH SA #4', $r->toRRule10($this->ical));
+        $this->assertEquals('FREQ=WEEKLY;INTERVAL=1;BYDAY=TH,SA;COUNT=4', $r->toRRule20($this->ical));
+        $this->assertEquals(array('2007-03-01 10:00:00',
+                                  '2007-03-03 10:00:00',
+                                  '2007-03-08 10:00:00',
+                                  '2007-03-10 10:00:00'),
+                            $this->_getRecurrences($r));
+    }
+
+    public function testWeeklyCountWithMultipleIncidencesPerWeekAndCountIsOne()
+    {
+        $r = new Horde_Date_Recurrence('2007-03-01 10:00:00');
+        $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
+        $r->setRecurOnDay(Horde_Date::MASK_THURSDAY | Horde_Date::MASK_SATURDAY);
+        $r->setRecurInterval(1);
+        $r->setRecurCount(1);
+        $this->assertEquals('W1 TH SA #1', $r->toRRule10($this->ical));
+        $this->assertEquals('FREQ=WEEKLY;INTERVAL=1;BYDAY=TH,SA;COUNT=1', $r->toRRule20($this->ical));
+        $this->assertEquals(array('2007-03-01 10:00:00'),
+                            $this->_getRecurrences($r));
+    }
+
     public function testMonthlyEnd()
     {
         $r = new Horde_Date_Recurrence('2007-03-01 10:00:00');