diff options
author | Minteck <contact@minteck.org> | 2023-02-23 19:34:56 +0100 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2023-02-23 19:34:56 +0100 |
commit | 3d1cd02f27518f1a04374c7c8320cd5d82ede6e9 (patch) | |
tree | 75be5fba4368472fb11c8015aee026b2b9a71888 /includes/ical/src/Freq.php | |
parent | 8cc1f13c17fa2fb5a4410542d39e650e02945634 (diff) | |
download | pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.tar.gz pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.tar.bz2 pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.zip |
Updated 40 files, added 37 files, deleted 1103 files and renamed 3905 files (automated)
Diffstat (limited to 'includes/ical/src/Freq.php')
-rw-r--r-- | includes/ical/src/Freq.php | 633 |
1 files changed, 0 insertions, 633 deletions
diff --git a/includes/ical/src/Freq.php b/includes/ical/src/Freq.php deleted file mode 100644 index 8557683..0000000 --- a/includes/ical/src/Freq.php +++ /dev/null @@ -1,633 +0,0 @@ -<?php - -namespace om; - -use DateTime; -use DateTimeZone; -use Exception; - -/** - * Class taken from https://github.com/coopTilleuls/intouch-iCalendar.git (Freq.php) - * - * @author PC Drew <pc@schoolblocks.com> - */ - -/** - * A class to store Frequency-rules in. Will allow a easy way to find the - * last and next occurrence of the rule. - * - * No - this is so not pretty. But.. ehh.. You do it better, and I will - * gladly accept patches. - * - * Created by trail-and-error on the examples given in the RFC. - * - * TODO: Update to a better way of doing calculating the different options. - * Instead of only keeping track of the best of the current dates found - * it should instead keep a array of all the calculated dates within the - * period. - * This should fix the issues with multi-rule + multi-rule interference, - * and make it possible to implement the SETPOS rule. - * By pushing the next period onto the stack as the last option will - * (hopefully) remove the need for the awful simpleMode - * - * @author Morten Fangel (C) 2008 - * @author Michael Kahn (C) 2013 - * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK - */ -class Freq { - - protected array $weekdays = [ - 'MO' => 'monday', - 'TU' => 'tuesday', - 'WE' => 'wednesday', - 'TH' => 'thursday', - 'FR' => 'friday', - 'SA' => 'saturday', - 'SU' => 'sunday', - ]; - protected array $knownRules = [ - 'month', - 'weekno', - 'day', - 'monthday', - 'yearday', - 'hour', - 'minute', - ]; //others : 'setpos', 'second' - - protected array $ruleModifiers = ['wkst']; - protected bool $simpleMode = true; - - protected array $rules = ['freq' => 'yearly', 'interval' => 1]; - protected int $start = 0; - protected string $freq = ''; - - protected array $excluded; //EXDATE - protected array $added; //RDATE - - protected $cache; // getAllOccurrences() - - /** - * Constructs a new Frequency-rule - * - * @param string|array $rule - * @param int $start Unix-timestamp (important : Need to be the start of Event) - * @param array $excluded of int (timestamps), see EXDATE documentation - * @param array $added of int (timestamps), see RDATE documentation - * @throws Exception - */ - public function __construct($rule, int $start, array $excluded = [], array $added = []) { - $this->start = $start; - $this->excluded = []; - - $rules = []; - foreach ($rule as $k => $v) { - $this->rules[strtolower($k)] = $v; - } - - if (isset($this->rules['until']) && is_string($this->rules['until'])) { - $this->rules['until'] = strtotime($this->rules['until']); - } elseif ($this->rules['until'] instanceof DateTime) { - $this->rules['until'] = $this->rules['until']->getTimestamp(); - } - $this->freq = strtolower($this->rules['freq']); - - foreach ($this->knownRules as $rule) { - if (isset($this->rules['by' . $rule])) { - if ($this->isPrerule($rule, $this->freq)) { - $this->simpleMode = false; - } - } - } - - if (!$this->simpleMode) { - if (!(isset($this->rules['byday']) || isset($this->rules['bymonthday']) || isset($this->rules['byyearday']))) { - $this->rules['bymonthday'] = date('d', $this->start); - } - } - - //set until, and cache - if (isset($this->rules['count'])) { - - $cache[$ts] = $ts = $this->start; - for ($n = 1; $n < $this->rules['count']; $n++) { - $ts = $this->findNext($ts); - $cache[$ts] = $ts; - } - $this->rules['until'] = $ts; - - //EXDATE - if (!empty($excluded)) { - foreach ($excluded as $ts) { - unset($cache[$ts]); - } - } - //RDATE - if (!empty($added)) { - $cache = array_unique(array_merge(array_values($cache), $added)); - asort($cache); - } - - $this->cache = array_values($cache); - } - - $this->excluded = $excluded; - $this->added = $added; - } - - private function isPrerule(string $rule, string $freq): bool { - if ($rule === 'year') { - return false; - } - if ($rule === 'month' && $freq === 'yearly') { - return true; - } - if ($rule === 'monthday' && in_array($freq, ['yearly', 'monthly']) && !isset($this->rules['byday'])) { - return true; - } - // TODO: is it faster to do monthday first, and ignore day if monthday exists? - prolly by a factor of 4.. - if ($rule === 'yearday' && $freq === 'yearly') { - return true; - } - if ($rule === 'weekno' && $freq === 'yearly') { - return true; - } - if ($rule === 'day' && in_array($freq, ['yearly', 'monthly', 'weekly'])) { - return true; - } - if ($rule === 'hour' && in_array($freq, ['yearly', 'monthly', 'weekly', 'daily'])) { - return true; - } - if ($rule === 'minute') { - return true; - } - - return false; - } - - /** - * Calculates the next time after the given offset that the rule - * will apply. - * - * The approach to finding the next is as follows: - * First we establish a timeframe to find timestamps in. This is - * between $offset and the end of the period that $offset is in. - * - * We then loop though all the rules (that is a Prerule in the - * current freq.), and finds the smallest timestamp inside the - * timeframe. - * - * If we find something, we check if the date is a valid recurrence - * (with validDate). If it is, we return it. Otherwise we try to - * find a new date inside the same timeframe (but using the new- - * found date as offset) - * - * If no new timestamps were found in the period, we try in the - * next period - * - * @param int $offset - * @return int|bool - * @throws Exception - */ - public function findNext(int $offset) { - if (!empty($this->cache)) { - foreach ($this->cache as $ts) { - if ($ts > $offset) { - return $ts; - } - } - } - - $debug = false; - - //make sure the offset is valid - if ($offset === false || (isset($this->rules['until']) && $offset > $this->rules['until'])) { - if ($debug) printf("STOP: %s\n", date('r', $offset)); - return false; - } - - $found = true; - - //set the timestamp of the offset (ignoring hours and minutes unless we want them to be - //part of the calculations. - if ($debug) printf("O: %s\n", date('r', $offset)); - $hour = (in_array($this->freq, ['hourly', 'minutely']) && $offset > $this->start) ? date('H', $offset) : date( - 'H', $this->start - ); - $minute = (($this->freq === 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date( - 'i', $offset - ) : date('i', $this->start); - $t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y', $offset)); - if ($debug) printf("START: %s\n", date('r', $t)); - - if ($this->simpleMode) { - if ($offset < $t) { - $ts = $t; - if ($ts && in_array($ts, $this->excluded, true)) { - $ts = $this->findNext($ts); - } - } else { - $ts = $this->findStartingPoint($t, $this->rules['interval'], false); - if (!$this->validDate($ts)) { - $ts = $this->findNext($ts); - } - } - - return $ts; - } - - //EOP needs to have the same TIME as START ($t) - $tO = new DateTime('@' . $t, new DateTimeZone('UTC')); - - $eop = $this->findEndOfPeriod($offset); - $eopO = new DateTime('@' . $eop, new DateTimeZone('UTC')); - $eopO->setTime($tO->format('H'), $tO->format('i'), $tO->format('s')); - $eop = $eopO->getTimestamp(); - unset($eopO, $tO); - - if ($debug) { - echo 'EOP: ' . date('r', $eop) . "\n"; - } - foreach ($this->knownRules as $rule) { - if ($found && isset($this->rules['by' . $rule])) { - if ($this->isPrerule($rule, $this->freq)) { - $subRules = explode(',', $this->rules['by' . $rule]); - $_t = null; - foreach ($subRules as $subRule) { - $imm = call_user_func_array([$this, "ruleBy$rule"], [$subRule, $t]); - if ($imm === false) { - break; - } - if ($debug) { - printf("%s: %s A: %d\n", strtoupper($rule), date('r', $imm), intval($imm > $offset && $imm < $eop)); - } - if ($imm > $offset && $imm <= $eop && ($_t == null || $imm < $_t)) { - $_t = $imm; - } - } - if ($_t !== null) { - $t = $_t; - } else { - $found = $this->validDate($t); - } - } - } - } - - if ($offset < $this->start && $this->start < $t) { - $ts = $this->start; - } elseif ($found && ($t != $offset)) { - if ($this->validDate($t)) { - if ($debug) echo 'OK' . "\n"; - $ts = $t; - } else { - if ($debug) echo 'Invalid' . "\n"; - $ts = $this->findNext($t); - } - } else { - if ($debug) echo 'Not found' . "\n"; - $ts = $this->findNext($this->findStartingPoint($offset, $this->rules['interval'])); - } - if ($ts && in_array($ts, $this->excluded, true)) { - return $this->findNext($ts); - } - - return $ts; - } - - /** - * Finds the starting point for the next rule. It goes $interval - * 'freq' forward in time since the given offset - * - * @param int $offset - * @param int $interval - * @param boolean $truncate - * @return int - */ - private function findStartingPoint(int $offset, int $interval, $truncate = true): int { - $_freq = ($this->freq === 'daily') ? 'day__' : $this->freq; - $t = '+' . $interval . ' ' . substr($_freq, 0, -2) . 's'; - if ($_freq === 'monthly' && $truncate) { - if ($interval > 1) { - $offset = strtotime('+' . ($interval - 1) . ' months ', $offset); // FIXME return type int|false - } - $t = '+' . (date('t', $offset) - date('d', $offset) + 1) . ' days'; - } - - $sp = strtotime($t, $offset); - - if ($truncate) { - $sp = $this->truncateToPeriod($sp, $this->freq); - } - - return $sp; - } - - /** - * Resets the timestamp to the beginning of the - * period specified by freq - * - * Yes - the fall-through is on purpose! - * - * @param int $time - * @param string $freq - * @return int - */ - private function truncateToPeriod(int $time, string $freq): int { - $date = getdate($time); - switch ($freq) { - case 'yearly': - $date['mon'] = 1; - case 'monthly': - $date['mday'] = 1; - case 'daily': - $date['hours'] = 0; - case 'hourly': - $date['minutes'] = 0; - case 'minutely': - $date['seconds'] = 0; - break; - case 'weekly': - if (date('N', $time) == 1) { // FIXME wrong compare, date return string|false - $date['hours'] = 0; - $date['minutes'] = 0; - $date['seconds'] = 0; - } else { - $date = getdate(strtotime('last monday 0:00', $time)); - } - break; - } - return mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']); - } - - private function validDate($t): bool { - if (isset($this->rules['until']) && $t > $this->rules['until']) { - return false; - } - - if (in_array($t, $this->excluded, true)) { - return false; - } - - if (isset($this->rules['bymonth'])) { - $months = explode(',', $this->rules['bymonth']); - if (!in_array(date('m', $t), $months, true)) { - return false; - } - } - if (isset($this->rules['byday'])) { - $days = explode(',', $this->rules['byday']); - foreach ($days as $i => $k) { - $days[$i] = $this->weekdays[preg_replace('/[^A-Z]/', '', $k)]; - } - if (!in_array(strtolower(date('l', $t)), $days, true)) { - return false; - } - } - if (isset($this->rules['byweekno'])) { - $weeks = explode(',', $this->rules['byweekno']); - if (!in_array(date('W', $t), $weeks, true)) { - return false; - } - } - if (isset($this->rules['bymonthday'])) { - $weekdays = explode(',', $this->rules['bymonthday']); - foreach ($weekdays as $i => $k) { - if ($k < 0) { - $weekdays[$i] = date('t', $t) + $k + 1; - } - } - if (!in_array(date('d', $t), $weekdays, true)) { - return false; - } - } - if (isset($this->rules['byhour'])) { - $hours = explode(',', $this->rules['byhour']); - if (!in_array(date('H', $t), $hours, true)) { - return false; - } - } - - return true; - } - - /** - * Finds the earliest timestamp possible outside this period. - * - * @param int $offset - * @return int - */ - public function findEndOfPeriod($offset = 0) { - return $this->findStartingPoint($offset, 1, false); - } - - /** - * Returns the previous (most recent) occurrence of the rule from the - * given offset - * - * @param int $offset - * @return int - * @throws Exception - */ - public function previousOccurrence(int $offset) { - if (!empty($this->cache)) { - $t2 = $this->start; - foreach ($this->cache as $ts) { - if ($ts >= $offset) { - return $t2; - } - $t2 = $ts; - } - } else { - $ts = $this->start; - while (($t2 = $this->findNext($ts)) < $offset) { - if ($t2 == false) { - break; - } - $ts = $t2; - } - } - - return $ts; - } - - /** - * Returns the next occurrence of this rule after the given offset - * - * @param int $offset - * @return int - * @throws Exception - */ - public function nextOccurrence(int $offset) { - if ($offset < $this->start) { - return $this->firstOccurrence(); - } - return $this->findNext($offset); - } - - /** - * Finds the first occurrence of the rule. - * - * @return int timestamp - * @throws Exception - */ - public function firstOccurrence() { - $t = $this->start; - if (in_array($t, $this->excluded)) { - $t = $this->findNext($t); - } - - return $t; - } - - /** - * Finds the absolute last occurrence of the rule from the given offset. - * Builds also the cache, if not set before... - * - * @return int timestamp - * @throws Exception - */ - public function lastOccurrence() { - //build cache if not done - $this->getAllOccurrences(); - //return last timestamp in cache - return end($this->cache); - } - - /** - * Returns all timestamps array(), build the cache if not made before - * - * @return array - * @throws Exception - */ - public function getAllOccurrences() { - if (empty($this->cache)) { - $cache = []; - - //build cache - $next = $this->firstOccurrence(); - while ($next) { - $cache[] = $next; - $next = $this->findNext($next); - } - if (!empty($this->added)) { - $cache = array_unique(array_merge($cache, $this->added)); - asort($cache); - } - $this->cache = $cache; - } - - return $this->cache; - } - - /** - * Applies the BYDAY rule to the given timestamp - * - * @param string $rule - * @param int $t - * @return int - */ - private function ruleByDay(string $rule, int $t): int { - $dir = ($rule[0] === '-') ? -1 : 1; - $dir_t = ($dir === 1) ? 'next' : 'last'; - - $d = $this->weekdays[substr($rule, -2)]; - $s = $dir_t . ' ' . $d . ' ' . date('H:i:s', $t); - - if ($rule == substr($rule, -2)) { - if (date('l', $t) == ucfirst($d)) { - $s = 'today ' . date('H:i:s', $t); - } - - $_t = strtotime($s, $t); - - if ($_t == $t && in_array($this->freq, ['weekly', 'monthly', 'yearly'])) { - // Yes. This is not a great idea.. but hey, it works.. for now - $s = 'next ' . $d . ' ' . date('H:i:s', $t); - $_t = strtotime($s, $_t); - } - - return $_t; - } else { - $_f = $this->freq; - if (isset($this->rules['bymonth']) && $this->freq === 'yearly') { - $this->freq = 'monthly'; - } - if ($dir === -1) { - $_t = $this->findEndOfPeriod($t); - } else { - $_t = $this->truncateToPeriod($t, $this->freq); - } - $this->freq = $_f; - - $c = preg_replace('/[^0-9]/', '', $rule); - $c = ($c == '') ? 1 : $c; - - $n = $_t; - while ($c > 0) { - if ($dir === 1 && $c == 1 && date('l', $t) == ucfirst($d)) { - $s = 'today ' . date('H:i:s', $t); - } - $n = strtotime($s, $n); - $c--; - } - - return $n; - } - } - - private function ruleByMonth($rule, int $t) { - $_t = mktime(date('H', $t), date('i', $t), date('s', $t), $rule, date('d', $t), date('Y', $t)); - if ($t == $_t && isset($this->rules['byday'])) { - // TODO: this should check if one of the by*day's exists, and have a multi-day value - return false; - } else { - return $_t; - } - } - - private function ruleByMonthday($rule, int $t) { - if ($rule < 0) { - $rule = date('t', $t) + $rule + 1; - } - - return mktime(date('H', $t), date('i', $t), date('s', $t), date('m', $t), $rule, date('Y', $t)); - } - - private function ruleByYearday($rule, int $t) { - if ($rule < 0) { - $_t = $this->findEndOfPeriod(); - $d = '-'; - } else { - $_t = $this->truncateToPeriod($t, $this->freq); - $d = '+'; - } - $s = $d . abs($rule - 1) . ' days ' . date('H:i:s', $t); - - return strtotime($s, $_t); - } - - private function ruleByWeekno($rule, int $t) { - if ($rule < 0) { - $_t = $this->findEndOfPeriod(); - $d = '-'; - } else { - $_t = $this->truncateToPeriod($t, $this->freq); - $d = '+'; - } - - $sub = (date('W', $_t) == 1) ? 2 : 1; - $s = $d . abs($rule - $sub) . ' weeks ' . date('H:i:s', $t); - $_t = strtotime($s, $_t); - - return $_t; - } - - private function ruleByHour($rule, int $t) { - return mktime($rule, date('i', $t), date('s', $t), date('m', $t), date('d', $t), date('Y', $t)); - } - - private function ruleByMinute($rule, int $t) { - return mktime(date('h', $t), $rule, date('s', $t), date('m', $t), date('d', $t), date('Y', $t)); - } -} |