diff --git a/CHANGELOG.md b/CHANGELOG.md index 067f640..43d5281 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - `getNthOccurrencesBefore` - `getNthOccurrencesAfter` - `getNthOccurrencesFrom` +- New classes: `Proxy` and `Event` to work with events (a date + a duration) instead of simple occurrence (a date) ## [1.6.3] - 2019-01-13 diff --git a/src/Event.php b/src/Event.php new file mode 100755 index 0000000..38b4c95 --- /dev/null +++ b/src/Event.php @@ -0,0 +1,102 @@ + + * @link https://github.com/rlanvin/php-rrule + */ + +namespace RRule; + +/** + * Helper class to work with event (a start time and a duration) + */ +class Event +{ + /** + * @var \DateTimeInterface + */ + protected $start_date; + + /** + * @var \DateTimeInterface + */ + protected $end_date; + + /** + * @var \DateInterval + */ + protected $duration; + + public function __construct(\DateTimeInterface $start_date, $duration) + { + $this->start_date = $start_date; + $this->duration = self::parseDuration($duration); + } + + /** + * @return bool + */ + public function occursAt($date) + { + $date = RRule::parseDate($date); + + return $this->start_date >= $date && $this->getEnd() <= $date; + } + + /** + * @return \DateTimeInterface + */ + public function getStart() + { + return clone $this->start_date; + } + + /** + * @return \DateInterval + */ + public function getDuration() + { + return $this->duration; + } + + /** + * @return \DateTimeInterface + */ + public function getEnd() + { + if ( $this->end_date === null ) { + $this->end_date = $this->start_date->add($this->duration); + } + + return clone $this->end_date; + } + + static public function parseDuration($duration) + { + if ( is_numeric($duration) ) { + if ( $duration < 0 ) { + throw new \InvalidArgumentException("Duration must be a positive integer"); + } + + // duration is a integer in seconds + return \DateInterval::createFromDateString("$duration seconds"); + } + + if ( is_string($duration) ) { + if ( $duration[0] == 'P' ) { + // 'P3M' + return new \DateInterval($duration); + } + else { + // '3 months' + return \DateInterval::createFromDateString($duration); + } + } + + throw new \InvalidArgumentException('Could not parse the duration'); + } +} \ No newline at end of file diff --git a/src/Proxy.php b/src/Proxy.php new file mode 100755 index 0000000..809d64a --- /dev/null +++ b/src/Proxy.php @@ -0,0 +1,145 @@ + + * @link https://github.com/rlanvin/php-rrule + */ + +namespace RRule; + +/** + * Simple proxy class to convert the results into something else. + * + * Designed as an example how to work with events (date+duration) instead of occurrences (date only). + * + * Example: + * $rrule = new Proxy( + * new RRule("DTSTART:20170101\nRRULE:FREQ=DAILY;COUNT=2;INTERVAL=10"), + * function (\DateTimeInterface $occurrence) { + * return new Event($occurrence, 3600); + * } + * ); + */ +class Proxy implements \ArrayAccess, \Countable, \IteratorAggregate +{ + /** + * @var RRuleInterface + */ + protected $rrule; + + /** + * @var \Callable + */ + protected $factory; + + public function __construct(RRuleInterface $rrule, callable $factory) + { + $this->rrule = $rrule; + $this->factory = $factory; + } + + /** + * @return \Iterator + */ + public function getOccurrences($limit = null) + { + $occurrences = $this->rrule->getOccurrences($limit); + return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory); + } + + /** + * @return \Iterator + */ + public function getOccurrencesBetween($begin, $end, $limit = null) + { + $occurrences = $this->rrule->getOccurrencesBetween($begin, $end, $limit); + return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory); + } + + /** + * @return \Iterator + */ + public function getOccurrencesAfter($date, $inclusive = false, $limit = null) + { + $occurrences = $this->rrule->getNthOccurrenceAfter($date, $inclusive, $limit); + return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory); + } + + public function getNthOccurrenceAfter($date, $index) + { + $occurrence = $this->rrule->getNthOccurrenceAfter($date, $index); + return call_user_func_array($this->factory, [$occurrence]); + } + + /** + * @return \Iterator + */ + public function getOccurrencesBefore($date, $inclusive = false, $limit = null) + { + $occurrences = $this->rrule->getOccurrencesBefore($date, $inclusive, $limit); + return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory); + } + + public function getNthOccurrenceBefore($date, $index) + { + $occurrence = $this->rrule->getNthOccurrenceBefore($date, $index); + return call_user_func_array($this->factory, [$occurrence]); + } + + public function getNthOccurrenceFrom($date, $index) + { + $occurrence = $this->rrule->getNthOccurrenceFrom($date, $index); + return call_user_func_array($this->factory, [$occurrence]); + } + + public function isFinite() + { + return $this->rrule->isFinite(); + } + + public function isInfinite() + { + return $this->rrule->isInfinite(); + } + + public function occursAt($date) + { + return $this->rrule->occursAt(); + } + + public function offsetExists($offset) + { + return $this->rrule->offsetExists($offset); + } + + public function offsetGet($offset) + { + $occurrence = $this->rrule->offsetGet($offset); + return call_user_func_array($this->factory, [$occurrence]); + } + + public function offsetSet($offset, $value) + { + return $this->rrule->offsetSet($offset, $value); + } + + public function offsetUnset($offset) + { + return $this->rrule->offsetUnset($offset); + } + + public function count() + { + return $this->rrule->count(); + } + + public function getIterator() + { + return new ProxyIterator($this->rrule, $this->factory); + } + +} \ No newline at end of file diff --git a/src/ProxyIterator.php b/src/ProxyIterator.php new file mode 100755 index 0000000..b3737e9 --- /dev/null +++ b/src/ProxyIterator.php @@ -0,0 +1,20 @@ +factory = $factory; + parent::__construct($iterator); + } + + public function current() + { + return call_user_func_array($this->factory, [parent::current()]); + } + +} \ No newline at end of file diff --git a/tests/ProxyTest.php b/tests/ProxyTest.php new file mode 100755 index 0000000..4ff2783 --- /dev/null +++ b/tests/ProxyTest.php @@ -0,0 +1,82 @@ + 'MONTHLY', + 'COUNT' => 3, + 'BYMONTHDAY' => 31, + 'DTSTART' => '1997-09-02' + ]), + function (\DateTimeInterface $occurrence) { + return new \RRule\Event($occurrence, 3600); + } + ); + + $expected = [ + new \RRule\Event(date_create('1997-10-31'), 3600), + new \RRule\Event(date_create('1997-12-31'), 3600), + new \RRule\Event(date_create('1998-01-31'), 3600), + ]; + + $n = 0; + foreach ( $proxy as $event ) { + $this->assertEquals($expected[$n],$event); + $n++; + } + } + + public function testGetOccurrences() + { + $proxy = new Proxy( + new RRule([ + 'FREQ' => 'MONTHLY', + 'COUNT' => 3, + 'BYMONTHDAY' => 31, + 'DTSTART' => '1997-09-02' + ]), + function (\DateTimeInterface $occurrence) { + return new \RRule\Event($occurrence, 3600); + } + ); + + $expected = [ + new \RRule\Event(date_create('1997-10-31'), 3600), + new \RRule\Event(date_create('1997-12-31'), 3600), + new \RRule\Event(date_create('1998-01-31'), 3600), + ]; + + $this->assertEquals($expected, iterator_to_array($proxy->getOccurrences())); + } + + public function testGetOccurrencesBetween() + { + $proxy = new Proxy( + new RRule([ + 'FREQ' => 'MONTHLY', + 'COUNT' => 3, + 'BYMONTHDAY' => 31, + 'DTSTART' => '1997-09-02' + ]), + function (\DateTimeInterface $occurrence) { + return new \RRule\Event($occurrence, 3600); + } + ); + + $expected = [ + new \RRule\Event(date_create('1997-10-31'), 3600), + new \RRule\Event(date_create('1997-12-31'), 3600) + ]; + + $this->assertEquals($expected, iterator_to_array($proxy->getOccurrencesBetween('1997-10-31', '1997-12-31'))); + } +} \ No newline at end of file