]> git.datanom.net - webcal.git/blob - caldav/rruleparser.class.php
Initial upload
[webcal.git] / caldav / rruleparser.class.php
1 <?php
2 /* $Id$ */
3
4 require_once 'caldavresource.class.php';
5
6 define ('FREQUENCE', 'HOURLY,DAILY,WEEKLY,MONTHLY,YEARLY');
7
8 /**
9 * Unsupported: SECONDLY, MINUTELY, YEARLY,
10 * BYSECOND, BYMINUTE, BYSETPOS, WKST
11 * Also preceeding where preceeding is allowed is not
12 * supported either.
13 */
14 class RRuleParser {
15
16 private $rules;
17 private $start;
18 private $end;
19 private $range_start;
20 private $range_end;
21 private $freq;
22
23 function __construct($rrule = NULL, $start = NULL, $end = NULL) {
24 $this->freq = preg_split("/[\s,]+/", FREQUENCE);
25 if ($rrule)
26 $this->setRule($rrule, $start, $end);
27 }
28
29 function setRule($rrule, $start, $end) {
30 if (!($start && $end))
31 throw new Exception("Missing values for DTSTART and/or DTEND");
32 //print "$start:$end<br/>";
33 //print var_export($rrule, TRUE) . "<br/>";
34 $this->start = CaldavRessource::iCal2Timestamp($start);
35 $this->end = CaldavRessource::iCal2Timestamp($end);
36 //print CaldavRessource::timestamp2ICal($this->start, TRUE).":".CaldavRessource::timestamp2ICal($this->end, TRUE)."<br/>";
37 $rules = explode(';', $rrule);
38 //print_r($rules);
39 if (count($rules) < 2) {
40 foreach ($rules as $rule) {
41 $pair = explode('=', $rule);
42 if (count($pair) < 2 || !in_array($pair[1], $this->freq)) {
43 $this->rules = array();
44 throw new Exception("$rrule: Invalid RRULE");
45 }
46 $this->rules[strtolower($pair[0])] = explode(',', $pair[1]);
47 }
48 }
49 else {
50 foreach ($rules as $rule) {
51 $pair = explode('=', $rule);
52 $this->rules[strtolower($pair[0])] = explode(',', $pair[1]);
53 }
54 }
55 if ($this->rules['until'][0] && $this->rules['count'][0]) {
56 $this->rules = array();
57 throw new Exception("COUNT and UNTIL cannot be present at the same time");
58 }
59 if (!in_array($this->rules['freq'][0], $this->freq)) {
60 trigger_error(
61 "[{$this->rules['freq'][0]}] Unsupported FREQ",
62 E_USER_NOTICE);
63 $this->rules = array();
64 }
65 }
66
67 private function getEnd($freq) {
68 $count = $this->getCount();
69 $until = $this->getUntil();
70 //print "$until<br/>";
71 if ($count) {
72 $int = $this->getInterval();
73 $count = ($int) ? $int * $count : $count;
74 switch ($freq) {
75 case 'HOURLY': $str = "+$count hour"; break;
76 case 'DAILY': $str = "+$count day"; break;
77 case 'WEEKLY': $str = "+$count week"; break;
78 case 'MONTHLY': $str = "+$count month"; break;
79 case 'YEARLY': $str = "+$count year"; break;
80 }
81 $end = strtotime($str, $this->start);
82 return ($this->range_end && $this->range_end < $end) ?
83 $this->range_end : $end;
84 }
85 else if ($until) {
86 /*
87 * UNTIL has first occurrence at end time and
88 * last ocurrence ending at start time
89 */
90 //print "$until:".$this->start."<br/>";
91 $u_s = explode("T", CaldavRessource::timestamp2ICal($this->end));
92 $time_s = (int) substr($u_s[1], 0, 2);
93 $u_e = explode("T", $until);
94 $time_e = (int) substr($u_e[1], 0, 2);
95 $until = ($time_s != $time_e) ? "{$u_e[0]}T{$u_s[1]}" : $until;
96 //print "$time_s:$time_e:$until<br/>";
97 $end = CaldavRessource::iCal2Timestamp($until);
98 //print CaldavRessource::timestamp2ICal($end)."<br/>";
99 return ($this->range_end && $this->range_end < $end) ?
100 $this->range_end : $end;
101 }
102 else
103 return $this->range_end;
104 }
105
106 private function except($ts) {
107 $byhour = $this->getByHour();
108 if ($byhour) {
109 $res = TRUE;
110 $match = gmdate('G', $ts);
111 foreach ($byhour as $hour) {
112 if ($match == $hour)
113 $res = FALSE;
114 if (! $res)
115 return FALSE;
116 }
117 return TRUE;
118 }
119 $byday = $this->getByDay();
120 if ($byday) {
121 $res = TRUE;
122 $match = substr(strtolower(gmdate('D', $ts)), 0, 2);
123 foreach ($byday as $day) {
124 //print "$match:$day\n";
125 if ($match == strtolower($day))
126 $res = FALSE;
127 if (! $res)
128 return FALSE;
129 }
130 return TRUE;
131 }
132 $bymonth = $this->getByMonth();
133 if ($bymonth) {
134 $res = TRUE;
135 $match = gmdate('n', $ts);
136 foreach ($bymonth as $month) {
137 if ($match == $month)
138 $res = FALSE;
139 if (! $res)
140 return FALSE;
141 }
142 return TRUE;
143 }
144 $bymonthday = $this->getByMonthDay();
145 if ($bymonthday) {
146 $res = TRUE;
147 $match = gmdate('j', $ts);
148 foreach ($bymonthday as $monthday) {
149 if ($match + 1 == $monthday)
150 $res = FALSE;
151 if (! $res)
152 return FALSE;
153 }
154 return TRUE;
155 }
156 $byweekno = $this->getByWeekNo();
157 if ($byweekno) {
158 $res = TRUE;
159 // Missing to handle Anglo week numbers
160 // (week start on Sunday)
161 $match = gmdate('W', $ts);
162 foreach ($byweekno as $weekno) {
163 if ($match == $weekno)
164 $res = FALSE;
165 if (! $res)
166 return FALSE;
167 }
168 return TRUE;
169 }
170 $byyearday = $this->getByYearDay();
171 if ($byyearday) {
172 $res = TRUE;
173 $match = gmdate('z', $ts);
174 foreach ($byyearday as $yearday) {
175 if ($match == $yearday)
176 $res = FALSE;
177 if (! $res)
178 return FALSE;
179 }
180 return TRUE;
181 }
182 return FALSE;
183 }
184
185 private function hourly() {
186 $res = array();
187 $end = $this->getEnd('HOURLY');
188 if (! $end) {
189 /**
190 * we will maximum handle one month at a time unless
191 * a specific end date specifies otherwise
192 */
193 $end = strtotime('+1 month', $this->start);
194 }
195 $int = ($this->getInterval()) ? $this->getInterval() : 1;
196 $c = $this->start;
197 for (; $c < $end; $c = strtotime("+$int hour", $c)) {
198 if (! $this->except($c))
199 array_push($res, CaldavRessource::timestamp2ICal($c));
200 }
201 return $res;
202 }
203
204 private function daily() {
205 $res = array();
206 $end = $this->getEnd('DAILY');
207 if (! $end) {
208 /**
209 * we will maximum handle one month at a time unless
210 * a specific end date specifies otherwise
211 */
212 $end = strtotime('+1 month', $this->start);
213 }
214 $int = ($this->getInterval()) ? $this->getInterval() : 1;
215 $c = $this->start;
216 for (; $c < $end; $c = strtotime("+$int day", $c)) {
217 if (! $this->except($c))
218 array_push($res, CaldavRessource::timestamp2ICal($c));
219 }
220 return $res;
221 }
222
223 private function weekly() {
224 $res = array();
225 $end = $this->getEnd('WEEKLY');
226 //print "start: ".CaldavRessource::timestamp2ICal($this->start)." end: ".CaldavRessource::timestamp2ICal($end)."\n";
227 if (! $end) {
228 /**
229 * we will maximum handle 12 weeks at a time unless
230 * a specific end date specifies otherwise
231 */
232 $end = strtotime('+12 week', $this->start);
233 }
234 $int = ($this->getInterval()) ? $this->getInterval() : 1;
235 $c = $this->start;
236 for (; $c < $end; $c = strtotime("+$int week", $c)) {
237 //print CaldavRessource::timestamp2ICal($c)."<br/>";
238 if (! $this->except($c))
239 array_push($res, CaldavRessource::timestamp2ICal($c));
240 }
241 //print_r($res);
242 return $res;
243 }
244
245 private function monthly() {
246 $res = array();
247 $end = $this->getEnd('MONTHLY');
248 if (! $end) {
249 /**
250 * we will maximum handle 12 months at a time unless
251 * a specific end date specifies otherwise
252 */
253 $end = strtotime('+12 month', $this->start);
254 }
255 $int = ($this->getInterval()) ? $this->getInterval() : 1;
256 $c = $this->start;
257 for (; $c < $end; $c = strtotime("+$int month", $c)) {
258 if (! $this->except($c))
259 array_push($res, CaldavRessource::timestamp2ICal($c));
260 }
261 return $res;
262 }
263
264 private function yearly() {
265 $res = array();
266 $end = $this->getEnd('YEARLY');
267 if (! $end) {
268 /**
269 * we will maximum handle 12 years at a time unless
270 * a specific end date specifies otherwise
271 */
272 $end = strtotime('+12 year', $this->start);
273 }
274 $int = ($this->getInterval()) ? $this->getInterval() : 1;
275 $c = $this->start;
276 for (; $c < $end; $c = strtotime("+$int year", $c)) {
277 if (! $this->except($c))
278 array_push($res, CaldavRessource::timestamp2ICal($c));
279 }
280 return $res;
281 }
282
283 private function limitRange($dates) {
284 $res = array();
285 if (!$this->range_start && !$this->range_end) {
286 $res = $dates;
287 }
288 else if ($this->range_start && !$this->range_end) {
289 $start = CaldavRessource::timestamp2ICal($this->range_start);
290 foreach ($dates as $date) {
291 if (CaldavRessource::datecmp($start, $date) < 0)
292 array_push($res, $date);
293 }
294 }
295 else {
296 $start = CaldavRessource::timestamp2ICal($this->range_start);
297 $end = CaldavRessource::timestamp2ICal($this->range_end);
298 foreach ($dates as $date) {
299 if (CaldavRessource::datecmp($start, $date) < 0 &&
300 CaldavRessource::datecmp($end, $date) > 0)
301 array_push($res, $date);
302 }
303 }
304 return $res;
305 }
306
307 function getFreq() {
308 return strtoupper($this->rules['freq'][0]);
309 }
310
311 function getUntil() {
312 return strtoupper($this->rules['until'][0]);
313 }
314
315 function getCount() {
316 return strtoupper($this->rules['count'][0]);
317 }
318
319 function getInterval() {
320 return strtoupper($this->rules['interval'][0]);
321 }
322
323 function getBySecond() {
324 $l = $this->rules['bysecond'];
325 if ($l) {
326 foreach ($l as $val)
327 $list[] = strtoupper($val);
328 }
329 return $list;
330 }
331
332 function getByMinute() {
333 $l = $this->rules['byminute'];
334 if ($l) {
335 foreach ($l as $val)
336 $list[] = strtoupper($val);
337 }
338 return $list;
339 }
340
341 function getByHour() {
342 $l = $this->rules['byhour'];
343 if ($l) {
344 foreach ($l as $val)
345 $list[] = strtoupper($val);
346 }
347 return $list;
348 }
349
350 function getByDay() {
351 $l = $this->rules['byday'];
352 if ($l) {
353 foreach ($l as $val)
354 $list[] = strtoupper($val);
355 }
356 return $list;
357 }
358
359 function getByMonthDay() {
360 $l = $this->rules['bymonthday'];
361 if ($l) {
362 foreach ($l as $val)
363 $list[] = strtoupper($val);
364 }
365 return $list;
366 }
367
368 function getByYearDay() {
369 $l = $this->rules['byyearday'];
370 if ($l) {
371 foreach ($l as $val)
372 $list[] = strtoupper($val);
373 }
374 return $list;
375 }
376
377 function getByWeekNo() {
378 $l = $this->rules['byweekno'];
379 if ($l) {
380 foreach ($l as $val)
381 $list[] = strtoupper($val);
382 }
383 return $list;
384 }
385
386 function getByMonth() {
387 $l = $this->rules['bymonth'];
388 if ($l) {
389 foreach ($l as $val)
390 $list[] = strtoupper($val);
391 }
392 return $list;
393 }
394
395 function getBySetPos() {
396 return strtoupper($this->rules['bysetpos'][0]);
397 }
398
399 function getWKST() {
400 return strtoupper($this->rules['wkst'][0]);
401 }
402
403 function getAll() {
404 return $this->rules;
405 }
406
407 function getEventDates($startDate = NULL, $endDate = NULL) {
408 $dates = array();
409
410 $freq = $this->getFreq();
411 //print "$freq\n";
412 if (! in_array($freq, $this->freq))
413 return $dates;
414 if ($startDate && $endDate) {
415 $this->range_start = CaldavRessource::iCal2Timestamp($startDate);
416 $this->range_end = CaldavRessource::iCal2Timestamp($endDate);
417 if ($this->start > $this->range_end)
418 return $dates;
419 }
420 else if ($startDate) {
421 $this->range_start = CaldavRessource::iCal2Timestamp($startDate);
422 $this->range_end = NULL;
423 }
424 else {
425 $this->range_start = NULL;
426 $this->range_end = NULL;
427 }
428 switch ($freq) {
429 case 'HOURLY': $dates = $this->hourly(); break;
430 case 'DAILY': $dates = $this->daily(); break;
431 case 'WEEKLY': $dates = $this->weekly(); break;
432 case 'MONTHLY': $dates = $this->monthly(); break;
433 case 'YEARLY': $dates = $this->yearly(); break;
434 default: break;
435 }
436 //print_r($dates);
437 return (count($dates) > 0) ? $this->limitRange($dates) : $dates;
438 }
439
440 function getStartAndEnd() {
441 $res['start'] = $this->start;
442 $res['end'] = $this->end;
443
444 return $res;
445 }
446
447 function __toString() {
448 $str = "FREQ=" . $this->getFreq();
449 $until = $this->getUntil();
450 $count = $this->getCount();
451 if ($until)
452 $str .= ';UNTIL=' . $until;
453 if ($count)
454 $str .= ';COUNT=' . $count;
455 foreach ($this->rules as $k => $v) {
456 if ($k == 'freq' || $k == 'count' || $k == 'until')
457 continue;
458 $str .= ';' . strtoupper($k) . '=';
459 foreach($v as $rule) {
460 if ($str[strlen($str) - 1] != '=')
461 $str .= ',';
462 $str .= strtoupper($rule);
463 }
464 }
465 return $str;
466 }
467
468 }
This page took 0.129784 seconds and 6 git commands to generate.