Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/calendar/parser/data/PhutilCalendarRecurrenceSet.php
12262 views
1
<?php
2
3
final class PhutilCalendarRecurrenceSet
4
extends Phobject {
5
6
private $sources = array();
7
private $viewerTimezone = 'UTC';
8
9
public function addSource(PhutilCalendarRecurrenceSource $source) {
10
$this->sources[] = $source;
11
return $this;
12
}
13
14
public function setViewerTimezone($viewer_timezone) {
15
$this->viewerTimezone = $viewer_timezone;
16
return $this;
17
}
18
19
public function getViewerTimezone() {
20
return $this->viewerTimezone;
21
}
22
23
public function getEventsBetween(
24
PhutilCalendarDateTime $start = null,
25
PhutilCalendarDateTime $end = null,
26
$limit = null) {
27
28
if ($end === null && $limit === null) {
29
throw new Exception(
30
pht(
31
'Recurring event range queries must have an end date, a limit, or '.
32
'both.'));
33
}
34
35
$timezone = $this->getViewerTimezone();
36
37
$sources = array();
38
foreach ($this->sources as $source) {
39
$source = clone $source;
40
$source->setViewerTimezone($timezone);
41
$source->resetSource();
42
43
$sources[] = array(
44
'source' => $source,
45
'state' => null,
46
'epoch' => null,
47
);
48
}
49
50
if ($start) {
51
$start = clone $start;
52
$start->setViewerTimezone($timezone);
53
$min_epoch = $start->getEpoch();
54
} else {
55
$min_epoch = 0;
56
}
57
58
if ($end) {
59
$end = clone $end;
60
$end->setViewerTimezone($timezone);
61
$end_epoch = $end->getEpoch();
62
} else {
63
$end_epoch = null;
64
}
65
66
$results = array();
67
$index = 0;
68
$cursor = 0;
69
while (true) {
70
// Get the next event for each source which we don't have a future
71
// event for.
72
foreach ($sources as $key => $source) {
73
$state = $source['state'];
74
$epoch = $source['epoch'];
75
76
if ($state !== null && $epoch >= $cursor) {
77
// We have an event for this source, and it's a future event, so
78
// we don't need to do anything.
79
continue;
80
}
81
82
$next = $source['source']->getNextEvent($cursor);
83
if ($next === null) {
84
// This source doesn't have any more events, so we're all done.
85
unset($sources[$key]);
86
continue;
87
}
88
89
$next_epoch = $next->getEpoch();
90
91
if ($end_epoch !== null && $next_epoch > $end_epoch) {
92
// We have an end time and the next event from this source is
93
// past that end, so we know there are no more relevant events
94
// coming from this source.
95
unset($sources[$key]);
96
continue;
97
}
98
99
$sources[$key]['state'] = $next;
100
$sources[$key]['epoch'] = $next_epoch;
101
}
102
103
if (!$sources) {
104
// We've run out of sources which can produce valid events in the
105
// window, so we're all done.
106
break;
107
}
108
109
// Find the minimum event time across all sources.
110
$next_epoch = null;
111
foreach ($sources as $source) {
112
if ($next_epoch === null) {
113
$next_epoch = $source['epoch'];
114
} else {
115
$next_epoch = min($next_epoch, $source['epoch']);
116
}
117
}
118
119
$is_exception = false;
120
$next_source = null;
121
foreach ($sources as $source) {
122
if ($source['epoch'] == $next_epoch) {
123
if ($source['source']->getIsExceptionSource()) {
124
$is_exception = true;
125
} else {
126
$next_source = $source;
127
}
128
}
129
}
130
131
// If this is an exception, it means the event does NOT occur. We
132
// skip it and move on. If it's not an exception, it does occur, so
133
// we record it.
134
if (!$is_exception) {
135
136
// Only actually include this event in the results if it starts after
137
// any specified start time. We increment the index regardless, so we
138
// return results with proper offsets.
139
if ($next_source['epoch'] >= $min_epoch) {
140
$results[$index] = $next_source['state'];
141
}
142
$index++;
143
144
if ($limit !== null && (count($results) >= $limit)) {
145
break;
146
}
147
}
148
149
$cursor = $next_epoch + 1;
150
151
// If we have an end of the window and we've reached it, we're done.
152
if ($end_epoch) {
153
if ($cursor > $end_epoch) {
154
break;
155
}
156
}
157
}
158
159
return $results;
160
}
161
162
}
163
164