Page MenuHomec4science

PhutilCalendarRecurrenceSet.php
No OneTemporary

File Metadata

Created
Tue, Feb 11, 12:09

PhutilCalendarRecurrenceSet.php

<?php
final class PhutilCalendarRecurrenceSet
extends Phobject {
private $sources = array();
private $viewerTimezone = 'UTC';
public function addSource(PhutilCalendarRecurrenceSource $source) {
$this->sources[] = $source;
return $this;
}
public function setViewerTimezone($viewer_timezone) {
$this->viewerTimezone = $viewer_timezone;
return $this;
}
public function getViewerTimezone() {
return $this->viewerTimezone;
}
public function getEventsBetween(
PhutilCalendarDateTime $start = null,
PhutilCalendarDateTime $end = null,
$limit = null) {
if ($end === null && $limit === null) {
throw new Exception(
pht(
'Recurring event range queries must have an end date, a limit, or '.
'both.'));
}
$timezone = $this->getViewerTimezone();
$sources = array();
foreach ($this->sources as $source) {
$source = clone $source;
$source->setViewerTimezone($timezone);
$source->resetSource();
$sources[] = array(
'source' => $source,
'state' => null,
'epoch' => null,
);
}
if ($start) {
$start = clone $start;
$start->setViewerTimezone($timezone);
$min_epoch = $start->getEpoch();
} else {
$min_epoch = 0;
}
if ($end) {
$end = clone $end;
$end->setViewerTimezone($timezone);
$end_epoch = $end->getEpoch();
} else {
$end_epoch = null;
}
$results = array();
$index = 0;
$cursor = 0;
while (true) {
// Get the next event for each source which we don't have a future
// event for.
foreach ($sources as $key => $source) {
$state = $source['state'];
$epoch = $source['epoch'];
if ($state !== null && $epoch >= $cursor) {
// We have an event for this source, and it's a future event, so
// we don't need to do anything.
continue;
}
$next = $source['source']->getNextEvent($cursor);
if ($next === null) {
// This source doesn't have any more events, so we're all done.
unset($sources[$key]);
continue;
}
$next_epoch = $next->getEpoch();
if ($end_epoch !== null && $next_epoch > $end_epoch) {
// We have an end time and the next event from this source is
// past that end, so we know there are no more relevant events
// coming from this source.
unset($sources[$key]);
continue;
}
$sources[$key]['state'] = $next;
$sources[$key]['epoch'] = $next_epoch;
}
if (!$sources) {
// We've run out of sources which can produce valid events in the
// window, so we're all done.
break;
}
// Find the minimum event time across all sources.
$next_epoch = null;
foreach ($sources as $source) {
if ($next_epoch === null) {
$next_epoch = $source['epoch'];
} else {
$next_epoch = min($next_epoch, $source['epoch']);
}
}
$is_exception = false;
$next_source = null;
foreach ($sources as $source) {
if ($source['epoch'] == $next_epoch) {
if ($source['source']->getIsExceptionSource()) {
$is_exception = true;
} else {
$next_source = $source;
}
}
}
// If this is an exception, it means the event does NOT occur. We
// skip it and move on. If it's not an exception, it does occur, so
// we record it.
if (!$is_exception) {
// Only actually include this event in the results if it starts after
// any specified start time. We increment the index regardless, so we
// return results with proper offsets.
if ($next_source['epoch'] >= $min_epoch) {
$results[$index] = $next_source['state'];
}
$index++;
if ($limit !== null && (count($results) >= $limit)) {
break;
}
}
$cursor = $next_epoch + 1;
// If we have an end of the window and we've reached it, we're done.
if ($end_epoch) {
if ($cursor > $end_epoch) {
break;
}
}
}
return $results;
}
}

Event Timeline