summaryrefslogtreecommitdiffstats
path: root/vendor/simshaun
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/simshaun')
-rw-r--r--vendor/simshaun/recurr/LICENSE50
-rw-r--r--vendor/simshaun/recurr/composer.json41
-rw-r--r--vendor/simshaun/recurr/src/Recurr/DateExclusion.php48
-rw-r--r--vendor/simshaun/recurr/src/Recurr/DateInclusion.php48
-rw-r--r--vendor/simshaun/recurr/src/Recurr/DateInfo.php73
-rw-r--r--vendor/simshaun/recurr/src/Recurr/DateUtil.php571
-rw-r--r--vendor/simshaun/recurr/src/Recurr/DaySet.php46
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Exception.php17
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Exception/InvalidArgument.php20
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Exception/InvalidRRule.php20
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Exception/InvalidWeekday.php20
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Frequency.php25
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Recurrence.php90
-rw-r--r--vendor/simshaun/recurr/src/Recurr/RecurrenceCollection.php153
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Rule.php1315
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Time.php39
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformer.php736
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformerConfig.php65
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/Constraint.php21
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/AfterConstraint.php64
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BeforeConstraint.php64
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BetweenConstraint.php81
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/ConstraintInterface.php25
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php502
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/Translator.php42
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Transformer/TranslatorInterface.php8
-rw-r--r--vendor/simshaun/recurr/src/Recurr/Weekday.php72
-rw-r--r--vendor/simshaun/recurr/translations/da.php100
-rw-r--r--vendor/simshaun/recurr/translations/de.php116
-rw-r--r--vendor/simshaun/recurr/translations/el.php110
-rw-r--r--vendor/simshaun/recurr/translations/en.php104
-rw-r--r--vendor/simshaun/recurr/translations/es.php100
-rw-r--r--vendor/simshaun/recurr/translations/eu.php43
-rw-r--r--vendor/simshaun/recurr/translations/fr.php103
-rw-r--r--vendor/simshaun/recurr/translations/it.php116
-rw-r--r--vendor/simshaun/recurr/translations/nl.php100
-rw-r--r--vendor/simshaun/recurr/translations/no.php100
-rw-r--r--vendor/simshaun/recurr/translations/pt-br.php87
-rw-r--r--vendor/simshaun/recurr/translations/sv.php100
-rw-r--r--vendor/simshaun/recurr/translations/tr.php100
40 files changed, 5535 insertions, 0 deletions
diff --git a/vendor/simshaun/recurr/LICENSE b/vendor/simshaun/recurr/LICENSE
new file mode 100644
index 0000000..594ce5a
--- /dev/null
+++ b/vendor/simshaun/recurr/LICENSE
@@ -0,0 +1,50 @@
+Copyright (c) 2015 Shaun Simmons
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+----------------------------------------------
+
+Recurr is heavily based on rrule.js:
+
+Copyright 2010, Jakub Roztocil <jakub@roztocil.name> and Lars Schöning
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of The author nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/simshaun/recurr/composer.json b/vendor/simshaun/recurr/composer.json
new file mode 100644
index 0000000..5474da2
--- /dev/null
+++ b/vendor/simshaun/recurr/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "simshaun/recurr",
+ "description": "PHP library for working with recurrence rules",
+ "keywords": ["rrule", "recurrence", "recurring", "events", "dates"],
+ "homepage": "https://github.com/simshaun/recurr",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Shaun Simmons",
+ "email": "shaun@shaun.pub",
+ "homepage": "https://shaun.pub"
+ }
+ ],
+ "require": {
+ "php": "^7.2||^8.0",
+ "doctrine/collections": "~1.6||^2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.16",
+ "symfony/yaml": "^5.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Recurr\\": "src/Recurr/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Recurr\\Test\\": "tests/Recurr/Test"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.x-dev"
+ }
+ },
+ "scripts": {
+ "test": "./vendor/bin/phpunit --color=always"
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/DateExclusion.php b/vendor/simshaun/recurr/src/Recurr/DateExclusion.php
new file mode 100644
index 0000000..6ecb6ec
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/DateExclusion.php
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr;
+
+/**
+ * Class DateExclusion is a container for a single \DateTimeInterface.
+ *
+ * The purpose of this class is to hold a flag that specifies whether
+ * or not the \DateTimeInterface was created from a DATE only, or with a
+ * DATETIME.
+ *
+ * It also tracks whether or not the exclusion is explicitly set to UTC.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class DateExclusion
+{
+ /** @var \DateTimeInterface */
+ public $date;
+
+ /** @var bool Day of year */
+ public $hasTime;
+
+ /** @var bool */
+ public $isUtcExplicit;
+
+ /**
+ * Constructor
+ *
+ * @param \DateTimeInterface $date
+ * @param bool $hasTime
+ * @param bool $isUtcExplicit
+ */
+ public function __construct(\DateTimeInterface $date, $hasTime = true, $isUtcExplicit = false)
+ {
+ $this->date = $date;
+ $this->hasTime = $hasTime;
+ $this->isUtcExplicit = $isUtcExplicit;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/DateInclusion.php b/vendor/simshaun/recurr/src/Recurr/DateInclusion.php
new file mode 100644
index 0000000..0ee6e84
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/DateInclusion.php
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * Copyright 2015 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr;
+
+/**
+ * Class DateInclusion is a container for a single \DateTimeInterface.
+ *
+ * The purpose of this class is to hold a flag that specifies whether
+ * or not the \DateTimeInterface was created from a DATE only, or with a
+ * DATETIME.
+ *
+ * It also tracks whether or not the inclusion is explicitly set to UTC.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class DateInclusion
+{
+ /** @var \DateTimeInterface */
+ public $date;
+
+ /** @var bool Day of year */
+ public $hasTime;
+
+ /** @var bool */
+ public $isUtcExplicit;
+
+ /**
+ * Constructor
+ *
+ * @param \DateTimeInterface $date
+ * @param bool $hasTime
+ * @param bool $isUtcExplicit
+ */
+ public function __construct(\DateTimeInterface $date, $hasTime = true, $isUtcExplicit = false)
+ {
+ $this->date = $date;
+ $this->hasTime = $hasTime;
+ $this->isUtcExplicit = $isUtcExplicit;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/DateInfo.php b/vendor/simshaun/recurr/src/Recurr/DateInfo.php
new file mode 100644
index 0000000..453734d
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/DateInfo.php
@@ -0,0 +1,73 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr;
+
+/**
+ * Class DateInfo is responsible for holding information based on a particular
+ * date that is applicable to a Rule.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class DateInfo
+{
+ /** @var \DateTime */
+ public $dt;
+
+ /**
+ * @var int Number of days in the month.
+ */
+ public $monthLength;
+
+ /**
+ * @var int Number of days in the year (365 normally, 366 on leap years)
+ */
+ public $yearLength;
+
+ /**
+ * @var int Number of days in the next year (365 normally, 366 on leap years)
+ */
+ public $nextYearLength;
+
+ /**
+ * @var array Day of year of last day of each month.
+ */
+ public $mRanges;
+
+ /** @var int Day of week */
+ public $dayOfWeek;
+
+ /** @var int Day of week of the year's first day */
+ public $dayOfWeekYearDay1;
+
+ /**
+ * @var array Month number for each day of the year.
+ */
+ public $mMask;
+
+ /**
+ * @var array Month-daynumber for each day of the year.
+ */
+ public $mDayMask;
+
+ /**
+ * @var array Month-daynumber for each day of the year (in reverse).
+ */
+ public $mDayMaskNeg;
+
+ /**
+ * @var array Day of week (0-6) for each day of the year, 0 being Monday
+ */
+ public $wDayMask;
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/DateUtil.php b/vendor/simshaun/recurr/src/Recurr/DateUtil.php
new file mode 100644
index 0000000..48a4e87
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/DateUtil.php
@@ -0,0 +1,571 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ *
+ * Based on python-dateutil - Extensions to the standard Python datetime module.
+ * Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
+ * Copyright (c) 2012 - Tomi Pieviläinen <tomi.pievilainen@iki.fi>
+ */
+
+namespace Recurr;
+
+/**
+ * Class DateUtil is responsible for providing utilities applicable to Rules.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class DateUtil
+{
+ public static $leapBug = null;
+
+ public static $monthEndDoY366 = array(
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
+ );
+
+ public static $monthEndDoY365 = array(
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+ );
+
+ public static $wDayMask = array(
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6,
+ 0, 1, 2, 3, 4, 5, 6,
+ );
+
+ /**
+ * Get an object containing info for a particular date
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return DateInfo
+ */
+ public static function getDateInfo(\DateTimeInterface $dt)
+ {
+ $i = new DateInfo();
+ $i->dt = $dt;
+ $i->dayOfWeek = self::getDayOfWeek($dt);
+ $i->monthLength = $dt->format('t');
+ $i->yearLength = self::getYearLength($dt);
+
+ $i->mMask = self::getMonthMask($dt);
+ $i->mDayMask = self::getMonthDaysMask($dt);
+ $i->mDayMaskNeg = self::getMonthDaysMask($dt, true);
+
+ if ($i->yearLength == 365) {
+ $i->mRanges = self::$monthEndDoY365;
+ } else {
+ $i->mRanges = self::$monthEndDoY366;
+ }
+
+ $tmpDt = clone $dt;
+ $tmpDt = $tmpDt->setDate($dt->format('Y') + 1, 1, 1);
+ $i->nextYearLength = self::getYearLength($tmpDt);
+
+ $tmpDt = clone $dt;
+ $tmpDt = $tmpDt->setDate($dt->format('Y'), 1, 1);
+ $i->dayOfWeekYearDay1 = self::getDayOfWeek($tmpDt);
+
+ $i->wDayMask = array_slice(
+ self::$wDayMask,
+ $i->dayOfWeekYearDay1
+ );
+
+ return $i;
+ }
+
+ /**
+ * Get an array of DOY (Day of Year) for each day in a particular week.
+ *
+ * @param \DateTimeInterface $dt
+ * @param \DateTimeInterface $start
+ * @param null|Rule $rule
+ * @param null|DateInfo $dtInfo
+ *
+ * @return DaySet
+ */
+ public static function getDaySetOfWeek(
+ \DateTimeInterface $dt,
+ \DateTimeInterface $start,
+ Rule $rule = null,
+ DateInfo $dtInfo = null
+ )
+ {
+ $start = clone $dt;
+ $start = $start->setDate($start->format('Y'), 1, 1);
+
+ $diff = $dt->diff($start);
+ $start = $diff->days;
+
+ $set = array();
+ for ($i = $start, $k = 0; $k < 7; $k++) {
+ $set[] = $i;
+ ++$i;
+
+ if (null !== $dtInfo && null !== $rule && $dtInfo->wDayMask[$i] == $rule->getWeekStartAsNum()) {
+ break;
+ }
+ }
+
+ $obj = new DaySet($set, $start, $i);
+
+ return $obj;
+ }
+
+ /**
+ * @param Rule $rule
+ * @param \DateTimeInterface $dt
+ * @param DateInfo $dtInfo
+ * @param \DateTimeInterface $start
+ *
+ * @return DaySet
+ */
+ public static function getDaySet(Rule $rule, \DateTimeInterface $dt, DateInfo $dtInfo, $start)
+ {
+ switch ($rule->getFreq()) {
+ case Frequency::SECONDLY:
+ return self::getDaySetOfDay($dt, $start, $rule, $dtInfo);
+ break;
+ case Frequency::MINUTELY:
+ return self::getDaySetOfDay($dt, $start, $rule, $dtInfo);
+ break;
+ case Frequency::HOURLY:
+ return self::getDaySetOfDay($dt, $start, $rule, $dtInfo);
+ break;
+ case Frequency::DAILY:
+ return self::getDaySetOfDay($dt, $start, $rule, $dtInfo);
+ break;
+ case Frequency::WEEKLY:
+ return self::getDaySetOfWeek($dt, $start, $rule, $dtInfo);
+ case Frequency::MONTHLY:
+ return self::getDaySetOfMonth($dt, $start, $rule, $dtInfo);
+ case Frequency::YEARLY:
+ return self::getDaySetOfYear($dt, $start, $rule, $dtInfo);
+ }
+
+ throw new \RuntimeException('Invalid freq.');
+ }
+
+ /**
+ * Get an array of DOY (Day of Year) for each day in a particular year.
+ *
+ * @param \DateTimeInterface $dt The datetime
+ *
+ * @return DaySet
+ */
+ public static function getDaySetOfYear(\DateTimeInterface $dt)
+ {
+ $yearLen = self::getYearLength($dt);
+ $set = range(0, $yearLen - 1);
+
+ return new DaySet($set, 0, $yearLen);
+ }
+
+ /**
+ * Get an array of DOY (Day of Year) for each day in a particular month.
+ *
+ * @param \DateTimeInterface $dt The datetime
+ *
+ * @return DaySet
+ */
+ public static function getDaySetOfMonth(\DateTimeInterface $dt)
+ {
+ $dateInfo = self::getDateInfo($dt);
+ $monthNum = $dt->format('n');
+
+ $start = $dateInfo->mRanges[$monthNum - 1];
+ $end = $dateInfo->mRanges[$monthNum];
+
+ $days = range(0, $dt->format('t') - 1);
+ $set = range($start, $end - 1);
+ $set = array_combine($days, $set);
+ $obj = new DaySet($set, $start, $end - 1);
+
+ return $obj;
+ }
+
+ /**
+ * Get an array of DOY (Day of Year) for each day in a particular month.
+ *
+ * @param \DateTimeInterface $dt The datetime
+ *
+ * @return DaySet
+ */
+ public static function getDaySetOfDay(\DateTimeInterface $dt)
+ {
+ $dayOfYear = $dt->format('z');
+
+ if (self::isLeapYearDate($dt) && self::hasLeapYearBug() && $dt->format('nj') > 229) {
+ $dayOfYear -= 1;
+ }
+
+ $start = $dayOfYear;
+ $end = $dayOfYear;
+
+ $set = range($start, $end);
+ $obj = new DaySet($set, $start, $end + 1);
+
+ return $obj;
+ }
+
+ /**
+ * @param Rule $rule
+ * @param \DateTimeInterface $dt
+ *
+ * @return array
+ */
+ public static function getTimeSetOfHour(Rule $rule, \DateTimeInterface $dt)
+ {
+ $set = array();
+
+ $hour = $dt->format('G');
+ $byMinute = $rule->getByMinute();
+ $bySecond = $rule->getBySecond();
+
+ if (empty($byMinute)) {
+ $byMinute = array($dt->format('i'));
+ }
+
+ if (empty($bySecond)) {
+ $bySecond = array($dt->format('s'));
+ }
+
+ foreach ($byMinute as $minute) {
+ foreach ($bySecond as $second) {
+ $set[] = new Time($hour, $minute, $second);
+ }
+ }
+
+ return $set;
+ }
+
+ /**
+ * @param Rule $rule
+ * @param \DateTimeInterface $dt
+ *
+ * @return array
+ */
+ public static function getTimeSetOfMinute(Rule $rule, \DateTimeInterface $dt)
+ {
+ $set = array();
+
+ $hour = $dt->format('G');
+ $minute = $dt->format('i');
+ $bySecond = $rule->getBySecond();
+
+ if (empty($bySecond)) {
+ $bySecond = array($dt->format('s'));
+ }
+
+ foreach ($bySecond as $second) {
+ $set[] = new Time($hour, $minute, $second);
+ }
+
+ return $set;
+ }
+
+ /**
+ * @param \DateTimeInterface $dt
+ *
+ * @return array
+ */
+ public static function getTimeSetOfSecond(\DateTimeInterface $dt)
+ {
+ return array(new Time($dt->format('G'), $dt->format('i'), $dt->format('s')));
+ }
+
+ /**
+ * @param Rule $rule
+ * @param \DateTimeInterface $dt
+ *
+ * @return array
+ */
+ public static function getTimeSet(Rule $rule, \DateTimeInterface $dt)
+ {
+ $set = array();
+
+ if (null === $rule || $rule->getFreq() >= Frequency::HOURLY) {
+ return $set;
+ }
+
+ $byHour = $rule->getByHour();
+ $byMinute = $rule->getByMinute();
+ $bySecond = $rule->getBySecond();
+
+ if (empty($byHour)) {
+ $byHour = array($dt->format('G'));
+ }
+
+ if (empty($byMinute)) {
+ $byMinute = array($dt->format('i'));
+ }
+
+ if (empty($bySecond)) {
+ $bySecond = array($dt->format('s'));
+ }
+
+ foreach ($byHour as $hour) {
+ foreach ($byMinute as $minute) {
+ foreach ($bySecond as $second) {
+ $set[] = new Time($hour, $minute, $second);
+ }
+ }
+ }
+
+ return $set;
+ }
+
+ /**
+ * Get a reference array with the day number for each day of each month.
+ *
+ * @param \DateTimeInterface $dt The datetime
+ * @param bool $negative
+ *
+ * @return array
+ */
+ public static function getMonthDaysMask(\DateTimeInterface $dt, $negative = false)
+ {
+ if ($negative) {
+ $m29 = range(-29, -1);
+ $m30 = range(-30, -1);
+ $m31 = range(-31, -1);
+ } else {
+ $m29 = range(1, 29);
+ $m30 = range(1, 30);
+ $m31 = range(1, 31);
+ }
+
+ $mask = array_merge(
+ $m31, // Jan (31)
+ $m29, // Feb (28)
+ $m31, // Mar (31)
+ $m30, // Apr (30)
+ $m31, // May (31)
+ $m30, // Jun (30)
+ $m31, // Jul (31)
+ $m31, // Aug (31)
+ $m30, // Sep (30)
+ $m31, // Oct (31)
+ $m30, // Nov (30)
+ $m31, // Dec (31)
+ array_slice(
+ $m31,
+ 0,
+ 7
+ )
+ );
+
+ if (self::isLeapYearDate($dt)) {
+ return $mask;
+ } else {
+ if ($negative) {
+ $mask = array_merge(array_slice($mask, 0, 31), array_slice($mask, 32));
+ } else {
+ $mask = array_merge(array_slice($mask, 0, 59), array_slice($mask, 60));
+ }
+
+ return $mask;
+ }
+ }
+
+ public static function getMonthMask(\DateTimeInterface $dt)
+ {
+ if (self::isLeapYearDate($dt)) {
+ return array_merge(
+ array_fill(0, 31, 1), // Jan (31)
+ array_fill(0, 29, 2), // Feb (29)
+ array_fill(0, 31, 3), // Mar (31)
+ array_fill(0, 30, 4), // Apr (30)
+ array_fill(0, 31, 5), // May (31)
+ array_fill(0, 30, 6), // Jun (30)
+ array_fill(0, 31, 7), // Jul (31)
+ array_fill(0, 31, 8), // Aug (31)
+ array_fill(0, 30, 9), // Sep (30)
+ array_fill(0, 31, 10), // Oct (31)
+ array_fill(0, 30, 11), // Nov (30)
+ array_fill(0, 31, 12), // Dec (31)
+ array_fill(0, 7, 1)
+ );
+ } else {
+ return array_merge(
+ array_fill(0, 31, 1), // Jan (31)
+ array_fill(0, 28, 2), // Feb (28)
+ array_fill(0, 31, 3), // Mar (31)
+ array_fill(0, 30, 4), // Apr (30)
+ array_fill(0, 31, 5), // May (31)
+ array_fill(0, 30, 6), // Jun (30)
+ array_fill(0, 31, 7), // Jul (31)
+ array_fill(0, 31, 8), // Aug (31)
+ array_fill(0, 30, 9), // Sep (30)
+ array_fill(0, 31, 10), // Oct (31)
+ array_fill(0, 30, 11), // Nov (30)
+ array_fill(0, 31, 12), // Dec (31)
+ array_fill(0, 7, 1)
+ );
+ }
+ }
+
+ public static function getDateTimeByDayOfYear($dayOfYear, $year, \DateTimeZone $timezone)
+ {
+ $dtTmp = new \DateTime('now', $timezone);
+ $dtTmp = $dtTmp->setDate($year, 1, 1);
+ $dtTmp = $dtTmp->modify("+$dayOfYear day");
+
+ return $dtTmp;
+ }
+
+ public static function hasLeapYearBug()
+ {
+ $leapBugTest = \DateTime::createFromFormat('Y-m-d', '2016-03-21');
+ return $leapBugTest->format('z') != '80';
+ }
+
+ /**
+ * closure/goog/math/math.js:modulo
+ * Copyright 2006 The Closure Library Authors.
+ *
+ * The % operator in PHP returns the remainder of a / b, but differs from
+ * some other languages in that the result will have the same sign as the
+ * dividend. For example, -1 % 8 == -1, whereas in some other languages
+ * (such as Python) the result would be 7. This function emulates the more
+ * correct modulo behavior, which is useful for certain applications such as
+ * calculating an offset index in a circular list.
+ *
+ * @param int $a The dividend.
+ * @param int $b The divisor.
+ *
+ * @return int $a % $b where the result is between 0 and $b
+ * (either 0 <= x < $b
+ * or $b < x <= 0, depending on the sign of $b).
+ */
+ public static function pymod($a, $b)
+ {
+ $x = $a % $b;
+
+ // If $x and $b differ in sign, add $b to wrap the result to the correct sign.
+ return ($x * $b < 0) ? $x + $b : $x;
+ }
+
+ /**
+ * Alias method to determine if a date falls within a leap year.
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return bool
+ */
+ public static function isLeapYearDate(\DateTimeInterface $dt)
+ {
+ return $dt->format('L') ? true : false;
+ }
+
+ /**
+ * Alias method to determine if a year is a leap year.
+ *
+ * @param int $year
+ *
+ * @return bool
+ */
+ public static function isLeapYear($year)
+ {
+ $isDivisBy4 = $year % 4 == 0 ? true : false;
+ $isDivisBy100 = $year % 100 == 0? true : false;
+ $isDivisBy400 = $year % 400 == 0 ? true : false;
+
+ // http://en.wikipedia.org/wiki/February_29
+ if ($isDivisBy100 && !$isDivisBy400) {
+ return false;
+ }
+
+ return $isDivisBy4;
+ }
+
+ /**
+ * Method to determine the day of the week from MO-SU.
+ *
+ * MO = Monday
+ * TU = Tuesday
+ * WE = Wednesday
+ * TH = Thursday
+ * FR = Friday
+ * SA = Saturday
+ * SU = Sunday
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return string
+ */
+ public static function getDayOfWeekAsText(\DateTimeInterface $dt)
+ {
+ $dayOfWeek = $dt->format('w') - 1;
+
+ if ($dayOfWeek < 0) {
+ $dayOfWeek = 6;
+ }
+
+ $map = array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU');
+
+ return $map[$dayOfWeek];
+ }
+
+ /**
+ * Alias method to determine the day of the week from 0-6.
+ *
+ * 0 = Monday
+ * 1 = Tuesday
+ * 2 = Wednesday
+ * 3 = Thursday
+ * 4 = Friday
+ * 5 = Saturday
+ * 6 = Sunday
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return int
+ */
+ public static function getDayOfWeek(\DateTimeInterface $dt)
+ {
+ $dayOfWeek = $dt->format('w') - 1;
+
+ if ($dayOfWeek < 0) {
+ $dayOfWeek = 6;
+ }
+
+ return $dayOfWeek;
+ }
+
+ /**
+ * Get the number of days in a year.
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return int
+ */
+ public static function getYearLength(\DateTimeInterface $dt)
+ {
+ return self::isLeapYearDate($dt) ? 366 : 365;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/DaySet.php b/vendor/simshaun/recurr/src/Recurr/DaySet.php
new file mode 100644
index 0000000..6162e41
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/DaySet.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr;
+
+/**
+ * Class DaySet is a container for a set and its meta.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class DaySet
+{
+ /** @var array */
+ public $set;
+
+ /** @var int Day of year */
+ public $start;
+
+ /** @var int Day of year */
+ public $end;
+
+ /**
+ * Constructor
+ *
+ * @param array $set Set of days
+ * @param int $start Day of year of start day
+ * @param int $end Day of year of end day
+ */
+ public function __construct($set, $start, $end)
+ {
+ $this->set = $set;
+ $this->start = $start;
+ $this->end = $end;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Exception.php b/vendor/simshaun/recurr/src/Recurr/Exception.php
new file mode 100644
index 0000000..d0d8c6b
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Exception.php
@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr;
+
+/**
+ * The base Recurr exception class
+ */
+class Exception extends \Exception
+{
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Exception/InvalidArgument.php b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidArgument.php
new file mode 100644
index 0000000..dce4467
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidArgument.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Exception;
+
+use Recurr\Exception;
+
+/**
+ * @package Recurr\Exception
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class InvalidArgument extends Exception
+{
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Exception/InvalidRRule.php b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidRRule.php
new file mode 100644
index 0000000..cbd2692
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidRRule.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Exception;
+
+use Recurr\Exception;
+
+/**
+ * @package Recurr\Exception
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class InvalidRRule extends Exception
+{
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Exception/InvalidWeekday.php b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidWeekday.php
new file mode 100644
index 0000000..041a773
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Exception/InvalidWeekday.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Exception;
+
+use Recurr\Exception;
+
+/**
+ * @package Recurr\Exception
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class InvalidWeekday extends Exception
+{
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Frequency.php b/vendor/simshaun/recurr/src/Recurr/Frequency.php
new file mode 100644
index 0000000..e5d277b
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Frequency.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on:
+ * rrule.js - Library for working with recurrence rules for calendar dates.
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr;
+
+class Frequency {
+ const YEARLY = 0;
+ const MONTHLY = 1;
+ const WEEKLY = 2;
+ const DAILY = 3;
+ const HOURLY = 4;
+ const MINUTELY = 5;
+ const SECONDLY = 6;
+} \ No newline at end of file
diff --git a/vendor/simshaun/recurr/src/Recurr/Recurrence.php b/vendor/simshaun/recurr/src/Recurr/Recurrence.php
new file mode 100644
index 0000000..48ab117
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Recurrence.php
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr;
+
+/**
+ * Class Recurrence is responsible for storing the start and end \DateTime of
+ * a specific recurrence in a RRULE.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class Recurrence
+{
+ /** @var \DateTimeInterface */
+ protected $start;
+
+ /** @var \DateTimeInterface */
+ protected $end;
+
+ /** @var int */
+ protected $index;
+
+ public function __construct(\DateTimeInterface $start = null, \DateTimeInterface $end = null, $index = 0)
+ {
+ if ($start instanceof \DateTimeInterface) {
+ $this->setStart($start);
+ }
+
+ if ($end instanceof \DateTimeInterface) {
+ $this->setEnd($end);
+ }
+
+ $this->index = $index;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getStart()
+ {
+ return $this->start;
+ }
+
+ /**
+ * @param \DateTime $start
+ */
+ public function setStart($start)
+ {
+ $this->start = $start;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getEnd()
+ {
+ return $this->end;
+ }
+
+ /**
+ * @param \DateTime $end
+ */
+ public function setEnd($end)
+ {
+ $this->end = $end;
+ }
+
+ /**
+ * @return int
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * @param int $index
+ */
+ public function setIndex($index)
+ {
+ $this->index = $index;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/RecurrenceCollection.php b/vendor/simshaun/recurr/src/Recurr/RecurrenceCollection.php
new file mode 100644
index 0000000..e1e676b
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/RecurrenceCollection.php
@@ -0,0 +1,153 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr;
+
+use \Doctrine\Common\Collections\ArrayCollection as BaseCollection;
+
+/**
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class RecurrenceCollection extends BaseCollection
+{
+ /**
+ * @param \DateTimeInterface $after
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include $after or $before if they happen to be a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function startsBetween(\DateTimeInterface $after, \DateTimeInterface $before, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($after, $before, $inc) {
+ /** @var $recurrence Recurrence */
+ $start = $recurrence->getStart();
+
+ if ($inc) {
+ return $start >= $after && $start <= $before;
+ }
+
+ return $start > $after && $start < $before;
+ }
+ );
+ }
+
+ /**
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include $before if it is a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function startsBefore(\DateTimeInterface $before, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($before, $inc) {
+ /** @var $recurrence Recurrence */
+ $start = $recurrence->getStart();
+
+ if ($inc) {
+ return $start <= $before;
+ }
+
+ return $start < $before;
+ }
+ );
+ }
+
+ /**
+ * @param \DateTimeInterface $after
+ * @param bool $inc Include $after if it a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function startsAfter(\DateTimeInterface $after, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($after, $inc) {
+ /** @var $recurrence Recurrence */
+ $start = $recurrence->getStart();
+
+ if ($inc) {
+ return $start >= $after;
+ }
+
+ return $start > $after;
+ }
+ );
+ }
+
+ /**
+ * @param \DateTimeInterface $after
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include $after or $before if they happen to be a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function endsBetween(\DateTimeInterface $after, \DateTimeInterface $before, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($after, $before, $inc) {
+ /** @var $recurrence Recurrence */
+ $end = $recurrence->getEnd();
+
+ if ($inc) {
+ return $end >= $after && $end <= $before;
+ }
+
+ return $end > $after && $end < $before;
+ }
+ );
+ }
+
+ /**
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include $before if it is a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function endsBefore(\DateTimeInterface $before, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($before, $inc) {
+ /** @var $recurrence Recurrence */
+ $end = $recurrence->getEnd();
+
+ if ($inc) {
+ return $end <= $before;
+ }
+
+ return $end < $before;
+ }
+ );
+ }
+
+ /**
+ * @param \DateTimeInterface $after
+ * @param bool $inc Include $after if it a recurrence.
+ *
+ * @return RecurrenceCollection
+ */
+ public function endsAfter(\DateTimeInterface $after, $inc = false)
+ {
+ return $this->filter(
+ function ($recurrence) use ($after, $inc) {
+ /** @var $recurrence Recurrence */
+ $end = $recurrence->getEnd();
+
+ if ($inc) {
+ return $end >= $after;
+ }
+
+ return $end > $after;
+ }
+ );
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Rule.php b/vendor/simshaun/recurr/src/Recurr/Rule.php
new file mode 100644
index 0000000..dfcf40f
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Rule.php
@@ -0,0 +1,1315 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ *
+ * Based on python-dateutil - Extensions to the standard Python datetime module.
+ * Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
+ * Copyright (c) 2012 - Tomi Pieviläinen <tomi.pievilainen@iki.fi>
+ */
+
+namespace Recurr;
+
+use Recurr\Exception\InvalidArgument;
+use Recurr\Exception\InvalidRRule;
+use Recurr\Exception\InvalidWeekday;
+
+/**
+ * This class is responsible for providing a programmatic way of building,
+ * parsing, and handling RRULEs.
+ *
+ * http://www.ietf.org/rfc/rfc2445.txt
+ *
+ * Information, not contained in the built/parsed RRULE, necessary to determine
+ * the various recurrence instance start time and dates are derived from the
+ * DTSTART property (default: \DateTime()).
+ *
+ * For example, "FREQ=YEARLY;BYMONTH=1" doesn't specify a specific day within
+ * the month or a time. This information would be the same as what is specified
+ * for DTSTART.
+ *
+ *
+ * BYxxx rule parts modify the recurrence in some manner. BYxxx rule parts for
+ * a period of time which is the same or greater than the frequency generally
+ * reduce or limit the number of occurrences of the recurrence generated.
+ *
+ * For example, "FREQ=DAILY;BYMONTH=1" reduces the number of recurrence
+ * instances from all days (if BYMONTH tag is not present) to all days in
+ * January.
+ *
+ * BYxxx rule parts for a period of time less than the frequency generally
+ * increase or expand the number of occurrences of the recurrence.
+ *
+ * For example, "FREQ=YEARLY;BYMONTH=1,2" increases the number of days within
+ * the yearly recurrence set from 1 (if BYMONTH tag is not present) to 2.
+ *
+ * If multiple BYxxx rule parts are specified, then after evaluating the
+ * specified FREQ and INTERVAL rule parts, the BYxxx rule parts are applied to
+ * the current set of evaluated occurrences in the following order:
+ *
+ * BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR,
+ * BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.
+ *
+ * Here is an example of evaluating multiple BYxxx rule parts.
+ *
+ * FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30
+ *
+ * First, the "INTERVAL=2" would be applied to "FREQ=YEARLY" to arrive at
+ * "every other year".
+ * Then, "BYMONTH=1" would be applied to arrive at "every January, every
+ * other year".
+ * Then, "BYDAY=SU" would be applied to arrive at "every Sunday in January,
+ * every other year".
+ * Then, "BYHOUR=8,9" would be applied to arrive at "every Sunday in January
+ * at 8 AM and 9 AM, every other year".
+ * Then, "BYMINUTE=30" would be applied to arrive at "every Sunday in January
+ * at 8:30 AM and 9:30 AM, every other year".
+ * Then, lacking information from RRULE, the second is derived from DTSTART, to
+ * end up in "every Sunday in January at 8:30:00 AM and 9:30:00 AM, every
+ * other year". Similarly, if the BYMINUTE, BYHOUR, BYDAY, BYMONTHDAY or
+ * BYMONTH rule part were missing, the appropriate minute, hour, day or month
+ * would have been retrieved from the "DTSTART" property.
+ *
+ * Example: The following is a rule which specifies 10 meetings which occur
+ * every other day:
+ *
+ * FREQ=DAILY;COUNT=10;INTERVAL=2
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class Rule
+{
+ const TZ_FIXED = 'fixed';
+ const TZ_FLOAT = 'floating';
+
+ public static $freqs = array(
+ 'YEARLY' => 0,
+ 'MONTHLY' => 1,
+ 'WEEKLY' => 2,
+ 'DAILY' => 3,
+ 'HOURLY' => 4,
+ 'MINUTELY' => 5,
+ 'SECONDLY' => 6,
+ );
+
+ /** @var string */
+ protected $timezone;
+
+ /** @var \DateTimeInterface|null */
+ protected $startDate;
+
+ /** @var \DateTimeInterface|null */
+ protected $endDate;
+
+ /** @var bool */
+ protected $isStartDateFromDtstart = false;
+
+ /** @var string */
+ protected $freq;
+
+ /** @var int */
+ protected $interval = 1;
+
+ /** @var bool */
+ protected $isExplicitInterval = false;
+
+ /** @var \DateTimeInterface|null */
+ protected $until;
+
+ /** @var int|null */
+ protected $count;
+
+ /** @var array */
+ protected $bySecond;
+
+ /** @var array */
+ protected $byMinute;
+
+ /** @var array */
+ protected $byHour;
+
+ /** @var array */
+ protected $byDay;
+
+ /** @var array */
+ protected $byMonthDay;
+
+ /** @var array */
+ protected $byYearDay;
+
+ /** @var array */
+ protected $byWeekNumber;
+
+ /** @var array */
+ protected $byMonth;
+
+ /** @var string */
+ protected $weekStart = 'MO';
+ protected $weekStartDefined = false;
+
+ /** @var array */
+ protected $days = array(
+ 'MO' => 0,
+ 'TU' => 1,
+ 'WE' => 2,
+ 'TH' => 3,
+ 'FR' => 4,
+ 'SA' => 5,
+ 'SU' => 6
+ );
+
+ /** @var int[] */
+ protected $bySetPosition;
+
+ /** @var array */
+ protected $rDates = array();
+
+ /** @var array */
+ protected $exDates = array();
+
+ /**
+ * Construct a new Rule.
+ *
+ * @param string $rrule RRULE string
+ * @param string|\DateTimeInterface|null $startDate
+ * @param string|\DateTimeInterface|null $endDate
+ * @param string $timezone
+ *
+ * @throws InvalidRRule
+ */
+ public function __construct($rrule = null, $startDate = null, $endDate = null, $timezone = null)
+ {
+ if (empty($timezone)) {
+ if ($startDate instanceof \DateTimeInterface) {
+ $timezone = $startDate->getTimezone()->getName();
+ } else {
+ $timezone = date_default_timezone_get();
+ }
+ }
+ $this->setTimezone($timezone);
+
+ if ($startDate !== null && !$startDate instanceof \DateTimeInterface) {
+ $startDate = new \DateTime($startDate, new \DateTimeZone($timezone));
+ }
+
+ $this->setStartDate($startDate);
+
+ if ($endDate !== null && !$endDate instanceof \DateTimeInterface) {
+ $endDate = new \DateTime($endDate, new \DateTimeZone($timezone));
+ }
+
+ $this->setEndDate($endDate);
+
+ if (is_array($rrule)) {
+ $this->loadFromArray($rrule);
+ } else if (!empty($rrule)) {
+ $this->loadFromString($rrule);
+ }
+ }
+
+ /**
+ * Create a Rule object based on a RRULE string.
+ *
+ * @param string $rrule RRULE string
+ * @param string|\DateTimeInterface $startDate
+ * @param \DateTimeInterface|null $endDate
+ * @param string $timezone
+ *
+ * @return Rule
+ * @throws InvalidRRule
+ */
+ public static function createFromString($rrule, $startDate = null, $endDate = null, $timezone = null)
+ {
+ $rule = new static($rrule, $startDate, $endDate, $timezone);
+
+ return $rule;
+ }
+
+ /**
+ * Create a Rule object based on a RRULE array.
+ *
+ * @param array $rrule RRULE array
+ * @param string|\DateTimeInterface $startDate
+ * @param \DateTimeInterface|null $endDate
+ * @param string $timezone
+ *
+ * @return Rule
+ * @throws InvalidRRule
+ */
+ public static function createFromArray($rrule, $startDate = null, $endDate = null, $timezone = null)
+ {
+ $rule = new static($rrule, $startDate, $endDate, $timezone);
+
+ return $rule;
+ }
+
+ /**
+ * Populate the object based on a RRULE string.
+ *
+ * @param string $rrule RRULE string
+ *
+ * @return Rule
+ * @throws InvalidRRule
+ */
+ public function loadFromString($rrule)
+ {
+ $rrule = strtoupper($rrule);
+ $rrule = trim($rrule, ';');
+ $rrule = trim($rrule, "\n");
+ $rows = explode("\n", $rrule);
+
+ $parts = array();
+
+ foreach ($rows as $rruleForRow) {
+ $parts = array_merge($parts, $this->parseString($rruleForRow));
+ }
+
+ return $this->loadFromArray($parts);
+ }
+
+ /**
+ * Parse string for parts
+ *
+ * @param string $rrule
+ *
+ * @return array
+ *
+ * @throws InvalidRRule
+ */
+ public function parseString($rrule)
+ {
+ if (strpos($rrule, 'DTSTART:') === 0) {
+ $pieces = explode(':', $rrule);
+
+ if (count($pieces) !== 2) {
+ throw new InvalidRRule('DSTART is not valid');
+ }
+
+ return array('DTSTART' => $pieces[1]);
+ }
+
+ if (strpos($rrule, 'RRULE:') === 0) {
+ $rrule = str_replace('RRULE:', '', $rrule);
+ }
+
+ $pieces = explode(';', $rrule);
+ $parts = array();
+
+ if (!count($pieces)) {
+ throw new InvalidRRule('RRULE is empty');
+ }
+
+ // Split each piece of the RRULE in to KEY=>VAL
+ foreach ($pieces as $piece) {
+ if (false === strpos($piece, '=')) {
+ continue;
+ }
+
+ list($key, $val) = explode('=', $piece);
+ $parts[$key] = $val;
+ }
+
+ return $parts;
+ }
+
+ /**
+ * Populate the object based on a RRULE array.
+ *
+ * @param array
+ *
+ * @return Rule
+ * @throws InvalidRRule
+ */
+ public function loadFromArray($parts)
+ {
+ // FREQ is required
+ if (!isset($parts['FREQ'])) {
+ throw new InvalidRRule('FREQ is required');
+ } else {
+ if (!in_array($parts['FREQ'], array_keys(self::$freqs))) {
+ throw new InvalidRRule('FREQ is invalid');
+ }
+
+ $this->setFreq(self::$freqs[$parts['FREQ']]);
+ }
+
+ // DTSTART
+ if (isset($parts['DTSTART'])) {
+ $this->isStartDateFromDtstart = true;
+ $date = new \DateTime($parts['DTSTART']);
+ $date = $date->setTimezone(new \DateTimeZone($this->getTimezone()));
+ $this->setStartDate($date);
+ }
+
+ // DTEND
+ if (isset($parts['DTEND'])) {
+ $date = new \DateTime($parts['DTEND']);
+ $date = $date->setTimezone(new \DateTimeZone($this->getTimezone()));
+ $this->setEndDate($date);
+ }
+
+ // UNTIL or COUNT
+ if (isset($parts['UNTIL']) && isset($parts['COUNT'])) {
+ throw new InvalidRRule('UNTIL and COUNT must not exist together in the same RRULE');
+ } elseif (isset($parts['UNTIL'])) {
+ $date = new \DateTime($parts['UNTIL']);
+ $date = $date->setTimezone(new \DateTimeZone($this->getTimezone()));
+ $this->setUntil($date);
+ } elseif (isset($parts['COUNT'])) {
+ $this->setCount($parts['COUNT']);
+ }
+
+ // INTERVAL
+ if (isset($parts['INTERVAL'])) {
+ $this->setInterval($parts['INTERVAL']);
+ }
+
+ // BYSECOND
+ if (isset($parts['BYSECOND'])) {
+ $this->setBySecond(explode(',', $parts['BYSECOND']));
+ }
+
+ // BYMINUTE
+ if (isset($parts['BYMINUTE'])) {
+ $this->setByMinute(explode(',', $parts['BYMINUTE']));
+ }
+
+ // BYHOUR
+ if (isset($parts['BYHOUR'])) {
+ $this->setByHour(explode(',', $parts['BYHOUR']));
+ }
+
+ // BYDAY
+ if (isset($parts['BYDAY'])) {
+ $this->setByDay(explode(',', $parts['BYDAY']));
+ }
+
+ // BYMONTHDAY
+ if (isset($parts['BYMONTHDAY'])) {
+ $this->setByMonthDay(explode(',', $parts['BYMONTHDAY']));
+ }
+
+ // BYYEARDAY
+ if (isset($parts['BYYEARDAY'])) {
+ $this->setByYearDay(explode(',', $parts['BYYEARDAY']));
+ }
+
+ // BYWEEKNO
+ if (isset($parts['BYWEEKNO'])) {
+ $this->setByWeekNumber(explode(',', $parts['BYWEEKNO']));
+ }
+
+ // BYMONTH
+ if (isset($parts['BYMONTH'])) {
+ $this->setByMonth(explode(',', $parts['BYMONTH']));
+ }
+
+ // BYSETPOS
+ if (isset($parts['BYSETPOS'])) {
+ $this->setBySetPosition(explode(',', $parts['BYSETPOS']));
+ }
+
+ // WKST
+ if (isset($parts['WKST'])) {
+ $this->setWeekStart($parts['WKST']);
+ }
+
+ // RDATE
+ if (isset($parts['RDATE'])) {
+ $this->setRDates(explode(',', $parts['RDATE']));
+ }
+
+ // EXDATE
+ if (isset($parts['EXDATE'])) {
+ $this->setExDates(explode(',', $parts['EXDATE']));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the RRULE as a string
+ *
+ * @param string $timezoneType
+ *
+ * @return string
+ */
+ public function getString($timezoneType=self::TZ_FLOAT)
+ {
+ $format = 'Ymd\THis';
+
+ $parts = array();
+
+ // FREQ
+ $parts[] = 'FREQ='.$this->getFreqAsText();
+
+ // UNTIL or COUNT
+ $until = $this->getUntil();
+ $count = $this->getCount();
+ if (!empty($until)) {
+ if ($timezoneType === self::TZ_FIXED) {
+ $u = clone $until;
+ $u = $u->setTimezone(new \DateTimeZone('UTC'));
+ $parts[] = 'UNTIL='.$u->format($format.'\Z');
+ } else {
+ $parts[] = 'UNTIL='.$until->format($format);
+ }
+ } elseif (!empty($count)) {
+ $parts[] = 'COUNT='.$count;
+ }
+
+ // DTSTART
+ if ($this->isStartDateFromDtstart) {
+ if ($timezoneType === self::TZ_FIXED) {
+ $d = $this->getStartDate();
+ $tzid = $d->getTimezone()->getName();
+ $date = $d->format($format);
+ $parts[] = "DTSTART;TZID=$tzid:$date";
+ } else {
+ $parts[] = 'DTSTART='.$this->getStartDate()->format($format);
+ }
+ }
+
+ // DTEND
+ if ($this->endDate instanceof \DateTime) {
+ if ($timezoneType === self::TZ_FIXED) {
+ $d = $this->getEndDate();
+ $tzid = $d->getTimezone()->getName();
+ $date = $d->format($format);
+
+ $parts[] = "DTEND;TZID=$tzid:$date";
+ } else {
+ $parts[] = 'DTEND='.$this->getEndDate()->format($format);
+ }
+ }
+
+ // INTERVAL
+ $interval = $this->getInterval();
+ if ($this->isExplicitInterval && !empty($interval)) {
+ $parts[] = 'INTERVAL='.$interval;
+ }
+
+ // BYSECOND
+ $bySecond = $this->getBySecond();
+ if (!empty($bySecond)) {
+ $parts[] = 'BYSECOND='.implode(',', $bySecond);
+ }
+
+ // BYMINUTE
+ $byMinute = $this->getByMinute();
+ if (!empty($byMinute)) {
+ $parts[] = 'BYMINUTE='.implode(',', $byMinute);
+ }
+
+ // BYHOUR
+ $byHour = $this->getByHour();
+ if (!empty($byHour)) {
+ $parts[] = 'BYHOUR='.implode(',', $byHour);
+ }
+
+ // BYDAY
+ $byDay = $this->getByDay();
+ if (!empty($byDay)) {
+ $parts[] = 'BYDAY='.implode(',', $byDay);
+ }
+
+ // BYMONTHDAY
+ $byMonthDay = $this->getByMonthDay();
+ if (!empty($byMonthDay)) {
+ $parts[] = 'BYMONTHDAY='.implode(',', $byMonthDay);
+ }
+
+ // BYYEARDAY
+ $byYearDay = $this->getByYearDay();
+ if (!empty($byYearDay)) {
+ $parts[] = 'BYYEARDAY='.implode(',', $byYearDay);
+ }
+
+ // BYWEEKNO
+ $byWeekNumber = $this->getByWeekNumber();
+ if (!empty($byWeekNumber)) {
+ $parts[] = 'BYWEEKNO='.implode(',', $byWeekNumber);
+ }
+
+ // BYMONTH
+ $byMonth = $this->getByMonth();
+ if (!empty($byMonth)) {
+ $parts[] = 'BYMONTH='.implode(',', $byMonth);
+ }
+
+ // BYSETPOS
+ $bySetPosition = $this->getBySetPosition();
+ if (!empty($bySetPosition)) {
+ $parts[] = 'BYSETPOS='.implode(',', $bySetPosition);
+ }
+
+ // WKST
+ $weekStart = $this->getWeekStart();
+ if ($this->weekStartDefined && !empty($weekStart)) {
+ $parts[] = 'WKST='.$weekStart;
+ }
+
+ // RDATE
+ $rDates = $this->getRDates();
+ if (!empty($rDates)) {
+ foreach ($rDates as $key => $inclusion) {
+ $format = 'Ymd';
+ if ($inclusion->hasTime) {
+ $format .= '\THis';
+ if ($inclusion->isUtcExplicit) {
+ $format .= '\Z';
+ }
+ }
+ $rDates[$key] = $inclusion->date->format($format);
+ }
+ $parts[] = 'RDATE='.implode(',', $rDates);
+ }
+
+ // EXDATE
+ $exDates = $this->getExDates();
+ if (!empty($exDates)) {
+ foreach ($exDates as $key => $exclusion) {
+ $format = 'Ymd';
+ if ($exclusion->hasTime) {
+ $format .= '\THis';
+ if ($exclusion->isUtcExplicit) {
+ $format .= '\Z';
+ }
+ }
+ $exDates[$key] = $exclusion->date->format($format);
+ }
+ $parts[] = 'EXDATE='.implode(',', $exDates);
+ }
+
+ return implode(';', $parts);
+ }
+
+ /**
+ * @param string $timezone
+ *
+ * @see http://www.php.net/manual/en/timezones.php
+ * @return $this
+ */
+ public function setTimezone($timezone)
+ {
+ $this->timezone = $timezone;
+
+ return $this;
+ }
+
+ /**
+ * Get timezone to use for \DateTimeInterface objects that are UTC.
+ *
+ * @return null|string
+ */
+ public function getTimezone()
+ {
+ return $this->timezone;
+ }
+
+ /**
+ * This date specifies the first instance in the recurrence set.
+ *
+ * @param \DateTimeInterface|null $startDate Date of the first instance in the recurrence
+ * @param bool|null $includeInString If true, include as DTSTART when calling getString()
+ *
+ * @return $this
+ */
+ public function setStartDate($startDate, $includeInString = null)
+ {
+ $this->startDate = $startDate;
+
+ if ($includeInString !== null) {
+ $this->isStartDateFromDtstart = (bool) $includeInString;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getStartDate()
+ {
+ return $this->startDate;
+ }
+
+ /**
+ * This date specifies the last possible instance in the recurrence set.
+ *
+ * @param \DateTimeInterface|null $endDate Date of the last possible instance in the recurrence
+ *
+ * @return $this
+ */
+ public function setEndDate($endDate)
+ {
+ $this->endDate = $endDate;
+
+ return $this;
+ }
+
+ /**
+ * @return \DateTimeInterface|null
+ */
+ public function getEndDate()
+ {
+ return $this->endDate;
+ }
+
+ /**
+ * Identifies the type of recurrence rule.
+ *
+ * May be one of:
+ * - Frequency::SECONDLY to specify repeating events based on an
+ * interval of a second or more.
+ * - Frequency::MINUTELY to specify repeating events based on an
+ * interval of a minute or more.
+ * - Frequency::HOURLY to specify repeating events based on an
+ * interval of an hour or more.
+ * - Frequency::DAILY to specify repeating events based on an
+ * interval of a day or more.
+ * - Frequency::WEEKLY to specify repeating events based on an
+ * interval of a week or more.
+ * - Frequency::MONTHLY to specify repeating events based on an
+ * interval of a month or more.
+ * - Frequency::YEAR to specify repeating events based on an
+ * interval of a year or more.
+ *
+ * @param string|int $freq Frequency of recurrence.
+ *
+ * @return $this
+ * @throws Exception\InvalidArgument
+ */
+ public function setFreq($freq)
+ {
+ if (is_string($freq)) {
+ if (!array_key_exists($freq, self::$freqs)) {
+ throw new InvalidArgument('Frequency must comply with RFC 2445.');
+ } else {
+ $freq = self::$freqs[$freq];
+ }
+ }
+
+ if (is_int($freq) && ($freq < 0 || $freq > 6)) {
+ throw new InvalidArgument('Frequency integer must be between 0 and 6 Use the class constants.');
+ }
+
+ $this->freq = $freq;
+
+ return $this;
+ }
+
+ /**
+ * Get the type of recurrence rule (as integer).
+ *
+ * @return int
+ */
+ public function getFreq()
+ {
+ return $this->freq;
+ }
+
+ /**
+ * Get the type of recurrence rule (as text).
+ *
+ * @return string
+ */
+ public function getFreqAsText()
+ {
+ return array_search($this->getFreq(), self::$freqs);
+ }
+
+ /**
+ * The interval represents how often the recurrence rule repeats.
+ *
+ * The default value is "1", meaning every second for a SECONDLY rule,
+ * or every minute for a MINUTELY rule, every hour for an HOURLY rule,
+ * every day for a DAILY rule, every week for a WEEKLY rule, every month
+ * for a MONTHLY rule and every year for a YEARLY rule.
+ *
+ * @param int $interval Positive integer that represents how often the
+ * recurrence rule repeats.
+ *
+ * @return $this
+ * @throws Exception\InvalidArgument
+ */
+ public function setInterval($interval)
+ {
+ $interval = (int) $interval;
+
+ if ($interval < 1) {
+ throw new InvalidArgument('Interval must be a positive integer');
+ }
+
+ $this->interval = $interval;
+ $this->isExplicitInterval = true;
+
+ return $this;
+ }
+
+ /**
+ * Get the interval that represents how often the recurrence rule repeats.
+ *
+ * @return int
+ */
+ public function getInterval()
+ {
+ return $this->interval;
+ }
+
+ /**
+ * Define a \DateTimeInterface value which bounds the recurrence rule in an
+ * inclusive manner. If the value specified is synchronized with the
+ * specified recurrence, this DateTime becomes the last instance of the
+ * recurrence. If not present, and a COUNT is also not present, the RRULE
+ * is considered to repeat forever.
+ *
+ * Either UNTIL or COUNT may be specified, but UNTIL and COUNT MUST NOT
+ * both be specified.
+ *
+ * @param \DateTimeInterface $until The upper bound of the recurrence.
+ *
+ * @return $this
+ */
+ public function setUntil(\DateTimeInterface $until)
+ {
+ $this->until = $until;
+ $this->count = null;
+
+ return $this;
+ }
+
+ /**
+ * Get the \DateTimeInterface that the recurrence lasts until.
+ *
+ * @return \DateTimeInterface|null
+ */
+ public function getUntil()
+ {
+ $date = $this->until;
+
+ if ($date instanceof \DateTime
+ && $date->getTimezone()->getName() == 'UTC'
+ && $this->getTimezone() != 'UTC'
+ ) {
+ $timestamp = $date->getTimestamp();
+ $date = $date->setTimezone(new \DateTimeZone($this->getTimezone()));
+ $date = $date->setTimestamp($timestamp);
+ }
+
+ return $date;
+ }
+
+ /**
+ * The count defines the number of occurrences at which to range-bound the
+ * recurrence. The DTSTART counts as the first occurrence.
+ *
+ * Either COUNT or UNTIL may be specified, but COUNT and UNTIL MUST NOT
+ * both be specified.
+ *
+ * @param int $count Number of occurrences
+ *
+ * @return $this
+ */
+ public function setCount($count)
+ {
+ $this->count = (int) $count;
+ $this->until = null;
+
+ return $this;
+ }
+
+ /**
+ * Get the number of occurrences at which the recurrence is range-bound.
+ *
+ * @return int|null
+ */
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ /**
+ * This rule specifies an array of seconds within a minute.
+ *
+ * Valid values are 0 to 59.
+ *
+ * @param array $bySecond Array of seconds within a minute
+ *
+ * @return $this
+ */
+ public function setBySecond(array $bySecond)
+ {
+ $this->bySecond = $bySecond;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of seconds within a minute.
+ *
+ * @return array
+ */
+ public function getBySecond()
+ {
+ return $this->bySecond;
+ }
+
+ /**
+ * This rule specifies an array of minutes within an hour.
+ *
+ * Valid values are 0 to 59.
+ *
+ * @param array $byMinute Array of minutes within an hour
+ *
+ * @return $this
+ */
+ public function setByMinute(array $byMinute)
+ {
+ $this->byMinute = $byMinute;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of minutes within an hour.
+ *
+ * @return array
+ */
+ public function getByMinute()
+ {
+ return $this->byMinute;
+ }
+
+ /**
+ * This rule specifies an array of hours of the day.
+ *
+ * Valid values are 0 to 23.
+ *
+ * @param array $byHour Array of hours of the day
+ *
+ * @return $this
+ */
+ public function setByHour(array $byHour)
+ {
+ $this->byHour = $byHour;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of hours of the day.
+ *
+ * @return array
+ */
+ public function getByHour()
+ {
+ return $this->byHour;
+ }
+
+ /**
+ * This rule specifies an array of days of the week;
+ *
+ * MO indicates Monday; TU indicates Tuesday; WE indicates Wednesday;
+ * TH indicates Thursday; FR indicates Friday; SA indicates Saturday;
+ * SU indicates Sunday.
+ *
+ * Each BYDAY value can also be preceded by a positive (+n) or negative
+ * (-n) integer. If present, this indicates the nth occurrence of the
+ * specific day within the MONTHLY or YEARLY RRULE. For example, within
+ * a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
+ * within the month, whereas -1MO represents the last Monday of the
+ * month. If an integer modifier is not present, it means all days of
+ * this type within the specified frequency. For example, within a
+ * MONTHLY rule, MO represents all Mondays within the month.
+ *
+ * -------------------------------------------
+ * DO NOT MIX DAYS AND DAYS WITH MODIFIERS.
+ * This is not supported.
+ * -------------------------------------------
+ *
+ * @param array $byDay Array of days of the week
+ *
+ * @return $this
+ * @throws InvalidRRule
+ */
+ public function setByDay(array $byDay)
+ {
+ if ($this->getFreq() > static::$freqs['MONTHLY'] && preg_match('/\d/', implode(',', $byDay))) {
+ throw new InvalidRRule('BYDAY only supports MONTHLY and YEARLY frequencies');
+ }
+ if (count($byDay) === 0 || $byDay === array('')) {
+ throw new InvalidRRule('BYDAY must be set to at least one day');
+ }
+
+ $this->byDay = $byDay;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of days of the week (SU, MO, TU, ..)
+ *
+ * @return array
+ */
+ public function getByDay()
+ {
+ return $this->byDay;
+ }
+
+ /**
+ * Get an array of Weekdays
+ *
+ * @return array of Weekdays
+ * @throws InvalidWeekday
+ */
+ public function getByDayTransformedToWeekdays()
+ {
+ $byDay = $this->getByDay();
+
+ if (null === $byDay || !count($byDay)) {
+ return array();
+ }
+
+ foreach ($byDay as $idx => $day) {
+ if (strlen($day) === 2) {
+ $byDay[$idx] = new Weekday($day, null);
+ } else {
+ preg_match('/^([+-]?[0-9]+)([A-Z]{2})$/', $day, $dayParts);
+ $byDay[$idx] = new Weekday($dayParts[2], $dayParts[1]);
+ }
+ }
+
+ return $byDay;
+ }
+
+ /**
+ * This rule specifies an array of days of the month.
+ * Valid values are 1 to 31 or -31 to -1.
+ *
+ * For example, -10 represents the tenth to the last day of the month.
+ *
+ * @param array $byMonthDay Array of days of the month from -31 to 31
+ *
+ * @return $this
+ */
+ public function setByMonthDay(array $byMonthDay)
+ {
+ $this->byMonthDay = $byMonthDay;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of days of the month.
+ *
+ * @return array
+ */
+ public function getByMonthDay()
+ {
+ return $this->byMonthDay;
+ }
+
+ /**
+ * This rule specifies an array of days of the year.
+ * Valid values are 1 to 366 or -366 to -1.
+ *
+ * For example, -1 represents the last day of the year (December 31st) and
+ * -306 represents the 306th to the last day of the year (March 1st).
+ *
+ * @param array $byYearDay Array of days of the year from -1 to 306
+ *
+ * @return $this
+ */
+ public function setByYearDay(array $byYearDay)
+ {
+ $this->byYearDay = $byYearDay;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of days of the year.
+ *
+ * @return array
+ */
+ public function getByYearDay()
+ {
+ return $this->byYearDay;
+ }
+
+ /**
+ * This rule specifies an array of ordinals specifying weeks of the year.
+ * Valid values are 1 to 53 or -53 to -1.
+ *
+ * This corresponds to weeks according to week numbering as defined in
+ * [ISO 8601]. A week is defined as a seven day period, starting on the day
+ * of the week defined to be the week start (see setWeekStart). Week number
+ * one of the calendar year is the first week which contains at least four
+ * days in that calendar year. This rule is only valid for YEARLY rules.
+ *
+ * For example, 3 represents the third week of the year.
+ *
+ * Note: Assuming a Monday week start, week 53 can only occur when
+ * Thursday is January 1 or if it is a leap year and Wednesday is January 1.
+ *
+ * @param array $byWeekNumber Array of ordinals specifying weeks of the year.
+ *
+ * @return $this
+ */
+ public function setByWeekNumber(array $byWeekNumber)
+ {
+ $this->byWeekNumber = $byWeekNumber;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of ordinals specifying weeks of the year.
+ *
+ * @return array
+ */
+ public function getByWeekNumber()
+ {
+ return $this->byWeekNumber;
+ }
+
+ /**
+ * This rule specifies an array of months of the year.
+ *
+ * Valid values are 1 to 12.
+ *
+ * @param array $byMonth Array of months of the year from 1 to 12
+ *
+ * @return $this
+ */
+ public function setByMonth(array $byMonth)
+ {
+ $this->byMonth = $byMonth;
+
+ return $this;
+ }
+
+ /**
+ * Get an array of months of the year.
+ *
+ * @return array
+ */
+ public function getByMonth()
+ {
+ return $this->byMonth;
+ }
+
+ public function hasByMonth()
+ {
+ $val = $this->getByMonth();
+
+ return ! empty($val);
+ }
+
+ /**
+ * This rule specifies the day on which the workweek starts.
+ *
+ * Valid values are MO, TU, WE, TH, FR, SA and SU.
+ *
+ * This is significant when a WEEKLY RRULE has an interval greater than 1,
+ * and a BYDAY rule part is specified.
+ *
+ * This is also significant when in a YEARLY RRULE when a BYWEEKNO rule
+ * is specified. The default value is MO.
+ *
+ * @param string $weekStart The day on which the workweek starts.
+ *
+ * @return $this
+ * @throws Exception\InvalidArgument
+ */
+ public function setWeekStart($weekStart)
+ {
+ $weekStart = strtoupper($weekStart);
+
+ if (!in_array($weekStart, array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'))) {
+ throw new InvalidArgument('Week Start must be one of MO, TU, WE, TH, FR, SA, SU');
+ }
+
+ $this->weekStart = $weekStart;
+ $this->weekStartDefined = true;
+
+ return $this;
+ }
+
+ /**
+ * Get the day on which the workweek starts.
+ *
+ * @return string
+ */
+ public function getWeekStart()
+ {
+ return $this->weekStart;
+ }
+
+ /**
+ * Get the day on which the workweek starts, as an integer from 0-6,
+ * 0 being Monday and 6 being Sunday.
+ *
+ * @return int
+ */
+ public function getWeekStartAsNum()
+ {
+ $weekStart = $this->getWeekStart();
+
+ return $this->days[$weekStart];
+ }
+
+ /**
+ * This rule specifies an array of values which corresponds to the nth
+ * occurrence within the set of events specified by the rule. Valid values
+ * are 1 to 366 or -366 to -1. It MUST only be used in conjunction with
+ * another BYxxx rule part.
+ *
+ * For example "the last work day of the month" could be represented as:
+ * RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1
+ *
+ * Each BYSETPOS value can include a positive or negative integer.
+ * If present, this indicates the nth occurrence of the specific occurrence
+ * within the set of events specified by the rule.
+ *
+ * @param array $bySetPosition Array of values which corresponds to the nth
+ * occurrence within the set of events specified by the rule.
+ *
+ * @return $this
+ */
+ public function setBySetPosition($bySetPosition)
+ {
+ $this->bySetPosition = $bySetPosition;
+
+ return $this;
+ }
+
+ /**
+ * Get the array of values which corresponds to the nth occurrence within
+ * the set of events specified by the rule.
+ *
+ * @return array
+ */
+ public function getBySetPosition()
+ {
+ return $this->bySetPosition;
+ }
+
+ /**
+ * This rule specifies an array of dates that will be
+ * included in a recurrence set.
+ *
+ * @param string[]|DateInclusion[] $rDates Array of dates that will be
+ * included in the recurrence set.
+ *
+ * @return $this
+ */
+ public function setRDates(array $rDates)
+ {
+ $timezone = new \DateTimeZone($this->getTimezone());
+
+ foreach ($rDates as $key => $val) {
+ if ($val instanceof DateInclusion) {
+ $val->date = $this->convertZtoUtc($val->date);
+ } else {
+ $date = new \DateTime($val, $timezone);
+ $rDates[$key] = new DateInclusion(
+ $this->convertZtoUtc($date),
+ strpos($val, 'T') !== false,
+ strpos($val, 'Z') !== false
+ );
+ }
+ }
+
+ $this->rDates = $rDates;
+
+ return $this;
+ }
+
+ /**
+ * Get the array of dates that will be included in a recurrence set.
+ *
+ * @return DateInclusion[]
+ */
+ public function getRDates()
+ {
+ return $this->rDates;
+ }
+
+ /**
+ * This rule specifies an array of exception dates that will not be
+ * included in a recurrence set.
+ *
+ * @param string[]|DateExclusion[] $exDates Array of dates that will not be
+ * included in the recurrence set.
+ *
+ * @return $this
+ */
+ public function setExDates(array $exDates)
+ {
+ $timezone = new \DateTimeZone($this->getTimezone());
+
+ foreach ($exDates as $key => $val) {
+ if ($val instanceof DateExclusion) {
+ $val->date = $this->convertZtoUtc($val->date);
+ } else {
+ $date = new \DateTime($val, $timezone);
+ $exDates[$key] = new DateExclusion(
+ $this->convertZtoUtc($date),
+ strpos($val, 'T') !== false,
+ strpos($val, 'Z') !== false
+ );
+ }
+ }
+
+ $this->exDates = $exDates;
+
+ return $this;
+ }
+
+ /**
+ * DateTime::setTimezone fails if the timezone does not have an ID.
+ * "Z" is the same as "UTC", but "Z" does not have an ID.
+ *
+ * This is necessary for exclusion dates to be handled properly.
+ *
+ * @param \DateTimeInterface $date
+ *
+ * @return \DateTimeInterface
+ */
+ private function convertZtoUtc(\DateTimeInterface $date)
+ {
+ if ($date->getTimezone()->getName() !== 'Z') {
+ return $date;
+ }
+
+ return $date->setTimezone(new \DateTimeZone('UTC'));
+ }
+
+ /**
+ * Get the array of dates that will not be included in a recurrence set.
+ *
+ * @return DateExclusion[]
+ */
+ public function getExDates()
+ {
+ return $this->exDates;
+ }
+
+ /**
+ * @return bool
+ */
+ public function repeatsIndefinitely()
+ {
+ return !$this->getCount() && !$this->getUntil() && !$this->getEndDate();
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Time.php b/vendor/simshaun/recurr/src/Recurr/Time.php
new file mode 100644
index 0000000..c6360af
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Time.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr;
+
+/**
+ * Class Time is a storage container for a time of day.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class Time
+{
+ /** @var int */
+ public $hour;
+
+ /** @var int */
+ public $minute;
+
+ /** @var int */
+ public $second;
+
+ public function __construct($hour, $minute, $second)
+ {
+ $this->hour = $hour;
+ $this->minute = $minute;
+ $this->second = $second;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformer.php b/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformer.php
new file mode 100644
index 0000000..414a3e6
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformer.php
@@ -0,0 +1,736 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on:
+ * rrule.js - Library for working with recurrence rules for calendar dates.
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr\Transformer;
+
+use Recurr\DateExclusion;
+use Recurr\DateInclusion;
+use Recurr\Exception\InvalidWeekday;
+use Recurr\Frequency;
+use Recurr\Recurrence;
+use Recurr\RecurrenceCollection;
+use Recurr\Rule;
+use Recurr\Time;
+use Recurr\Weekday;
+use Recurr\DateUtil;
+
+/**
+ * This class is responsible for transforming a Rule in to an array
+ * of \DateTimeInterface objects.
+ *
+ * If a recurrence rule is infinitely recurring, a virtual limit is imposed.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class ArrayTransformer
+{
+ /** @var ArrayTransformerConfig */
+ protected $config;
+
+ /**
+ * Some versions of PHP are affected by a bug where
+ * \DateTimeInterface::createFromFormat('z Y', ...) does not account for leap years.
+ *
+ * @var bool
+ */
+ protected $leapBug = false;
+
+ /**
+ * Construct a new ArrayTransformer
+ *
+ * @param ArrayTransformerConfig $config
+ */
+ public function __construct(ArrayTransformerConfig $config = null)
+ {
+ if (!$config instanceof ArrayTransformerConfig) {
+ $config = new ArrayTransformerConfig();
+ }
+
+ $this->config = $config;
+
+ $this->leapBug = DateUtil::hasLeapYearBug();
+ }
+
+ /**
+ * @param ArrayTransformerConfig $config
+ */
+ public function setConfig($config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * Transform a Rule in to an array of \DateTimeInterface objects
+ *
+ * @param Rule $rule the Rule object
+ * @param ConstraintInterface|null $constraint Potential recurrences must pass the constraint, else
+ * they will not be included in the returned collection.
+ * @param bool $countConstraintFailures Whether recurrences that fail the constraint's test
+ * should count towards a rule's COUNT limit.
+ *
+ * @return RecurrenceCollection|Recurrence[]
+ * @throws InvalidWeekday
+ */
+ public function transform(Rule $rule, ConstraintInterface $constraint = null, $countConstraintFailures = true)
+ {
+ $start = $rule->getStartDate();
+ $end = $rule->getEndDate();
+ $until = $rule->getUntil();
+
+ if (null === $start) {
+ $start = new \DateTime(
+ 'now', $until instanceof \DateTimeInterface ? $until->getTimezone() : null
+ );
+ }
+
+ if (null === $end) {
+ $end = $start;
+ }
+
+ $durationInterval = $start->diff($end);
+
+ $startDay = $start->format('j');
+ $startMonthLength = $start->format('t');
+ $fixLastDayOfMonth = false;
+
+ $dt = clone $start;
+
+ $maxCount = $rule->getCount();
+ $vLimit = $this->config->getVirtualLimit();
+
+ $freq = $rule->getFreq();
+ $weekStart = $rule->getWeekStartAsNum();
+ $bySecond = $rule->getBySecond();
+ $byMinute = $rule->getByMinute();
+ $byHour = $rule->getByHour();
+ $byMonth = $rule->getByMonth();
+ $byWeekNum = $rule->getByWeekNumber();
+ $byYearDay = $rule->getByYearDay();
+ $byMonthDay = $rule->getByMonthDay();
+ $byMonthDayNeg = array();
+ $byWeekDay = $rule->getByDayTransformedToWeekdays();
+ $byWeekDayRel = array();
+ $bySetPos = $rule->getBySetPosition();
+
+ $implicitByMonthDay = false;
+ if (!(!empty($byWeekNum) || !empty($byYearDay) || !empty($byMonthDay) || !empty($byWeekDay))) {
+ switch ($freq) {
+ case Frequency::YEARLY:
+ if (empty($byMonth)) {
+ $byMonth = array($start->format('n'));
+ }
+
+ if ($startDay > 28) {
+ $fixLastDayOfMonth = true;
+ }
+
+ $implicitByMonthDay = true;
+ $byMonthDay = array($startDay);
+ break;
+ case Frequency::MONTHLY:
+ if ($startDay > 28) {
+ $fixLastDayOfMonth = true;
+ }
+
+ $implicitByMonthDay = true;
+ $byMonthDay = array($startDay);
+ break;
+ case Frequency::WEEKLY:
+ $byWeekDay = array(
+ new Weekday(
+ DateUtil::getDayOfWeek($start), null
+ )
+ );
+ break;
+ }
+ }
+
+ if (!$this->config->isLastDayOfMonthFixEnabled()) {
+ $fixLastDayOfMonth = false;
+ }
+
+ if (is_array($byMonthDay) && count($byMonthDay)) {
+ foreach ($byMonthDay as $idx => $day) {
+ if ($day < 0) {
+ unset($byMonthDay[$idx]);
+ $byMonthDayNeg[] = $day;
+ }
+ }
+ }
+
+ if (!empty($byWeekDay)) {
+ foreach ($byWeekDay as $idx => $day) {
+ /** @var $day Weekday */
+
+ if (!empty($day->num)) {
+ $byWeekDayRel[] = $day;
+ unset($byWeekDay[$idx]);
+ } else {
+ $byWeekDay[$idx] = $day->weekday;
+ }
+ }
+ }
+
+ if (empty($byYearDay)) {
+ $byYearDay = null;
+ }
+
+ if (empty($byMonthDay)) {
+ $byMonthDay = null;
+ }
+
+ if (empty($byMonthDayNeg)) {
+ $byMonthDayNeg = null;
+ }
+
+ if (empty($byWeekDay)) {
+ $byWeekDay = null;
+ }
+
+ if (!count($byWeekDayRel)) {
+ $byWeekDayRel = null;
+ }
+
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ $hour = $dt->format('G');
+ $minute = $dt->format('i');
+ $second = $dt->format('s');
+
+ $dates = array();
+ $total = 1;
+ $count = $maxCount;
+ $continue = true;
+ $iterations = 0;
+ while ($continue) {
+ $dtInfo = DateUtil::getDateInfo($dt);
+
+ $tmp = DateUtil::getDaySet($rule, $dt, $dtInfo, $start);
+ $daySet = $tmp->set;
+ $daySetStart = $tmp->start;
+ $daySetEnd = $tmp->end;
+ $wNoMask = array();
+ $wDayMaskRel = array();
+ $timeSet = DateUtil::getTimeSet($rule, $dt);
+
+ if ($freq >= Frequency::HOURLY) {
+ if (
+ ($freq >= Frequency::HOURLY && !empty($byHour) && !in_array($hour, $byHour))
+ || ($freq >= Frequency::MINUTELY && !empty($byMinute) && !in_array($minute, $byMinute))
+ || ($freq >= Frequency::SECONDLY && !empty($bySecond) && !in_array($second, $bySecond))
+ ) {
+ $timeSet = array();
+ } else {
+ switch ($freq) {
+ case Frequency::HOURLY:
+ $timeSet = DateUtil::getTimeSetOfHour($rule, $dt);
+ break;
+ case Frequency::MINUTELY:
+ $timeSet = DateUtil::getTimeSetOfMinute($rule, $dt);
+ break;
+ case Frequency::SECONDLY:
+ $timeSet = DateUtil::getTimeSetOfSecond($dt);
+ break;
+ }
+ }
+ }
+
+ // Handle byWeekNum
+ if (!empty($byWeekNum)) {
+ $no1WeekStart = $firstWeekStart = DateUtil::pymod(7 - $dtInfo->dayOfWeekYearDay1 + $weekStart, 7);
+
+ if ($no1WeekStart >= 4) {
+ $no1WeekStart = 0;
+
+ $wYearLength = $dtInfo->yearLength + DateUtil::pymod(
+ $dtInfo->dayOfWeekYearDay1 - $weekStart,
+ 7
+ );
+ } else {
+ $wYearLength = $dtInfo->yearLength - $no1WeekStart;
+ }
+
+ $div = floor($wYearLength / 7);
+ $mod = DateUtil::pymod($wYearLength, 7);
+ $numWeeks = floor($div + ($mod / 4));
+
+ foreach ($byWeekNum as $weekNum) {
+ if ($weekNum < 0) {
+ $weekNum += $numWeeks + 1;
+ }
+
+ if (!(0 < $weekNum && $weekNum <= $numWeeks)) {
+ continue;
+ }
+
+ if ($weekNum > 1) {
+ $offset = $no1WeekStart + ($weekNum - 1) * 7;
+ if ($no1WeekStart != $firstWeekStart) {
+ $offset -= 7 - $firstWeekStart;
+ }
+ } else {
+ $offset = $no1WeekStart;
+ }
+
+ for ($i = 0; $i < 7; $i++) {
+ $wNoMask[] = $offset;
+ $offset++;
+ if ($dtInfo->wDayMask[$offset] == $weekStart) {
+ break;
+ }
+ }
+ }
+
+ // Check week number 1 of next year as well
+ if (in_array(1, $byWeekNum)) {
+ $offset = $no1WeekStart + $numWeeks * 7;
+
+ if ($no1WeekStart != $firstWeekStart) {
+ $offset -= 7 - $firstWeekStart;
+ }
+
+ // If week starts in next year, we don't care about it.
+ if ($offset < $dtInfo->yearLength) {
+ for ($k = 0; $k < 7; $k++) {
+ $wNoMask[] = $offset;
+ $offset += 1;
+ if ($dtInfo->wDayMask[$offset] == $weekStart) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ($no1WeekStart) {
+ // Check last week number of last year as well.
+ // If $no1WeekStart is 0, either the year started on week start,
+ // or week number 1 got days from last year, so there are no
+ // days from last year's last week number in this year.
+ if (!in_array(-1, $byWeekNum)) {
+ $dtTmp = new \DateTime();
+ $dtTmp = $dtTmp->setDate($year - 1, 1, 1);
+ $lastYearWeekDay = DateUtil::getDayOfWeek($dtTmp);
+ $lastYearNo1WeekStart = DateUtil::pymod(7 - $lastYearWeekDay + $weekStart, 7);
+ $lastYearLength = DateUtil::getYearLength($dtTmp);
+ if ($lastYearNo1WeekStart >= 4) {
+ $lastYearNo1WeekStart = 0;
+ $lastYearNumWeeks = floor(
+ 52 + DateUtil::pymod(
+ $lastYearLength + DateUtil::pymod(
+ $lastYearWeekDay - $weekStart,
+ 7
+ ),
+ 7
+ ) / 4
+ );
+ } else {
+ $lastYearNumWeeks = floor(
+ 52 + DateUtil::pymod(
+ $dtInfo->yearLength - $no1WeekStart,
+ 7
+ ) / 4
+ );
+ }
+ } else {
+ $lastYearNumWeeks = -1;
+ }
+
+ if (in_array($lastYearNumWeeks, $byWeekNum)) {
+ for ($i = 0; $i < $no1WeekStart; $i++) {
+ $wNoMask[] = $i;
+ }
+ }
+ }
+ }
+
+ // Handle relative weekdays (e.g. 3rd Friday of month)
+ if (!empty($byWeekDayRel)) {
+ $ranges = array();
+
+ if (Frequency::YEARLY == $freq) {
+ if (!empty($byMonth)) {
+ foreach ($byMonth as $mo) {
+ $ranges[] = array_slice($dtInfo->mRanges, $mo - 1, 2);
+ }
+ } else {
+ $ranges[] = array(0, $dtInfo->yearLength);
+ }
+ } elseif (Frequency::MONTHLY == $freq) {
+ $ranges[] = array_slice($dtInfo->mRanges, $month - 1, 2);
+ }
+
+ if (!empty($ranges)) {
+ foreach ($ranges as $range) {
+ $rangeStart = $range[0];
+ $rangeEnd = $range[1];
+ --$rangeEnd;
+
+ reset($byWeekDayRel);
+ foreach ($byWeekDayRel as $weekday) {
+ /** @var Weekday $weekday */
+
+ if ($weekday->num < 0) {
+ $i = $rangeEnd + ($weekday->num + 1) * 7;
+ $i -= DateUtil::pymod(
+ $dtInfo->wDayMask[$i] - $weekday->weekday,
+ 7
+ );
+ } else {
+ $i = $rangeStart + ($weekday->num - 1) * 7;
+ $i += DateUtil::pymod(
+ 7 - $dtInfo->wDayMask[$i] + $weekday->weekday,
+ 7
+ );
+ }
+
+ if ($rangeStart <= $i && $i <= $rangeEnd) {
+ $wDayMaskRel[] = $i;
+ }
+ }
+ }
+ }
+ }
+
+ $numMatched = 0;
+ foreach ($daySet as $i => $dayOfYear) {
+ $dayOfMonth = $dtInfo->mDayMask[$dayOfYear];
+
+ $ifByMonth = $byMonth !== null && !in_array($dtInfo->mMask[$dayOfYear], $byMonth);
+
+ $ifByWeekNum = $byWeekNum !== null && !in_array($i, $wNoMask);
+
+ $ifByYearDay = $byYearDay !== null && (
+ (
+ $i < $dtInfo->yearLength &&
+ !in_array($i + 1, $byYearDay) &&
+ !in_array(-$dtInfo->yearLength + $i, $byYearDay)
+ ) ||
+ (
+ $i >= $dtInfo->yearLength &&
+ !in_array($i + 1 - $dtInfo->yearLength, $byYearDay) &&
+ !in_array(-$dtInfo->nextYearLength + $i - $dtInfo->yearLength, $byYearDay)
+ )
+ );
+
+ $ifByMonthDay = $byMonthDay !== null && !in_array($dtInfo->mDayMask[$dayOfYear], $byMonthDay);
+
+ // Handle "last day of next month" problem.
+ if ($fixLastDayOfMonth
+ && $ifByMonthDay
+ && $implicitByMonthDay
+ && $startMonthLength > $dtInfo->monthLength
+ && $dayOfMonth == $dtInfo->monthLength
+ && $dayOfMonth < $startMonthLength
+ && !$numMatched
+ ) {
+ $ifByMonthDay = false;
+ }
+
+ $ifByMonthDayNeg = $byMonthDayNeg !== null
+ && !in_array($dtInfo->mDayMaskNeg[$dayOfYear], $byMonthDayNeg);
+
+ $ifByDay = $byWeekDay !== null && count($byWeekDay)
+ && !in_array($dtInfo->wDayMask[$dayOfYear], $byWeekDay);
+
+ $ifWDayMaskRel = $byWeekDayRel !== null && !in_array($dayOfYear, $wDayMaskRel);
+
+ if ($byMonthDay !== null && $byMonthDayNeg !== null) {
+ if ($ifByMonthDay && $ifByMonthDayNeg) {
+ unset($daySet[$i]);
+ }
+ } elseif ($ifByMonth || $ifByWeekNum || $ifByYearDay || $ifByMonthDay || $ifByMonthDayNeg || $ifByDay || $ifWDayMaskRel) {
+ unset($daySet[$i]);
+ } else {
+ ++$numMatched;
+ }
+ }
+
+ if (!empty($bySetPos) && !empty($daySet)) {
+ $datesAdj = array();
+ $tmpDaySet = array_combine($daySet, $daySet);
+
+ foreach ($bySetPos as $setPos) {
+ if ($setPos < 0) {
+ $dayPos = floor($setPos / count($timeSet));
+ $timePos = DateUtil::pymod($setPos, count($timeSet));
+ } else {
+ $dayPos = floor(($setPos - 1) / count($timeSet));
+ $timePos = DateUtil::pymod(($setPos - 1), count($timeSet));
+ }
+
+ $tmp = array();
+ for ($k = $daySetStart; $k <= $daySetEnd; $k++) {
+ if (!array_key_exists($k, $tmpDaySet)) {
+ continue;
+ }
+
+ $tmp[] = $tmpDaySet[$k];
+ }
+
+ if ($dayPos < 0) {
+ $nextInSet = array_slice($tmp, $dayPos, 1);
+ if (count($nextInSet) === 0) {
+ continue;
+ }
+ $nextInSet = $nextInSet[0];
+ } else {
+ $nextInSet = isset($tmp[$dayPos]) ? $tmp[$dayPos] : null;
+ }
+
+ if (null !== $nextInSet) {
+ /** @var Time $time */
+ $time = $timeSet[$timePos];
+
+ $dtTmp = DateUtil::getDateTimeByDayOfYear($nextInSet, $dt->format('Y'), $start->getTimezone());
+
+ $dtTmp = $dtTmp->setTime(
+ $time->hour,
+ $time->minute,
+ $time->second
+ );
+
+ $datesAdj[] = $dtTmp;
+ }
+ }
+
+ foreach ($datesAdj as $dtTmp) {
+ if (null !== $until && $dtTmp > $until) {
+ $continue = false;
+ break;
+ }
+
+ if ($dtTmp < $start) {
+ continue;
+ }
+
+ if ($constraint instanceof ConstraintInterface && !$constraint->test($dtTmp)) {
+ if (!$countConstraintFailures) {
+ if ($constraint->stopsTransformer()) {
+ $continue = false;
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else {
+ $dates[$total] = $dtTmp;
+ }
+
+ if (null !== $count) {
+ --$count;
+ if ($count <= 0) {
+ $continue = false;
+ break;
+ }
+ }
+
+ ++$total;
+ if ($total > $vLimit) {
+ $continue = false;
+ break;
+ }
+ }
+ } else {
+ foreach ($daySet as $dayOfYear) {
+ $dtTmp = DateUtil::getDateTimeByDayOfYear($dayOfYear, $dt->format('Y'), $start->getTimezone());
+
+ foreach ($timeSet as $time) {
+ /** @var Time $time */
+ $dtTmp = $dtTmp->setTime(
+ $time->hour,
+ $time->minute,
+ $time->second
+ );
+
+ if (null !== $until && $dtTmp > $until) {
+ $continue = false;
+ break;
+ }
+
+ if ($dtTmp < $start) {
+ continue;
+ }
+
+ if ($constraint instanceof ConstraintInterface && !$constraint->test($dtTmp)) {
+ if (!$countConstraintFailures) {
+ if ($constraint->stopsTransformer()) {
+ $continue = false;
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else {
+ $dates[$total] = clone $dtTmp;
+ }
+
+ if (null !== $count) {
+ --$count;
+ if ($count <= 0) {
+ $continue = false;
+ break;
+ }
+ }
+
+ ++$total;
+ if ($total > $vLimit) {
+ $continue = false;
+ break;
+ }
+ }
+
+ if (!$continue) {
+ break;
+ }
+ }
+
+ if ($total > $vLimit) {
+ $continue = false;
+ break;
+ }
+ }
+
+ switch ($freq) {
+ case Frequency::YEARLY:
+ $year += $rule->getInterval();
+ $month = $dt->format('n');
+ $dt = $dt->setDate($year, $month, 1);
+
+ // Stop an infinite loop w/ a sane limit
+ ++$iterations;
+ if ($iterations > 300 && !count($dates)) {
+ break 2;
+ }
+ break;
+ case Frequency::MONTHLY:
+ $month += $rule->getInterval();
+ if ($month > 12) {
+ $delta = floor($month / 12);
+ $mod = DateUtil::pymod($month, 12);
+ $month = $mod;
+ $year += $delta;
+ if ($month == 0) {
+ $month = 12;
+ --$year;
+ }
+ }
+ $dt = $dt->setDate($year, $month, 1);
+ break;
+ case Frequency::WEEKLY:
+ if ($weekStart > $dtInfo->dayOfWeek) {
+ $delta = ($dtInfo->dayOfWeek + 1 + (6 - $weekStart)) * -1 + $rule->getInterval() * 7;
+ } else {
+ $delta = ($dtInfo->dayOfWeek - $weekStart) * -1 + $rule->getInterval() * 7;
+ }
+
+ $dt = $dt->modify("+$delta day");
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ break;
+ case Frequency::DAILY:
+ $dt = $dt->modify('+'.$rule->getInterval().' day');
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ break;
+ case Frequency::HOURLY:
+ $dt = $dt->modify('+'.$rule->getInterval().' hours');
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ $hour = $dt->format('G');
+ break;
+ case Frequency::MINUTELY:
+ $dt = $dt->modify('+'.$rule->getInterval().' minutes');
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ $hour = $dt->format('G');
+ $minute = $dt->format('i');
+ break;
+ case Frequency::SECONDLY:
+ $dt = $dt->modify('+'.$rule->getInterval().' seconds');
+ $year = $dt->format('Y');
+ $month = $dt->format('n');
+ $hour = $dt->format('G');
+ $minute = $dt->format('i');
+ $second = $dt->format('s');
+ break;
+ }
+ }
+
+ /** @var Recurrence[] $recurrences */
+ $recurrences = array();
+ foreach ($dates as $key => $start) {
+ /** @var \DateTimeInterface $end */
+ $end = clone $start;
+
+ $recurrences[] = new Recurrence($start, $end->add($durationInterval), $key);
+ }
+
+ $recurrences = $this->handleInclusions($rule->getRDates(), $recurrences);
+ $recurrences = $this->handleExclusions($rule->getExDates(), $recurrences);
+
+ return new RecurrenceCollection($recurrences);
+ }
+
+ /**
+ * @param DateExclusion[] $exclusions
+ * @param Recurrence[] $recurrences
+ *
+ * @return Recurrence[]
+ */
+ protected function handleExclusions(array $exclusions, array $recurrences)
+ {
+ foreach ($exclusions as $exclusion) {
+ $exclusionDate = $exclusion->date->format('Ymd');
+ $exclusionTime = $exclusion->date->format('Ymd\THis');
+ $exclusionTimezone = $exclusion->date->getTimezone();
+
+ foreach ($recurrences as $key => $recurrence) {
+ $recurrenceDate = $recurrence->getStart();
+
+ if ($recurrenceDate->getTimezone()->getName() !== $exclusionTimezone->getName()) {
+ $recurrenceDate = clone $recurrenceDate;
+ $recurrenceDate = $recurrenceDate->setTimezone($exclusionTimezone);
+ }
+
+ if (!$exclusion->hasTime && $recurrenceDate->format('Ymd') == $exclusionDate) {
+ unset($recurrences[$key]);
+ continue;
+ }
+
+ if ($exclusion->hasTime && $recurrenceDate->format('Ymd\THis') == $exclusionTime) {
+ unset($recurrences[$key]);
+ }
+ }
+ }
+
+ return array_values($recurrences);
+ }
+
+ /**
+ * @param DateInclusion[] $inclusions
+ * @param Recurrence[] $recurrences
+ *
+ * @return Recurrence[]
+ */
+ protected function handleInclusions(array $inclusions, array $recurrences)
+ {
+ foreach ($inclusions as $inclusion) {
+ $recurrence = new Recurrence(clone $inclusion->date, clone $inclusion->date);
+ $recurrences[] = $recurrence;
+ }
+
+ return array_values($recurrences);
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformerConfig.php b/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformerConfig.php
new file mode 100644
index 0000000..68350b0
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/ArrayTransformerConfig.php
@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer;
+
+class ArrayTransformerConfig
+{
+ /** @var int */
+ protected $virtualLimit = 732;
+
+ protected $lastDayOfMonthFix = false;
+
+ /**
+ * Set the virtual limit imposed upon infinitely recurring events.
+ *
+ * @param int $virtualLimit The limit
+ *
+ * @return $this
+ */
+ public function setVirtualLimit($virtualLimit)
+ {
+ $this->virtualLimit = (int) $virtualLimit;
+
+ return $this;
+ }
+
+ /**
+ * Get the virtual limit imposed upon infinitely recurring events.
+ *
+ * @return int
+ */
+ public function getVirtualLimit()
+ {
+ return $this->virtualLimit;
+ }
+
+ /**
+ * By default, January 30 + 1 month results in March 30 because February doesn't have 30 days.
+ *
+ * Enabling this fix tells Recurr that +1 month means "last day of next month".
+ */
+ public function enableLastDayOfMonthFix()
+ {
+ $this->lastDayOfMonthFix = true;
+ }
+
+ public function disableLastDayOfMonthFix()
+ {
+ $this->lastDayOfMonthFix = false;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isLastDayOfMonthFixEnabled()
+ {
+ return $this->lastDayOfMonthFix;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint.php b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint.php
new file mode 100644
index 0000000..2a9cf2a
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer;
+
+abstract class Constraint implements ConstraintInterface
+{
+
+ protected $stopsTransformer = true;
+
+ public function stopsTransformer()
+ {
+ return $this->stopsTransformer;
+ }
+} \ No newline at end of file
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/AfterConstraint.php b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/AfterConstraint.php
new file mode 100644
index 0000000..3baf9a0
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/AfterConstraint.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer\Constraint;
+
+use Recurr\Transformer\Constraint;
+
+class AfterConstraint extends Constraint
+{
+
+ protected $stopsTransformer = false;
+
+ /** @var \DateTimeInterface */
+ protected $after;
+
+ /** @var bool */
+ protected $inc;
+
+ /**
+ * @param \DateTimeInterface $after
+ * @param bool $inc Include date if it equals $after.
+ */
+ public function __construct(\DateTimeInterface $after, $inc = false)
+ {
+ $this->after = $after;
+ $this->inc = $inc;
+ }
+
+ /**
+ * Passes if $date is after $after
+ *
+ * {@inheritdoc}
+ */
+ public function test(\DateTimeInterface $date)
+ {
+ if ($this->inc) {
+ return $date >= $this->after;
+ }
+
+ return $date > $this->after;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getAfter()
+ {
+ return $this->after;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInc()
+ {
+ return $this->inc;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BeforeConstraint.php b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BeforeConstraint.php
new file mode 100644
index 0000000..aa94525
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BeforeConstraint.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer\Constraint;
+
+use Recurr\Transformer\Constraint;
+
+class BeforeConstraint extends Constraint
+{
+
+ protected $stopsTransformer = true;
+
+ /** @var \DateTimeInterface */
+ protected $before;
+
+ /** @var bool */
+ protected $inc;
+
+ /**
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include date if it equals $before.
+ */
+ public function __construct(\DateTimeInterface $before, $inc = false)
+ {
+ $this->before = $before;
+ $this->inc = $inc;
+ }
+
+ /**
+ * Passes if $date is before $before
+ *
+ * {@inheritdoc}
+ */
+ public function test(\DateTimeInterface $date)
+ {
+ if ($this->inc) {
+ return $date <= $this->before;
+ }
+
+ return $date < $this->before;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getBefore()
+ {
+ return $this->before;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInc()
+ {
+ return $this->inc;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BetweenConstraint.php b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BetweenConstraint.php
new file mode 100644
index 0000000..f16d491
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/Constraint/BetweenConstraint.php
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer\Constraint;
+
+use Recurr\Transformer\Constraint;
+
+class BetweenConstraint extends Constraint
+{
+
+ protected $stopsTransformer = false;
+
+ /** @var \DateTimeInterface */
+ protected $before;
+
+ /** @var \DateTimeInterface */
+ protected $after;
+
+ /** @var bool */
+ protected $inc;
+
+ /**
+ * @param \DateTimeInterface $after
+ * @param \DateTimeInterface $before
+ * @param bool $inc Include date if it equals $after or $before.
+ */
+ public function __construct(\DateTimeInterface $after, \DateTimeInterface $before, $inc = false)
+ {
+ $this->after = $after;
+ $this->before = $before;
+ $this->inc = $inc;
+ }
+
+ /**
+ * Passes if $date is between $after and $before
+ *
+ * {@inheritdoc}
+ */
+ public function test(\DateTimeInterface $date)
+ {
+ if ($date > $this->before) {
+ $this->stopsTransformer = true;
+ }
+
+ if ($this->inc) {
+ return $date >= $this->after && $date <= $this->before;
+ }
+
+ return $date > $this->after && $date < $this->before;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getBefore()
+ {
+ return $this->before;
+ }
+
+ /**
+ * @return \DateTimeInterface
+ */
+ public function getAfter()
+ {
+ return $this->after;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInc()
+ {
+ return $this->inc;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/ConstraintInterface.php b/vendor/simshaun/recurr/src/Recurr/Transformer/ConstraintInterface.php
new file mode 100644
index 0000000..6879554
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/ConstraintInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * Copyright 2014 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Recurr\Transformer;
+
+interface ConstraintInterface
+{
+ /**
+ * @return bool
+ */
+ public function stopsTransformer();
+
+ /**
+ * @param \DateTimeInterface $date
+ *
+ * @return bool
+ */
+ public function test(\DateTimeInterface $date);
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php b/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php
new file mode 100644
index 0000000..0fdef0b
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php
@@ -0,0 +1,502 @@
+<?php
+
+namespace Recurr\Transformer;
+
+use Recurr\Rule;
+
+class TextTransformer
+{
+ protected $fragments = array();
+ protected $translator;
+
+ public function __construct(TranslatorInterface $translator = null)
+ {
+ $this->translator = $translator ?: new Translator('en');
+ }
+
+ public function transform(Rule $rule)
+ {
+ $this->fragments = array();
+
+ switch ($rule->getFreq()) {
+ case 0:
+ $this->addYearly($rule);
+ break;
+ case 1:
+ $this->addMonthly($rule);
+ break;
+ case 2:
+ $this->addWeekly($rule);
+ break;
+ case 3:
+ $this->addDaily($rule);
+ break;
+ case 4:
+ $this->addHourly($rule);
+ break;
+ case 5:
+ case 6:
+ return $this->translator->trans('Unable to fully convert this rrule to text.');
+ }
+
+ $until = $rule->getUntil();
+ $count = $rule->getCount();
+ if ($until instanceof \DateTimeInterface) {
+ $dateFormatted = $this->translator->trans('day_date', array('date' => $until->format('U')));
+ $this->addFragment($this->translator->trans('until %date%', array('date' => $dateFormatted)));
+ } else if (!empty($count)) {
+ if ($this->isPlural($count)) {
+ $this->addFragment($this->translator->trans('for %count% times', array('count' => $count)));
+ } else {
+ $this->addFragment($this->translator->trans('for one time'));
+ }
+ }
+
+ if (!$this->isFullyConvertible($rule)) {
+ $this->addFragment($this->translator->trans('(~ approximate)'));
+ }
+
+ return implode(' ', $this->fragments);
+ }
+
+ protected function isFullyConvertible(Rule $rule)
+ {
+ if ($rule->getFreq() >= 5) {
+ return false;
+ }
+
+ $until = $rule->getUntil();
+ $count = $rule->getCount();
+ if (!empty($until) && !empty($count)) {
+ return false;
+ }
+
+ $bySecond = $rule->getBySecond();
+ $byMinute = $rule->getByMinute();
+ $byHour = $rule->getByHour();
+
+ if (!empty($bySecond) || !empty($byMinute) || !empty($byHour)) {
+ return false;
+ }
+
+ $byWeekNum = $rule->getByWeekNumber();
+ $byYearDay = $rule->getByYearDay();
+ if ($rule->getFreq() != 0 && (!empty($byWeekNum) || !empty($byYearDay))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function addYearly(Rule $rule)
+ {
+ $interval = $rule->getInterval();
+ $byMonth = $rule->getByMonth();
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+ $byYearDay = $rule->getByYearDay();
+ $byWeekNum = $rule->getByWeekNumber();
+
+ if (!empty($byMonth) && count($byMonth) > 1 && $interval == 1) {
+ $this->addFragment($this->translator->trans('every_month_list'));
+ } else {
+ $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% years' : 'every year', array('count' => $interval)));
+ }
+
+ $hasNoOrOneByMonth = is_null($byMonth) || count($byMonth) <= 1;
+ if ($hasNoOrOneByMonth && empty($byMonthDay) && empty($byDay) && empty($byYearDay) && empty($byWeekNum)) {
+ $this->addFragment($this->translator->trans('on'));
+ $monthNum = (is_array($byMonth) && count($byMonth)) ? $byMonth[0] : $rule->getStartDate()->format('n');
+ $this->addFragment(
+ $this->translator->trans('day_month', array('month' => $monthNum, 'day' => $rule->getStartDate()->format('d')))
+ );
+ } elseif (!empty($byMonth)) {
+ if ($interval != 1) {
+ $this->addFragment($this->translator->trans('in_month'));
+ }
+
+ $this->addByMonth($rule);
+ }
+
+ if (!empty($byMonthDay)) {
+ $this->addByMonthDay($rule);
+ $this->addFragment($this->translator->trans('of_the_month'));
+ } else if (!empty($byDay)) {
+ $this->addByDay($rule);
+ }
+
+ if (!empty($byYearDay)) {
+ $this->addFragment($this->translator->trans('on the'));
+ $this->addFragment($this->getByYearDayAsText($byYearDay));
+ $this->addFragment($this->translator->trans('day'));
+ }
+
+ if (!empty($byWeekNum)) {
+ $this->addFragment($this->translator->trans('in_week'));
+ $this->addFragment($this->translator->trans($this->isPlural(count($byWeekNum)) ? 'weeks' : 'week'));
+ $this->addFragment($this->getByWeekNumberAsText($byWeekNum));
+ }
+
+ if (empty($byMonthDay) && empty($byYearDay) && empty($byDay) && !empty($byWeekNum)) {
+ $this->addDayOfWeek($rule);
+ }
+ }
+
+ protected function addMonthly(Rule $rule)
+ {
+ $interval = $rule->getInterval();
+ $byMonth = $rule->getByMonth();
+
+ if (!empty($byMonth) && $interval == 1) {
+ $this->addFragment($this->translator->trans('every_month_list'));
+ } else {
+ $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% months' : 'every month', array('count' => $interval)));
+ }
+
+ if (!empty($byMonth)) {
+ if ($interval != 1) {
+ $this->addFragment($this->translator->trans('in_month'));
+ }
+
+ $this->addByMonth($rule);
+ }
+
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+ if (!empty($byMonthDay)) {
+ $this->addByMonthDay($rule);
+ } else if (!empty($byDay)) {
+ $this->addByDay($rule);
+ }
+ }
+
+ protected function addWeekly(Rule $rule)
+ {
+ $interval = $rule->getInterval();
+ $byMonth = $rule->getByMonth();
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+
+ $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% weeks' : 'every week', array('count' => $interval)));
+
+ if (empty($byMonthDay) && empty($byDay)) {
+ $this->addDayOfWeek($rule);
+ }
+
+ if (!empty($byMonth)) {
+ $this->addFragment($this->translator->trans('in_month'));
+ $this->addByMonth($rule);
+ }
+
+ if (!empty($byMonthDay)) {
+ $this->addByMonthDay($rule);
+ $this->addFragment($this->translator->trans('of_the_month'));
+ } else if (!empty($byDay)) {
+ $this->addByDay($rule);
+ }
+ }
+
+ protected function addDaily(Rule $rule)
+ {
+ $interval = $rule->getInterval();
+ $byMonth = $rule->getByMonth();
+
+ $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% days' : 'every day', array('count' => $interval)));
+
+ if (!empty($byMonth)) {
+ $this->addFragment($this->translator->trans('in_month'));
+ $this->addByMonth($rule);
+ }
+
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+ if (!empty($byMonthDay)) {
+ $this->addByMonthDay($rule);
+ $this->addFragment($this->translator->trans('of_the_month'));
+ } else if (!empty($byDay)) {
+ $this->addByDay($rule);
+ }
+ }
+
+ protected function addHourly(Rule $rule)
+ {
+ $interval = $rule->getInterval();
+ $byMonth = $rule->getByMonth();
+
+ $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% hours' : 'every hour', array('count' => $interval)));
+
+ if (!empty($byMonth)) {
+ $this->addFragment($this->translator->trans('in_month'));
+ $this->addByMonth($rule);
+ }
+
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+ if (!empty($byMonthDay)) {
+ $this->addByMonthDay($rule);
+ $this->addFragment($this->translator->trans('of_the_month'));
+ } else if (!empty($byDay)) {
+ $this->addByDay($rule);
+ }
+ }
+
+ protected function addByMonth(Rule $rule)
+ {
+ $byMonth = $rule->getByMonth();
+
+ if (empty($byMonth)) {
+ return;
+ }
+
+ $this->addFragment($this->getByMonthAsText($byMonth));
+ }
+
+ protected function addByMonthDay(Rule $rule)
+ {
+ $byMonthDay = $rule->getByMonthDay();
+ $byDay = $rule->getByDay();
+
+ if (!empty($byDay)) {
+ $this->addFragment($this->translator->trans('on'));
+ $this->addFragment($this->getByDayAsText($byDay, 'or'));
+ $this->addFragment($this->translator->trans('the_for_monthday'));
+ $this->addFragment($this->getByMonthDayAsText($byMonthDay, 'or'));
+ } else {
+ $this->addFragment($this->translator->trans('on the'));
+ $this->addFragment($this->getByMonthDayAsText($byMonthDay, 'and'));
+ }
+ }
+
+ protected function addByDay(Rule $rule)
+ {
+ $byDay = $rule->getByDay();
+
+ $this->addFragment($this->translator->trans('on'));
+ $this->addFragment($this->getByDayAsText($byDay));
+ }
+
+ protected function addDayOfWeek(Rule $rule)
+ {
+ $this->addFragment($this->translator->trans('on'));
+ $dayNames = $this->translator->trans('day_names');
+ $this->addFragment($dayNames[$rule->getStartDate()->format('w')]);
+ }
+
+ public function getByMonthAsText($byMonth)
+ {
+ if (empty($byMonth)) {
+ return '';
+ }
+
+ if (count($byMonth) > 1) {
+ sort($byMonth);
+ }
+
+ $monthNames = $this->translator->trans('month_names');
+ $byMonth = array_map(
+ function ($monthInt) use ($monthNames) {
+ return $monthNames[$monthInt - 1];
+ },
+ $byMonth
+ );
+
+ return $this->getListStringFromArray($byMonth);
+ }
+
+ public function getByDayAsText($byDay, $listSeparator = 'and')
+ {
+ if (empty($byDay)) {
+ return '';
+ }
+
+ $map = array(
+ 'SU' => null,
+ 'MO' => null,
+ 'TU' => null,
+ 'WE' => null,
+ 'TH' => null,
+ 'FR' => null,
+ 'SA' => null
+ );
+
+ $dayNames = $this->translator->trans('day_names');
+ $timestamp = mktime(1, 1, 1, 1, 12, 2014); // A Sunday
+ foreach (array_keys($map) as $short) {
+ $long = $dayNames[date('w', $timestamp)];
+ $map[$short] = $long;
+ $timestamp += 86400;
+ }
+
+ $numOrdinals = 0;
+ foreach ($byDay as $key => $short) {
+ $day = strtoupper($short);
+ $string = '';
+
+ if (preg_match('/([+-]?)([0-9]*)([A-Z]+)/', $short, $parts)) {
+ $symbol = $parts[1];
+ $nth = $parts[2];
+ $day = $parts[3];
+
+ if (!empty($nth)) {
+ ++$numOrdinals;
+ $string .= $this->getOrdinalNumber($symbol == '-' ? -$nth : $nth);
+ }
+ }
+
+ if (!isset($map[$day])) {
+ throw new \RuntimeException("byDay $short could not be transformed");
+ }
+
+ if (!empty($string)) {
+ $string .= ' ';
+ }
+
+ $byDay[$key] = ltrim($string.$map[$day]);
+ }
+
+ $output = $numOrdinals ? $this->translator->trans('the_for_weekday') . ' ' : '';
+ if ($output == ' ') {
+ $output = '';
+ }
+ $output .= $this->getListStringFromArray($byDay, $listSeparator);
+
+ return $output;
+ }
+
+ public function getByMonthDayAsText($byMonthDay, $listSeparator = 'and')
+ {
+ if (empty($byMonthDay)) {
+ return '';
+ }
+
+ // sort negative indices in reverse order so we get e.g. 1st, 2nd, 4th, 3rd last, last day
+ usort($byMonthDay, function ($a, $b) {
+ if (($a < 0 && $b < 0) || ($a >= 0 && $b >= 0)) {
+ return $a - $b;
+ }
+
+ return $b - $a;
+ });
+
+ // generate ordinal numbers and insert a "on the" for clarity in the middle if we have both
+ // positive and negative ordinals. This is to avoid confusing situations like:
+ //
+ // monthly on the 1st and 2nd to the last day
+ //
+ // which gets clarified to:
+ //
+ // monthly on the 1st day and on the 2nd to the last day
+ $hadPositives = false;
+ $hadNegatives = false;
+ foreach ($byMonthDay as $index => $day) {
+ $prefix = '';
+ if ($day >= 0) {
+ $hadPositives = true;
+ }
+ if ($day < 0) {
+ if ($hadPositives && !$hadNegatives && $listSeparator === 'and') {
+ $prefix = $this->translator->trans('on the') . ' ';
+ }
+ $hadNegatives = true;
+ }
+ $byMonthDay[$index] = $prefix . $this->getOrdinalNumber($day, end($byMonthDay) < 0, true);
+ }
+
+ return $this->getListStringFromArray($byMonthDay, $listSeparator);
+ }
+
+ public function getByYearDayAsText($byYearDay)
+ {
+ if (empty($byYearDay)) {
+ return '';
+ }
+
+ // sort negative indices in reverse order so we get e.g. 1st, 2nd, 4th, 3rd last, last day
+ usort($byYearDay, function ($a, $b) {
+ if (($a < 0 && $b < 0) || ($a >= 0 && $b >= 0)) {
+ return $a - $b;
+ }
+
+ return $b - $a;
+ });
+
+ $byYearDay = array_map(
+ array($this, 'getOrdinalNumber'),
+ $byYearDay,
+ array_fill(0, count($byYearDay), end($byYearDay) < 0)
+ );
+
+ return $this->getListStringFromArray($byYearDay);
+ }
+
+ public function getByWeekNumberAsText($byWeekNum)
+ {
+ if (empty($byWeekNum)) {
+ return '';
+ }
+
+ if (count($byWeekNum) > 1) {
+ sort($byWeekNum);
+ }
+
+ return $this->getListStringFromArray($byWeekNum);
+ }
+
+ protected function addFragment($fragment)
+ {
+ if ($fragment && $fragment !== ' ') {
+ $this->fragments[] = $fragment;
+ }
+ }
+
+ public function resetFragments()
+ {
+ $this->fragments = array();
+ }
+
+ protected function isPlural($number)
+ {
+ return $number % 100 != 1;
+ }
+
+
+ protected function getOrdinalNumber($number, $hasNegatives = false, $dayInMonth = false)
+ {
+ if (!preg_match('{^-?\d+$}D', $number)) {
+ throw new \RuntimeException('$number must be a whole number');
+ }
+
+ return $this->translator->trans('ordinal_number', array('number' => $number, 'has_negatives' => $hasNegatives, 'day_in_month' => $dayInMonth));
+ }
+
+ protected function getListStringFromArray($values, $separator = 'and')
+ {
+ $separator = $this->translator->trans($separator);
+
+ if (!is_array($values)) {
+ throw new \RuntimeException('$values must be an array.');
+ }
+
+ $numValues = count($values);
+
+ if (!$numValues) {
+ return '';
+ }
+
+ if ($numValues == 1) {
+ reset($values);
+
+ return current($values);
+ }
+
+ if ($numValues == 2) {
+ return implode(" $separator ", $values);
+ }
+
+ $lastValue = array_pop($values);
+ $output = implode(', ', $values);
+ $output .= " $separator ".$lastValue;
+
+ return $output;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/Translator.php b/vendor/simshaun/recurr/src/Recurr/Transformer/Translator.php
new file mode 100644
index 0000000..cf3cb2a
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/Translator.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Recurr\Transformer;
+
+class Translator implements TranslatorInterface
+{
+ protected $data = array();
+
+ public function __construct($locale = 'en', $fallbackLocale = 'en')
+ {
+ $this->loadLocale($fallbackLocale);
+ if ($locale !== $fallbackLocale) {
+ $this->loadLocale($locale);
+ }
+ }
+
+ public function loadLocale($locale, $path = null)
+ {
+ if (!$path) {
+ $path = __DIR__ . '/../../../translations/' . $locale . '.php';
+ }
+ if (!file_exists($path)) {
+ throw new \InvalidArgumentException('Locale '.$locale.' could not be found in '.$path);
+ }
+
+ $this->data = array_merge($this->data, include $path);
+ }
+
+ public function trans($string, array $params = array())
+ {
+ $res = $this->data[$string];
+ if (is_object($res) && is_callable($res)) {
+ $res = $res($string, $params);
+ }
+
+ foreach ($params as $key => $val) {
+ $res = str_replace('%' . $key . '%', $val, $res);
+ }
+
+ return $res;
+ }
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/TranslatorInterface.php b/vendor/simshaun/recurr/src/Recurr/Transformer/TranslatorInterface.php
new file mode 100644
index 0000000..96e77f4
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Transformer/TranslatorInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Recurr\Transformer;
+
+interface TranslatorInterface
+{
+ public function trans($string);
+}
diff --git a/vendor/simshaun/recurr/src/Recurr/Weekday.php b/vendor/simshaun/recurr/src/Recurr/Weekday.php
new file mode 100644
index 0000000..b972b9d
--- /dev/null
+++ b/vendor/simshaun/recurr/src/Recurr/Weekday.php
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * Copyright 2013 Shaun Simmons
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * Based on rrule.js
+ * Copyright 2010, Jakub Roztocil and Lars Schoning
+ * https://github.com/jkbr/rrule/blob/master/LICENCE
+ */
+
+namespace Recurr;
+
+use Recurr\Exception\InvalidWeekday;
+
+/**
+ * Class Weekday is a storage container for a day of the week.
+ *
+ * @package Recurr
+ * @author Shaun Simmons <shaun@envysphere.com>
+ */
+class Weekday
+{
+ /**
+ * Weekday number.
+ *
+ * 0 = Sunday
+ * 1 = Monday
+ * 2 = Tuesday
+ * 3 = Wednesday
+ * 4 = Thursday
+ * 5 = Friday
+ * 6 = Saturday
+ *
+ * @var string
+ */
+ public $weekday;
+
+ /** @var int nth occurrence of the weekday */
+ public $num;
+
+ protected $days = array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU');
+
+ /**
+ * @param int|string $weekday 0-6 or MO..SU
+ * @param null|int $num
+ *
+ * @throws InvalidWeekday
+ */
+ public function __construct($weekday, $num)
+ {
+ if (is_numeric($weekday) && $weekday > 6 || $weekday < 0) {
+ throw new InvalidWeekday('Day is not a valid weekday (0-6)');
+ } elseif (!is_numeric($weekday) && !in_array($weekday, $this->days)) {
+ throw new InvalidWeekday('Day is not a valid weekday (SU, MO, ...)');
+ }
+
+ if (!is_numeric($weekday)) {
+ $weekday = array_search($weekday, $this->days);
+ }
+
+ $this->weekday = $weekday;
+ $this->num = $num;
+ }
+
+ public function __toString()
+ {
+ return $this->num . $this->days[$this->weekday];
+ }
+}
diff --git a/vendor/simshaun/recurr/translations/da.php b/vendor/simshaun/recurr/translations/da.php
new file mode 100644
index 0000000..ba4d6b6
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/da.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'søndag',
+ 'mandag',
+ 'tirsdag',
+ 'onsdag',
+ 'torsdag',
+ 'fredag',
+ 'lørdag',
+);
+$months = array(
+ 'januar',
+ 'februar',
+ 'marts',
+ 'april',
+ 'maj',
+ 'juni',
+ 'juli',
+ 'august',
+ 'september',
+ 'oktober',
+ 'november',
+ 'december',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Kunne ikke konvertere denne regel til tekst.',
+ 'for %count% times' => '%count% gange',
+ 'for one time' => 'en gang',
+ '(~ approximate)' => '(~ cirka)',
+ 'until %date%' => 't.o.m. %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return date('j', $params['date']) . '. '. $months[date('n', $params['date']) - 1].date(', Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].'. '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'og',
+ 'or' => 'eller',
+ 'in_month' => 'i', // e.g. weekly in January, May and August
+ 'in_week' => 'i', // e.g. yearly in week 3
+ 'on' => 'hver', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'den', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'den', // e.g. monthly on the 4th Monday
+ 'on the' => 'på den', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'i måneden', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'hvert %count% år',
+ 'every year' => 'årligt',
+ 'every_month_list' => 'hver', // e.g. every January, May and August
+ 'every %count% months' => 'hver %count% måned',
+ 'every month' => 'månedsvis',
+ 'every %count% weeks' => 'hver %count% uge',
+ 'every week' => 'ugenligt',
+ 'every %count% days' => 'hver %count% dag',
+ 'every day' => 'dagligt',
+ 'last' => 'sidste', // e.g. 2nd last Friday
+ 'days' => 'dage',
+ 'day' => 'dag',
+ 'weeks' => 'uger',
+ 'week' => 'uge',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array(':e', ':a', ':a', ':e', ':e', ':e', ':e', ':e', ':e', ':e');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'last';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' to the last';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'.';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' dag';
+ }
+
+ return $abbreviation . $suffix;
+ },
+); \ No newline at end of file
diff --git a/vendor/simshaun/recurr/translations/de.php b/vendor/simshaun/recurr/translations/de.php
new file mode 100644
index 0000000..9c836b8
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/de.php
@@ -0,0 +1,116 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Sonntag',
+ 'Montag',
+ 'Dienstag',
+ 'Mittwoch',
+ 'Donnerstag',
+ 'Freitag',
+ 'Samstag',
+);
+$months = array(
+ 'Januar',
+ 'Februar',
+ 'März',
+ 'April',
+ 'Mai',
+ 'Juni',
+ 'Juli',
+ 'August',
+ 'September',
+ 'Oktober',
+ 'November',
+ 'Dezember',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'RRule kann nicht vollständig zu Text konvertiert werden.',
+ 'for %count% times' => '%count% Mal',
+ 'for one time' => 'einmal',
+ '(~ approximate)' => '(~ ungefähr)',
+ 'until %date%' => 'bis %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. 4. Juli, 2014
+ return date('j. ', $params['date']) . $months[date('n', $params['date']) - 1] . date(', Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].'. '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'und',
+ 'or' => 'oder',
+ 'in_month' => 'im', // e.g. weekly in January, May and August
+ 'in_week' => 'in', // e.g. yearly in week 3
+ 'on' => 'am', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'dem', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => '', // e.g. monthly on the 4th Monday
+ 'on the' => 'am', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'des Monats', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'alle %count% Jahre',
+ 'every year' => 'jährlich',
+ 'every_month_list' => 'jeden', // e.g. every January, May and August
+ 'every %count% months' => 'alle %count% Monate',
+ 'every month' => 'monatlich',
+ 'every %count% weeks' => 'alle %count% Wochen',
+ 'every week' => 'wöchentlich',
+ 'every %count% days' => 'alle %count% Tage',
+ 'every day' => 'täglich',
+ 'every %count% hours' => 'alle %count% Stunden',
+ 'every hour' => 'stündlich',
+ 'last' => 'letzte', // e.g. 2nd last Friday
+ 'days' => 'Tage',
+ 'day' => 'Tag',
+ 'weeks' => 'Wochen',
+ 'week' => 'Woche',
+ 'hours' => 'Stunden',
+ 'hour' => 'stündlich',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $suffix = '';
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'letzten';
+ } elseif ($number == -2) {
+ $abbreviation = 'vorletzten';
+ } elseif ($number == -3) {
+ $abbreviation = 'drittletzten';
+ } elseif ($number == -4) {
+ $abbreviation = 'viertletzten';
+ } elseif ($number == -5) {
+ $abbreviation = 'fünftletzten';
+ } elseif ($number == -6) {
+ $abbreviation = 'sechstletzten';
+ } elseif ($number == -7) {
+ $abbreviation = 'siebtletzten';
+ } elseif ($number == -8) {
+ $abbreviation = 'achtletzten';
+ } elseif ($number == -9) {
+ $abbreviation = 'neuntletzten';
+ } elseif ($number == -10) {
+ $abbreviation = 'zehntletzten';
+ } elseif ($number == -11) {
+ $abbreviation = 'elftletzten';
+ } elseif ($isNegative) {
+ $number = abs($number);
+ $abbreviation = $number . 't letzten';
+ } else {
+ $abbreviation = $number . '.';
+ }
+
+ if (!empty($params['has_negatives']) && $isNegative) {
+ $suffix .= ' Tag';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/el.php b/vendor/simshaun/recurr/translations/el.php
new file mode 100644
index 0000000..dc8c344
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/el.php
@@ -0,0 +1,110 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Κυριακή',
+ 'Δευτέρα',
+ 'Τρίτη',
+ 'Τετάρτη',
+ 'Πέμπτη',
+ 'Παρασκευή',
+ 'Σάββατο',
+);
+$months = array(
+ 'Ιανουάριος',
+ 'Φεβρουάριος',
+ 'Μάρτιος',
+ 'Απρίλιος',
+ 'Μάιος',
+ 'Ιούνιος',
+ 'Ιούλιος',
+ 'Αύγουστος',
+ 'Σεπτέμβριος',
+ 'Οκτώβριος',
+ 'Νοέμβριος',
+ 'Δεκέμβριος',
+);
+$months_genitive = array(
+ 'Ιανουαρίου',
+ 'Φεβρουαρίου',
+ 'Μαρτίου',
+ 'Απριλίου',
+ 'Μαΐου',
+ 'Ιουνίου',
+ 'Ιουλίου',
+ 'Αυγούστου',
+ 'Σεπτεμβρίου',
+ 'Οκτωβρίου',
+ 'Νοεμβρίου',
+ 'Δεκεμβρίου',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Αδυναμία πλήρους μετατροπής αυτού του κανόνα rrule σε κείμενο.',
+ 'for %count% times' => 'για %count% φορές',
+ 'for one time' => 'για μία φορά',
+ '(~ approximate)' => '(~ κατά προσέγγιση)',
+ 'until %date%' => 'μέχρι %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months_genitive) { // outputs a day date, e.g. 4 Ιουλίου 2014
+ return date('j', $params['date']) . ' ' . $months_genitive[date('n', $params['date']) - 1] . ' '. date('Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months_genitive) { // outputs a day month, e.g. 4 Ιουλίου
+ return $params['day'] . ' ' . $months_genitive[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'και',
+ 'or' => 'ή',
+ 'in_month' => 'τον', // e.g. weekly in January, May and August
+ 'in_week' => 'την', // e.g. yearly in week 3
+ 'on' => 'την', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'την', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'την', // e.g. monthly on the 4th Monday
+ 'on the' => 'την', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'του μήνα', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'κάθε %count% χρόνια',
+ 'every year' => 'ετήσια',
+ 'every_month_list' => 'κάθε', // e.g. every January, May and August
+ 'every %count% months' => 'κάθε %count% μήνες',
+ 'every month' => 'μηνιαία',
+ 'every %count% weeks' => 'κάθε %count% εβδομάδες',
+ 'every week' => 'εβδομαδιαία',
+ 'every %count% days' => 'κάθε %count% ημέρες',
+ 'every day' => 'καθημερινά',
+ 'last' => 'τελευταία', // e.g. 2nd last Friday
+ 'days' => 'ημέρες',
+ 'day' => 'ημέρα',
+ 'weeks' => 'εβδομάδες',
+ 'week' => 'εβδομάδα',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = 'η';
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'τελευταία';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' μέχρι την τελευταία';
+ }
+
+ $abbreviation = $number . $ends;
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' ημέρα';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/en.php b/vendor/simshaun/recurr/translations/en.php
new file mode 100644
index 0000000..a7c1276
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/en.php
@@ -0,0 +1,104 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Sunday',
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday',
+);
+$months = array(
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Unable to fully convert this rrule to text.',
+ 'for %count% times' => 'for %count% times',
+ 'for one time' => 'once',
+ '(~ approximate)' => '(~ approximate)',
+ 'until %date%' => 'until %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return $months[date('n', $params['date']) - 1] . ' '. date('j, Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $months[$params['month'] - 1] . ' '. $params['day'];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'and',
+ 'or' => 'or',
+ 'in_month' => 'in', // e.g. weekly in January, May and August
+ 'in_week' => 'in', // e.g. yearly in week 3
+ 'on' => 'on', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'the', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'the', // e.g. monthly on the 4th Monday
+ 'on the' => 'on the', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'of the month', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'every %count% years',
+ 'every year' => 'yearly',
+ 'every_month_list' => 'every', // e.g. every January, May and August
+ 'every %count% months' => 'every %count% months',
+ 'every month' => 'monthly',
+ 'every %count% weeks' => 'every %count% weeks',
+ 'every week' => 'weekly',
+ 'every %count% days' => 'every %count% days',
+ 'every day' => 'daily',
+ 'every %count% hours' => 'every %count% hours',
+ 'every hour' => 'hourly',
+ 'last' => 'last', // e.g. 2nd last Friday
+ 'days' => 'days',
+ 'day' => 'day',
+ 'weeks' => 'weeks',
+ 'week' => 'week',
+ 'hours' => 'hours',
+ 'hour' => 'hour',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'last';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' to the last';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'th';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' day';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/es.php b/vendor/simshaun/recurr/translations/es.php
new file mode 100644
index 0000000..991ad1d
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/es.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'domingo',
+ 'lunes',
+ 'martes',
+ 'miércoles',
+ 'jueves',
+ 'viernes',
+ 'sábado',
+);
+$months = array(
+ 'enero',
+ 'febrero',
+ 'marzo',
+ 'abril',
+ 'mayo',
+ 'junio',
+ 'julio',
+ 'agosto',
+ 'septiembre',
+ 'octubre',
+ 'noviembre',
+ 'diciembre',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'No se puede convertir completamente este RRULE al texto.',
+ 'for %count% times' => 'para %count% veces',
+ 'for one time' => 'por una vez',
+ '(~ approximate)' => '(~ aproximado)',
+ 'until %date%' => 'hasta %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return $months[date('n', $params['date']) - 1] . ' '. date('j, Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $months[$params['month'] - 1] . ' '. $params['day'];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'y',
+ 'or' => 'o',
+ 'in_month' => 'en', // e.g. weekly in January, May and August
+ 'in_week' => 'en', // e.g. yearly in week 3
+ 'on' => 'en', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'el', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'en el', // e.g. monthly on the 4th Monday
+ 'on the' => 'en el', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'del mes', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'cada %count% años',
+ 'every year' => 'anual',
+ 'every_month_list' => 'cada', // e.g. every January, May and August
+ 'every %count% months' => 'cada %count% meses',
+ 'every month' => 'mensual',
+ 'every %count% weeks' => 'cada %count% semanas',
+ 'every week' => 'cada semana',
+ 'every %count% days' => 'cada %count% días',
+ 'every day' => 'diariamente',
+ 'last' => 'pasado', // e.g. 2nd last Friday
+ 'days' => 'día',
+ 'day' => 'el día',
+ 'weeks' => 'semanas',
+ 'week' => 'semana',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array('a', 'a', 'nd', 'a', 'a', 'a', 'a', 'a', 'a', 'a');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'último';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' a la última';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'a';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' día';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/eu.php b/vendor/simshaun/recurr/translations/eu.php
new file mode 100644
index 0000000..1c3adba
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/eu.php
@@ -0,0 +1,43 @@
+<?php
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Ezin izan da rrule testura osoki bihurtu.',
+ 'for %count% times' => '%count% aldiz',
+ 'for %count% time' => '%count% aldia',
+ '(~ approximate)' => '(~ inguru)',
+ 'until %date%' => '%date% arte', // e.g. every year until July 4, 2014
+ 'day_date' => defined('PHP_WINDOWS_VERSION_BUILD') ? '%B %#d, %Y' : '%B %e, %Y',
+ 'and' => 'eta',
+ 'or' => 'edo',
+ 'in' => 'hilabete hauetan:', // e.g. every week in January, May and August
+ 'on' => 'egun hauetan:', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the' => '',
+ 'on the' => '', // e.g. every year on the 1st and 200th day
+ 'every %count% years' => '%count% urtero',
+ 'every year' => 'urtero',
+ 'every_month_list' => 'hilabete hauetan', // e.g. every January, May and August
+ 'every %count% months' => '%count% hilabetero',
+ 'every month' => 'hilabetero',
+ 'every %count% weeks' => '%count% astero',
+ 'every week' => 'astero',
+ 'every %count% days' => '%count% egunero',
+ 'every day' => 'egunero',
+ 'last' => 'azken', // e.g. 2nd last Friday
+ 'days' => 'egun',
+ 'day' => 'egun',
+ 'weeks' => 'aste',
+ 'week' => 'aste',
+ 'ordinal_number' => function ($str, $params) { // formats a number with a prefix e.g. every year on the 1st and 200th day
+ $number = $params['number'];
+
+ $ends = array('garren', 'go', 'garren', 'garren', 'garren', 'garren', 'garren', 'garren', 'garren', 'garren');
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'garren';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+
+ return $abbreviation;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/fr.php b/vendor/simshaun/recurr/translations/fr.php
new file mode 100644
index 0000000..6bc44a7
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/fr.php
@@ -0,0 +1,103 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'dimanche',
+ 'lundi',
+ 'mardi',
+ 'mercredi',
+ 'jeudi',
+ 'vendredi',
+ 'samedi',
+);
+$months = array(
+ 'janvier',
+ 'février',
+ 'mars',
+ 'avril',
+ 'mai',
+ 'juin',
+ 'juillet',
+ 'août',
+ 'septembre',
+ 'octobre',
+ 'novembre',
+ 'décembre',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Cette règle de récurrence n\'a pas pu être convertie en texte.',
+ 'for %count% times' => '%count% fois',
+ 'for one time' => 'une fois',
+ '(~ approximate)' => '(~ approximation)',
+ 'until %date%' => 'jusqu\'au %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. 4 juillet, 2014
+ return date('j ', $params['date']) . $months[date('n', $params['date']) - 1] . date(', Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].' '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'et',
+ 'or' => 'ou',
+ 'in_month' => 'en', // e.g. weekly in January, May and August
+ 'in_week' => 'en', // e.g. yearly in week 3
+ 'on' => 'le', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'le', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => '', // e.g. monthly on the 4th Monday
+ 'on the' => 'le', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'du mois', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'tous les %count% ans',
+ 'every year' => 'chaque année',
+ 'every_month_list' => 'chaque', // e.g. every January, May and August
+ 'every %count% months' => 'tous les %count% mois',
+ 'every month' => 'chaque mois',
+ 'every %count% weeks' => 'toutes les %count% semaines',
+ 'every week' => 'chaque semaine',
+ 'every %count% days' => 'tous les %count% jours',
+ 'every day' => 'chaque jour',
+ 'every %count% hours' => 'toutes les %count% heures',
+ 'every hour' => 'chaque heure',
+ 'last' => 'dernier', // e.g. 2nd last Friday
+ 'days' => 'jours',
+ 'day' => 'jour',
+ 'weeks' => 'semaines',
+ 'week' => 'semaine',
+ 'hours' => 'heures',
+ 'hour' => 'heure',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $suffix = '';
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'dernier';
+ } elseif ($number == -2) {
+ $abbreviation = 'avant dernier';
+ } elseif ($isNegative) {
+ $number = abs($number);
+ $abbreviation = $number . 'ème au dernier';
+ } elseif ($number == 1 && (!$params['day_in_month'])) {
+ $abbreviation = $number . 'er';
+ } else if (!$params['day_in_month']) {
+ $abbreviation = $number . 'ème';
+ }
+ else {
+ $abbreviation = $number;
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' jour';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/it.php b/vendor/simshaun/recurr/translations/it.php
new file mode 100644
index 0000000..9f4d4c0
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/it.php
@@ -0,0 +1,116 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'domenica',
+ 'lunedì',
+ 'martedì',
+ 'mercoledì',
+ 'giovedì',
+ 'venerdì',
+ 'sabato',
+);
+$months = array(
+ 'gennaio',
+ 'febbraio',
+ 'marzo',
+ 'aprile',
+ 'maggio',
+ 'giugno',
+ 'luglio',
+ 'agosto',
+ 'settembre',
+ 'ottobre',
+ 'novembre',
+ 'dicembre',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Non è possibile convertire questo rrule in testo.',
+ 'for %count% times' => 'per %count% volte',
+ 'for one time' => 'per una volta',
+ '(~ approximate)' => '(~ approssimato)',
+ 'until %date%' => 'fino al %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. 4 luglio, 2014
+ return date('j ', $params['date']) . $months[date('n', $params['date']) - 1] . date(', Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].' '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'e',
+ 'or' => 'o',
+ 'in_month' => 'in', // e.g. weekly in January, May and August
+ 'in_week' => 'in', // e.g. yearly in week 3
+ 'on' => 'il', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'il', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'il', // e.g. monthly on the 4th Monday
+ 'on the' => 'il', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'del mese', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'ogni %count% anni',
+ 'every year' => 'ogni anno',
+ 'every_month_list' => 'ogni', // e.g. every January, May and August
+ 'every %count% months' => 'ogni %count% mesi',
+ 'every month' => 'ogni mese',
+ 'every %count% weeks' => 'ogni %count% settimane',
+ 'every week' => 'ogni settimana',
+ 'every %count% days' => 'ogni %count% giorni',
+ 'every day' => 'ogni giorno',
+ 'every %count% hours' => 'ogni %count% ore',
+ 'every hour' => 'ogni ora',
+ 'last' => 'scorso', // e.g. 2nd last Friday
+ 'days' => 'giorni',
+ 'day' => 'giorno',
+ 'weeks' => 'settimane',
+ 'week' => 'settimana',
+ 'hours' => 'ore',
+ 'hour' => 'ora',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $suffix = '';
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'ultimo';
+ } elseif ($number == -2) {
+ $abbreviation = 'penultimo';
+ } elseif ($number == -3) {
+ $abbreviation = 'terzultimo';
+ } elseif ($number == -4) {
+ $abbreviation = 'quarto ultimo';
+ } elseif ($number == -5) {
+ $abbreviation = 'quinta ultimo';
+ } elseif ($number == -6) {
+ $abbreviation = 'sesto ultimo';
+ } elseif ($number == -7) {
+ $abbreviation = 'settimo ultimo';
+ } elseif ($number == -8) {
+ $abbreviation = 'otto ultimo';
+ } elseif ($number == -9) {
+ $abbreviation = 'nono ultimo';
+ } elseif ($number == -10) {
+ $abbreviation = 'decimo ultimo';
+ } elseif ($number == -11) {
+ $abbreviation = 'undici ultimo';
+ } elseif ($isNegative) {
+ $number = abs($number);
+ $abbreviation = $number . ' ultimo';
+ } else {
+ $abbreviation = $number;
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' giorno';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/nl.php b/vendor/simshaun/recurr/translations/nl.php
new file mode 100644
index 0000000..77b2718
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/nl.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'zondag',
+ 'maandag',
+ 'dinsdag',
+ 'woensdag',
+ 'donderdag',
+ 'vrijdag',
+ 'zaterdag',
+);
+$months = array(
+ 'januari',
+ 'februari',
+ 'maart',
+ 'april',
+ 'mei',
+ 'juni',
+ 'juli',
+ 'augustus',
+ 'september',
+ 'oktober',
+ 'november',
+ 'december',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Unable to fully convert this rrule to text.',
+ 'for %count% times' => 'voor %count% keer',
+ 'for one time' => 'eenmalig',
+ '(~ approximate)' => '(~ ongeveer)',
+ 'until %date%' => 'tot en met %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return date('j', $params['date']).' '.$months[date('n', $params['date']) - 1] . ' '. date('Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].' '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'en',
+ 'or' => 'of',
+ 'in_month' => 'op', // e.g. weekly in January, May and August
+ 'in_week' => 'op', // e.g. yearly in week 3
+ 'on' => 'op', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'de', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'de', // e.g. monthly on the 4th Monday
+ 'on the' => 'op de', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'van de maand', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'elke %count% jaar',
+ 'every year' => 'jaarlijks',
+ 'every_month_list' => 'elke', // e.g. every January, May and August
+ 'every %count% months' => 'elke %count% maanden',
+ 'every month' => 'maandelijks',
+ 'every %count% weeks' => 'elke %count% weken',
+ 'every week' => 'wekelijks',
+ 'every %count% days' => 'elke %count% dagen',
+ 'every day' => 'dagelijks',
+ 'last' => 'laatste', // e.g. 2nd last Friday
+ 'days' => 'dagen',
+ 'day' => 'dag',
+ 'weeks' => 'weken',
+ 'week' => 'week',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array('ste', 'de', 'de', 'de', 'de', 'de', 'de', 'de', 'de', 'de');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'laatste';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' na laatste';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'ste';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' dag';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/no.php b/vendor/simshaun/recurr/translations/no.php
new file mode 100644
index 0000000..e7b5c95
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/no.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Sndag',
+ 'Mandag',
+ 'Tirsdag',
+ 'Onsdag',
+ 'Torsdag',
+ 'Fredag',
+ 'Lrdag',
+);
+$months = array(
+ 'Januar',
+ 'Februar',
+ 'Mars',
+ 'April',
+ 'Mai',
+ 'Juni',
+ 'Juli',
+ 'August',
+ 'September',
+ 'Oktober',
+ 'November',
+ 'Desember',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Kunne ikke konvertere rrule til tekst.',
+ 'for %count% times' => '%count% ganger',
+ 'for one time' => 'en gang',
+ '(~ approximate)' => '(~ omtrent)',
+ 'until %date%' => 'frem til %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return $months[date('n', $params['date']) - 1] . ' '. date('j, Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $months[$params['month'] - 1] . ' '. $params['day'];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'og',
+ 'or' => 'eller',
+ 'in_month' => 'i', // e.g. weekly in January, May and August
+ 'in_week' => 'i', // e.g. yearly in week 3
+ 'on' => 'p', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'den', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'den', // e.g. monthly on the 4th Monday
+ 'on the' => 'p den', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'i mneden', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'hvert %count% r',
+ 'every year' => 'rlig',
+ 'every_month_list' => 'hver', // e.g. every January, May and August
+ 'every %count% months' => 'hver %count% mned',
+ 'every month' => 'mnedlig',
+ 'every %count% weeks' => 'hver %count% uke',
+ 'every week' => 'ukentlig',
+ 'every %count% days' => 'hver %count% dag',
+ 'every day' => 'daglig',
+ 'last' => 'siste', // e.g. 2nd last Friday
+ 'days' => 'dager',
+ 'day' => 'dag',
+ 'weeks' => 'uker',
+ 'week' => 'uke',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'sist';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' til den siste';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'th';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' dag';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/pt-br.php b/vendor/simshaun/recurr/translations/pt-br.php
new file mode 100644
index 0000000..b92b5dc
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/pt-br.php
@@ -0,0 +1,87 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'domingo',
+ 'segunda-feira',
+ 'terça-feira',
+ 'quarta-feira',
+ 'quinta-feira',
+ 'sexta-feira',
+ 'sábado',
+);
+$months = array(
+ 'Janeiro',
+ 'Fevereiro',
+ 'Março',
+ 'Abril',
+ 'Maio',
+ 'Junho',
+ 'Julho',
+ 'Agosto',
+ 'Setembro',
+ 'Outubro',
+ 'Novembro',
+ 'Dezembro',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Não foi possível converter esta regra para texto.',
+ 'for %count% times' => 'por %count% vezes',
+ 'for one time' => 'uma vez',
+ '(~ approximate)' => '(~ approximado)',
+ 'until %date%' => 'até %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return date('j', $params['date']) . ' de ' . $months[date('n', $params['date']) - 1] . ' de ' . date('Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $params['day'].' de '.$months[$params['month'] - 1];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'e',
+ 'or' => 'ou',
+ 'in_month' => 'em', // e.g. weekly in January, May and August
+ 'in_week' => 'na', // e.g. yearly in week 3
+ 'on' => 'à', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'o', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'o', // e.g. monthly on the 4th Monday
+ 'on the' => 'no', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'do mês', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'a cada %count% anos',
+ 'every year' => 'anualmente',
+ 'every_month_list' => 'sempre em', // e.g. every January, May and August
+ 'every %count% months' => 'a cada %count% meses',
+ 'every month' => 'mensalmente',
+ 'every %count% weeks' => 'a cada %count% semanas',
+ 'every week' => 'semanalmente',
+ 'every %count% days' => 'a cada %count% dias',
+ 'every day' => 'diariamente',
+ 'last' => 'último', // e.g. 2nd last Friday
+ 'days' => 'dias',
+ 'day' => 'dia',
+ 'weeks' => 'semanas',
+ 'week' => 'semana',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $abbreviation = $number.'°';
+ $isNegative = $number < 0;
+ if ($isNegative) {
+ $abbreviation = $abbreviation.' último';
+ }
+
+ $suffix = '';
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' dia';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/sv.php b/vendor/simshaun/recurr/translations/sv.php
new file mode 100644
index 0000000..0dafeae
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/sv.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Söndag',
+ 'Måndag',
+ 'Tisdag',
+ 'Onsdag',
+ 'Torsdag',
+ 'Fredag',
+ 'Lördag',
+);
+$months = array(
+ 'Januari',
+ 'Februari',
+ 'Mars',
+ 'April',
+ 'Maj',
+ 'Juni',
+ 'Juli',
+ 'Augusti',
+ 'September',
+ 'Oktober',
+ 'November',
+ 'December',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Kunde inte konvertera denna rrule till text.',
+ 'for %count% times' => '%count% gånger',
+ 'for one time' => 'en gång',
+ '(~ approximate)' => '(~ ungefärlig)',
+ 'until %date%' => 't.o.m. %date%', // e.g. every year until July 4, 2014
+ 'day_date' => function ($str, $params) use ($days, $months) { // outputs a day date, e.g. July 4, 2014
+ return $months[date('n', $params['date']) - 1] . ' '. date('j, Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $months[$params['month'] - 1].' '.$params['day'];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 'och',
+ 'or' => 'eller',
+ 'in_month' => 'i', // e.g. weekly in January, May and August
+ 'in_week' => 'i', // e.g. yearly in week 3
+ 'on' => 'på', // e.g. every day on Tuesday, Wednesday and Friday
+ 'the_for_monthday' => 'den', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'den', // e.g. monthly on the 4th Monday
+ 'on the' => 'på den', // e.g. every year on the 1st and 200th day
+ 'of_the_month' => 'i månaden', // e.g. every year on the 2nd or 3rd of the month
+ 'every %count% years' => 'varje %count% år',
+ 'every year' => 'årligen',
+ 'every_month_list' => 'varje', // e.g. every January, May and August
+ 'every %count% months' => 'varje %count% månad',
+ 'every month' => 'månadsvis',
+ 'every %count% weeks' => 'varje %count% vecka',
+ 'every week' => 'veckovis',
+ 'every %count% days' => 'varje %count% dag',
+ 'every day' => 'dagligen',
+ 'last' => 'sista', // e.g. 2nd last Friday
+ 'days' => 'dagar',
+ 'day' => 'dag',
+ 'weeks' => 'veckor',
+ 'week' => 'vecka',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array(':e', ':a', ':a', ':e', ':e', ':e', ':e', ':e', ':e', ':e');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'last';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' to the last';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'th';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' dag';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);
diff --git a/vendor/simshaun/recurr/translations/tr.php b/vendor/simshaun/recurr/translations/tr.php
new file mode 100644
index 0000000..33d5295
--- /dev/null
+++ b/vendor/simshaun/recurr/translations/tr.php
@@ -0,0 +1,100 @@
+<?php
+
+// sunday first as date('w') is zero-based on sunday
+$days = array(
+ 'Pazar',
+ 'Pazartesi',
+ 'Salı',
+ 'Çarşamba',
+ 'Perşembe',
+ 'Cuma',
+ 'Cumartesi',
+);
+$months = array(
+ 'Ocak',
+ 'Şubat',
+ 'Mart',
+ 'Nisan',
+ 'Mayıs',
+ 'Haziran',
+ 'Temmuz',
+ 'Ağustos',
+ 'Eylül',
+ 'Ekim',
+ 'Kasım',
+ 'Aralık',
+);
+
+return array(
+ 'Unable to fully convert this rrule to text.' => 'Bu rrule tam metne dönüştürülemiyor.',
+ 'for %count% times' => '%count% kez',
+ 'for one time' => 'bir kere',
+ '(~ approximate)' => '(~ yaklaşık)',
+ 'until %date%' => 'kadar %date%', // e.g. 4 Temmuz 2014 e kadar her yıl
+ 'day_date' => function ($str, $params) use ($days, $months) { // tarih çıktıları, e.g. Temmuz 4, 2014
+ return $months[date('n', $params['date']) - 1] . ' '. date('j, Y', $params['date']);
+ },
+ 'day_month' => function ($str, $params) use ($days, $months) { // outputs a day month, e.g. July 4
+ return $months[$params['month'] - 1].' '.$params['day'];
+ },
+ 'day_names' => $days,
+ 'month_names' => $months,
+ 'and' => 've',
+ 'or' => 'veya',
+ 'in_month' => 'içinde', // e.g. Ocak, Mayıs ve Ağustos'ta haftalık
+ 'in_week' => 'içinde', // e.g. yıllık haftada 3
+ 'on' => 'on', // e.g. her Salı, Çarşamba ve Cuma günü
+ 'the_for_monthday' => 'the', // e.g. monthly on Tuesday the 1st
+ 'the_for_weekday' => 'the', // e.g. monthly on the 4th Monday
+ 'on the' => 'üzerinde', // e.g. her yıl 1. ve 200. günde
+ 'of_the_month' => 'ayın', // e.g. her yıl 2. ve 3. ayın
+ 'every %count% years' => 'every %count% years',
+ 'every year' => 'yıllık',
+ 'every_month_list' => 'her', // e.g. her Ocak, Mayıs ve Ağustos
+ 'every %count% months' => 'her %count% ay',
+ 'every month' => 'aylık',
+ 'every %count% weeks' => 'her %count% hafta',
+ 'every week' => 'haftalık',
+ 'every %count% days' => 'her %count% gün',
+ 'every day' => 'günlük',
+ 'last' => 'son', // e.g. 2nd last Friday
+ 'days' => 'günler',
+ 'day' => 'gün',
+ 'weeks' => 'haftalar',
+ 'week' => 'hafta',
+ // formats a number with a prefix e.g. every year on the 1st and 200th day
+ // negative numbers should be handled as in '5th to the last' or 'last'
+ //
+ // if has_negatives is true in the params, it is good form to add 'day' after
+ // each number, as in: 'every month on the 5th day or 2nd to the last day' or
+ // it may be confusing like 'every month on the 5th or 2nd to the last day'
+ 'ordinal_number' => function ($str, $params) {
+ $number = $params['number'];
+
+ $ends = array('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th');
+ $suffix = '';
+
+ $isNegative = $number < 0;
+
+ if ($number == -1) {
+ $abbreviation = 'son';
+ } else {
+ if ($isNegative) {
+ $number = abs($number);
+ $suffix = ' sonuna kadar';
+ }
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13) {
+ $abbreviation = $number.'th';
+ } else {
+ $abbreviation = $number.$ends[$number % 10];
+ }
+ }
+
+ if (!empty($params['has_negatives'])) {
+ $suffix .= ' gün';
+ }
+
+ return $abbreviation . $suffix;
+ },
+);