Path: blob/master/src/applications/calendar/parser/data/PhutilCalendarRecurrenceSet.php
12262 views
<?php12final class PhutilCalendarRecurrenceSet3extends Phobject {45private $sources = array();6private $viewerTimezone = 'UTC';78public function addSource(PhutilCalendarRecurrenceSource $source) {9$this->sources[] = $source;10return $this;11}1213public function setViewerTimezone($viewer_timezone) {14$this->viewerTimezone = $viewer_timezone;15return $this;16}1718public function getViewerTimezone() {19return $this->viewerTimezone;20}2122public function getEventsBetween(23PhutilCalendarDateTime $start = null,24PhutilCalendarDateTime $end = null,25$limit = null) {2627if ($end === null && $limit === null) {28throw new Exception(29pht(30'Recurring event range queries must have an end date, a limit, or '.31'both.'));32}3334$timezone = $this->getViewerTimezone();3536$sources = array();37foreach ($this->sources as $source) {38$source = clone $source;39$source->setViewerTimezone($timezone);40$source->resetSource();4142$sources[] = array(43'source' => $source,44'state' => null,45'epoch' => null,46);47}4849if ($start) {50$start = clone $start;51$start->setViewerTimezone($timezone);52$min_epoch = $start->getEpoch();53} else {54$min_epoch = 0;55}5657if ($end) {58$end = clone $end;59$end->setViewerTimezone($timezone);60$end_epoch = $end->getEpoch();61} else {62$end_epoch = null;63}6465$results = array();66$index = 0;67$cursor = 0;68while (true) {69// Get the next event for each source which we don't have a future70// event for.71foreach ($sources as $key => $source) {72$state = $source['state'];73$epoch = $source['epoch'];7475if ($state !== null && $epoch >= $cursor) {76// We have an event for this source, and it's a future event, so77// we don't need to do anything.78continue;79}8081$next = $source['source']->getNextEvent($cursor);82if ($next === null) {83// This source doesn't have any more events, so we're all done.84unset($sources[$key]);85continue;86}8788$next_epoch = $next->getEpoch();8990if ($end_epoch !== null && $next_epoch > $end_epoch) {91// We have an end time and the next event from this source is92// past that end, so we know there are no more relevant events93// coming from this source.94unset($sources[$key]);95continue;96}9798$sources[$key]['state'] = $next;99$sources[$key]['epoch'] = $next_epoch;100}101102if (!$sources) {103// We've run out of sources which can produce valid events in the104// window, so we're all done.105break;106}107108// Find the minimum event time across all sources.109$next_epoch = null;110foreach ($sources as $source) {111if ($next_epoch === null) {112$next_epoch = $source['epoch'];113} else {114$next_epoch = min($next_epoch, $source['epoch']);115}116}117118$is_exception = false;119$next_source = null;120foreach ($sources as $source) {121if ($source['epoch'] == $next_epoch) {122if ($source['source']->getIsExceptionSource()) {123$is_exception = true;124} else {125$next_source = $source;126}127}128}129130// If this is an exception, it means the event does NOT occur. We131// skip it and move on. If it's not an exception, it does occur, so132// we record it.133if (!$is_exception) {134135// Only actually include this event in the results if it starts after136// any specified start time. We increment the index regardless, so we137// return results with proper offsets.138if ($next_source['epoch'] >= $min_epoch) {139$results[$index] = $next_source['state'];140}141$index++;142143if ($limit !== null && (count($results) >= $limit)) {144break;145}146}147148$cursor = $next_epoch + 1;149150// If we have an end of the window and we've reached it, we're done.151if ($end_epoch) {152if ($cursor > $end_epoch) {153break;154}155}156}157158return $results;159}160161}162163164