PluginProbe
Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar / 2.0
Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar v2.0
2.1.21 2.1.20 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 trunk 1.1 2.0 2.0.1 2.0.10.2 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.18 2.0.2 2.0.20 2.0.21 2.0.22 All 55 releases
booking-manager / assets / libs / icalendar / includes / recurringdate.php

recurringdate.php in Booking Manager – Sync WP Booking Calendar – Import Events, Export Bookings to ICS Calendar 2.0, at assets/libs/icalendar/includes/recurringdate.php

597 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Zap Calendar Recurring Date Helper Class
5 *
6 * @copyright Copyright (C) 2006 - 2016 by Dan Cogliano
7 * @license GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10 // no direct access
11 defined('_ZAPCAL') or die( 'Restricted access' );
12
13 /**
14 * Class to expand recurring rule to a list of dates
15 */
16 class ZCRecurringDate {
17 var $rules = "";
18 var $startdate = null;
19 var $nextdate = null;
20 var $freq = null;
21 var $tzid = null;
22 var $year=0;
23 var $month=0;
24 var $week=0;
25 var $day=0;
26 var $hour=0;
27 var $minute=0;
28 var $second=0;
29 var $until=null;
30 var $count=0;
31 var $repeatmode=null;
32 var $incr=0;
33 var $bysecond=array();
34 var $byminute=array();
35 var $byhour=array();
36 var $byday=array();
37 var $bymonthday=array();
38 var $daybegin=array();
39 var $dayend=array();
40 var $bymonth=array();
41 var $byyear=array();
42 var $bysetpos=array();
43 var $ibymonthday = 0;
44 var $ibymonth = 0;
45 var $interval = 1;
46 var $debug = 0;
47 var $error;
48 var $exdates=array();
49
50 // $startdate is in local timezone
51 // $exdates array is in UTC timezone
52 function ZCRecurringDate($rules, $startdate, $exdates = array(),$tzid = "UTC"){
53 if(strlen($rules) > 0){
54 //move exdates to event timezone for comparing with event date
55 for($i = 0; $i < count($exdates); $i++)
56 {
57 $exdates[$i] = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($exdates[$i]),$tzid));
58 }
59
60 $rules=str_replace("\'","",$rules);
61 $this->rules = $rules;
62 if($startdate == null){
63 // if not specified, use start date of beginning of last year
64 $tdate=getdate();
65 $startdate=mktime(0,0,0,1,1,$tdate["year"] - 1);
66 }
67 $this->startdate = $startdate;
68 $this->nextdate = $startdate;
69 $this->tzid = $tzid;
70 $this->exdates = $exdates;
71
72 $rules=explode(";", $rules);
73 $ruletype = "";
74 foreach($rules as $rule){
75 $item=explode("=",$rule);
76 //echo $item[0] . "=" . $item[1] . "<br/>\n";
77 switch($item[0]){
78 case "FREQ":
79 switch($item[1]){
80 case "YEARLY":
81 $this->year = 1;
82 $this->freq="y";
83 break;
84 case "MONTHLY":
85 $this->month = 1;
86 $this->freq="m";
87 break;
88 case "WEEKLY":
89 $this->week = 1;
90 $this->freq="w";
91 break;
92 case "DAILY":
93 $this->day = 1;
94 $this->freq="d";
95 break;
96 case "HOURLY":
97 $this->hour = 1;
98 $this->freq="h";
99 break;
100 case "MINUTELY":
101 $this->minute = 1;
102 $this->freq="i";
103 break;
104 case "SECONDLY":
105 $this->second = 1;
106 $this->freq="s";
107 break;
108 }
109 break;
110 case "INTERVAL":
111 $this->interval = $item[1];
112 break;
113 case "BYSECOND":
114 $this->bysecond = explode(",",$item[1]);
115 $ruletype = $item[0];
116 break;
117 case "BYMINUTE":
118 $this->byminute = explode(",",$item[1]);
119 $ruletype = $item[0];
120 break;
121 case "BYHOUR":
122 $this->byhour = explode(",",$item[1]);
123 $ruletype = $item[0];
124 break;
125 case "BYDAY":
126 $this->byday = explode(",",$item[1]);
127 $ruletype = $item[0];
128 break;
129 case "BYMONTHDAY":
130 $this->bymonthday = explode(",",$item[1]);
131 $ruletype = $item[0];
132 break;
133 case "BYMONTH":
134 $this->bymonth = explode(",",$item[1]);
135 $ruletype = $item[0];
136 break;
137 case "BYYEAR":
138 $this->byyear = explode(",",$item[1]);
139 $ruletype = $item[0];
140 break;
141 case "COUNT":
142 $this->count = intval($item[1]);
143 $this->repeatmode = "c";
144 break;
145 case "BYSETPOS":
146 $this->bysetpos = explode(",",$item[1]);
147 break;
148 case "UNTIL":
149 $this->until = ZDateHelper::fromiCaltoUnixDateTime($item[1]);
150 $this->repeatmode = "u";
151 break;
152 }
153 }
154 if(count($this->bysetpos) > 0){
155 switch($ruletype){
156 case "BYYEAR":
157 $this->byyear = $this->bySetPos($this->byyear,$this->bysetpos);
158 break;
159 case "BYMONTH":
160 $this->bymonth = $this->bySetPos($this->bymonth,$this->bysetpos);
161 break;
162 case "BYMONTHDAY":
163 $this->bymonthday = $this->bySetPos($this->bymonthday,$this->bysetpos);
164 break;
165 case "BYDAY":
166 $this->byday = $this->bySetPos($this->byday,$this->bysetpos);
167 break;
168 case "BYHOUR":
169 $this->byhour = $this->bySetPos($this->byhour,$this->bysetpos);
170 break;
171 case "BYMINUTE":
172 $this->byminute = $this->bySetPos($this->byminute,$this->bysetpos);
173 break;
174 case "BYSECOND":
175 $this->bysecond = $this->bySetPos($this->bysecond,$this->bysetpos);
176 break;
177 }
178 }
179 if(is_array($this->byday)){
180 $dow = array("SU","MO","TU","WE","TH","FR","SA");
181 for($w=0; $w<=5; $w++){
182 for($d=0; $d < 7; $d++){
183 $this->daybegin[$d][$w] = 0;
184 $this->dayend[$d][$w] = 0;
185 if(in_array($dow[$d],$this->byday)||
186 in_array(($w+1) . $dow[$d], $this->byday)||
187 in_array(($w+1) . $dow[$d], $this->byday))
188 $this->daybegin[$d][$w] = 1;
189 if(in_array("-" . ($w+1) . $dow[$d], $this->byday)){
190 $this->dayend[$d][$w] = 1;
191 }
192 // echo $this->daybegin[$d][$w] . " ";
193 }
194 // echo "<br />\n";
195 }
196 }
197
198 }
199 }
200
201 function bySetPos($bytype, $bysetpos){
202 $result = array();
203 for($i=0; $i < count($bysetpos); $i++){
204 for($j=0; $j < count($bytype); $j++){
205 $result[] = $bysetpos[$i] . $bytype[$j];
206 }
207 }
208 return $result;
209 }
210
211 function error($msg){
212 $this->error = $msg;
213 echo "ZCRecurringDate() error:" . $this->error . "<br />\n";
214 }
215
216 function debug($level, $msg){
217 if($this->debug >= $level)
218 echo $msg . "<br/>\n";
219 }
220
221 function getError(){
222 return $this->error;
223 }
224
225 function byYear($startdate, $enddate, &$rdates, $tzid="UTC"){
226 self::debug(1,"byYear(" . ZDateHelper::toSqlDateTime($startdate) . ","
227 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
228 $count = 0;
229 if(count($this->byyear) > 0){
230 foreach($this->byyear as $year){
231 $t = getdate($startdate);
232 $wdate = mktime($t[hours],$t[minutes],$t[seconds],$t[month],$t[mday],$year);
233 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
234 $count = $this->byMonth($wdate, $enddate, $rdates, $tzid);
235 if($count == 0) {
236 $rdates[] = $wdate;
237 $count++;
238 }
239 }
240 }
241 }
242 else if(!$this->maxDates($rdates))
243 $count = $this->byMonth($startdate, $enddate, $rdates, $tzid);
244 self::debug(1,"byYear() returned " . $count );
245 return $count;
246 }
247
248 function byMonth($startdate, $enddate, &$rdates, $tzid="UTC"){
249 self::debug(1,"byMonth(" . ZDateHelper::toSqlDateTime($startdate) . ","
250 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
251 $count = 0;
252 if(count($this->bymonth) > 0){
253 foreach($this->bymonth as $month){
254 $t = getdate($startdate);
255 $wdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$month,$t["mday"],$t["year"]);
256 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
257 $count = $this->byMonthDay($wdate, $enddate, $rdates, $tzid);
258 if($count == 0) {
259 $rdates[] = $wdate;
260 $count++;
261 }
262 }
263 }
264 }
265 else if(!$this->maxDates($rdates))
266 $count = $this->byMonthDay($startdate, $enddate, $rdates, $tzid);
267 self::debug(1,"byMonth() returned " . $count );
268 return $count;
269 }
270
271 function byMonthDay($startdate, $enddate, &$rdates, $tzid="UTC"){
272 self::debug(1,"byMonthDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
273 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
274 $count = 0;
275 self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
276 if(count($this->bymonthday) > 0){
277 foreach($this->bymonthday as $day){
278 $day = intval($day);
279 $t = getdate($startdate);
280 $wdate = mktime($t['hours'],$t['minutes'],$t['seconds'],$t['mon'],$day,$t['year']);
281 self::debug(2,"mktime(" . $t['hours'] . ", " . $t['minutes']
282 . ", " . $t['mon'] . ", " . $day . ", " . $t['year'] . ") returned $wdate");
283 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
284 $count = $this->byDay($wdate, $enddate, $rdates, $tzid);
285 if($count == 0) {
286 $rdates[] = $wdate;
287 $count++;
288 }
289 }
290 }
291 }
292 else if(!$this->maxDates($rdates)) {
293 self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
294 $count = $this->byDay($startdate, $enddate, $rdates, $tzid);
295 }
296 self::debug(1,"byMonthDay() returned " . $count );
297 return $count;
298 }
299
300 function byDay($startdate, $enddate, &$rdates, $tzid="UTC"){
301 self::debug(1,"byDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
302 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
303 $days = array(
304 "SU" => 0,
305 "MO" => 1,
306 "TU" => 2,
307 "WE" => 3,
308 "TH" => 4,
309 "FR" => 5,
310 "SA" => 6);
311 $idays = array(
312 0 => "SU",
313 1 => "MO",
314 2 => "TU",
315 3 => "WE",
316 4 => "TH",
317 5 => "FR",
318 6 => "SA");
319
320 $count = 0;
321 if(count($this->byday) > 0){
322 if(empty($this->byday[0]))
323 {
324 $this->byday[0] = $idays[date("w",$startdate)];
325 }
326 foreach($this->byday as $tday){
327 $t = getdate($startdate);
328 $day = substr($tday,strlen($tday) - 2);
329 if(strlen($day) < 2)
330 {
331 // missing start day, use current date for DOW
332 $day = $idays[date("w",$startdate)];
333 }
334 if(strlen($tday) > 2) {
335 $imin = 1;
336 $imax = 5; // max # of occurances in a month
337 if(strlen($tday) > 2)
338 $imin = $imax = substr($tday,0,strlen($tday) - 2);
339 self::debug(2,"imin: $imin, imax: $imax, tday: $tday, day: $day, daynum: {$days[$day]}");
340 for($i = $imin; $i <= $imax; $i++){
341 $wdate = ZDateHelper::getDateFromDay($startdate,$i-1,$days[$day],$tzid);
342 self::debug(2,"getDateFromDay(" . ZDateHelper::toSqlDateTime($startdate)
343 . ",$i,{$days[$day]}) returned " . ZDateHelper::toSqlDateTime($wdate));
344 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
345 $count = $this->byHour($wdate, $enddate, $rdates);
346 if($count == 0){
347 $rdates[] = $wdate;
348 $count++;
349 //break;
350 }
351 }
352 }
353 }
354 else {
355 // day of week version
356 $startdate_dow = date("w",$startdate);
357 $datedelta = $days[$day] - $startdate_dow;
358 self::debug(2, "start_dow: $startdate_dow, datedelta: $datedelta");
359 if($datedelta >= 0)
360 {
361 $wdate = ZDateHelper::addDate($startdate,0,0,0,0,$datedelta,0,$this->tzid);
362 self::debug(2, "wdate: " . ZDateHelper::toSqlDateTime($wdate));
363 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
364 $count = $this->byHour($wdate, $enddate, $rdates);
365 if($count == 0){
366 $rdates[] = $wdate;
367 $count++;
368 self::debug(2,"adding date " . ZDateHelper::toSqlDateTime($wdate) );
369 }
370 }
371 }
372 }
373 }
374 }
375 else if(!$this->maxDates($rdates))
376 $count = $this->byHour($startdate, $enddate, $rdates);
377 self::debug(1,"byDay() returned " . $count );
378 return $count;
379 }
380
381 function byHour($startdate, $enddate, &$rdates, $tzid="UTC"){
382 self::debug(1,"byHour(" . ZDateHelper::toSqlDateTime($startdate) . ","
383 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
384 $count = 0;
385 if(count($this->byhour) > 0){
386 foreach($this->byhour as $hour){
387 $t = getdate($startdate);
388 $wdate = mktime($hour,$t["minutes"],$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
389 self::debug(2,"checking date/time " . ZDateHelper::toSqlDateTime($wdate));
390 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
391 $count = $this->byMinute($wdate, $enddate, $rdates);
392 if($count == 0) {
393 $rdates[] = $wdate;
394 $count++;
395 }
396 }
397 }
398 }
399 else if(!$this->maxDates($rdates))
400 $count = $this->byMinute($startdate, $enddate, $rdates);
401 self::debug(1,"byHour() returned " . $count );
402 return $count;
403 }
404
405 function byMinute($startdate, $enddate, &$rdates, $tzid="UTC"){
406 self::debug(1,"byMinute(" . ZDateHelper::toSqlDateTime($startdate) . ","
407 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
408 $count = 0;
409 if(count($this->byminute) > 0){
410 foreach($this->byminute as $minute){
411 $t = getdate($startdate);
412 $wdate = mktime($t["hours"],$minute,$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
413 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
414 $count = $this->bySecond($wdate, $enddate, $rdates);
415 if($count == 0) {
416 $rdates[] = $wdate;
417 $count++;
418 }
419 }
420 }
421 }
422 else if(!$this->maxDates($rdates))
423 $count = $this->bySecond($startdate, $enddate, $rdates);
424 self::debug(1,"byMinute() returned " . $count );
425 return $count;
426 }
427
428 function bySecond($startdate, $enddate, &$rdates, $tzid="UTC"){
429 self::debug(1,"bySecond(" . ZDateHelper::toSqlDateTime($startdate) . ","
430 . ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
431 $count = 0;
432 if(count($this->bysecond) > 0){
433 foreach($this->bysecond as $second){
434 $t = getdate($startdate);
435 $wdate = mktime($t["hours"],$t["minutes"],$second,$t["mon"],$t["mday"],$t["year"]);
436 if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
437 $rdates[] = $wdate;
438 $count++;
439 }
440 }
441 }
442 self::debug(1,"bySecond() returned " . $count );
443 return $count;
444 }
445
446 function maxDates($rdates){
447 if($this->repeatmode == "c" && count($rdates) >= $this->count)
448 return true; // exceeded count
449 else if(count($rdates) > 0 && $this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
450 return true; //past date
451 }
452 return false;
453 }
454
455 function getDates($maxdate = null){
456 //$this->debug = 2;
457 self::debug(1,"getDates()");
458 $nextdate = $enddate = $this->startdate;
459 $rdates = array();
460 $done = false;
461 $eventcount = 0;
462 $loopcount = 0;
463 self::debug(2,"freq: " . $this->freq . ", interval: " . $this->interval);
464 while(!$done){
465 self::debug(1,"<b>*** Frequency ({$this->freq}) loop pass $loopcount ***</b>");
466 switch($this->freq){
467 case "y":
468 if($eventcount > 0)
469 {
470 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,0,$this->interval,$this->tzid);
471 self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
472 if(!empty($this->byday)){
473 $t = getdate($nextdate);
474 $nextdate = gmmktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
475 }
476 self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
477 }
478 $enddate=ZDateHelper::addDate($nextdate,0,0,0,0,0,1);
479 break;
480 case "m":
481 if($eventcount > 0)
482 {
483
484 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
485 self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
486 }
487 if(count($this->byday) > 0)
488 {
489 $t = getdate($nextdate);
490 if($t["mday"] > 28)
491 {
492 //check for short months when using month by day, make sure we do not overshoot the counter and skip a month
493 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
494 $t2 = getdate($nextdate);
495 if($t2["mday"] < $t["mday"])
496 {
497 // oops, skipped a month, backup to previous month
498 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$t2["mday"] - $t["mday"],0,$this->tzid);
499 }
500 }
501 $t = getdate($nextdate);
502 $nextdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
503 }
504 self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
505 $enddate=ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0);
506 break;
507 case "w":
508 if($eventcount == 0)
509 $nextdate=$nextdate;
510 else {
511 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0,$this->tzid);
512 if(count($this->byday) > 0){
513 $dow = date("w", $nextdate);
514 // move to beginning of week (Sunday)
515 $bow = 0;
516 $diff = $bow - $dow;
517 if($diff > 0)
518 $diff = $diff - 7;
519 $nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$diff,0);
520 }
521 self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
522 }
523 $enddate=ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0);
524 break;
525 case "d":
526 $nextdate=($eventcount==0?$nextdate:
527 ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval,0,$this->tzid));
528 $enddate=ZDateHelper::addDate($nextdate,0,0,0,0,1,0);
529 break;
530 }
531
532 $count = $this->byYear($nextdate,$enddate,$rdates,$this->tzid);
533 $eventcount += $count;
534 if($maxdate > 0 && $maxdate < $nextdate)
535 {
536 array_pop($rdates);
537 $done = true;
538 }
539 else if($count == 0 && !$this->maxDates($rdates)){
540 $rdates[] = $nextdate;
541 $eventcount++;
542 }
543 if($this->maxDates($rdates))
544 $done = true;
545
546 $year = date("Y", $nextdate);
547 if($year > _ZAPCAL_MAXYEAR)
548 {
549 $done = true;
550 }
551 $loopcount++;
552 if($loopcount > _ZAPCAL_MAXYEAR){
553 $done = true;
554 throw new Exception("Infinite loop detected in getDates()");
555 }
556 }
557 if($this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
558 // erase last item
559 array_pop($rdates);
560 }
561 $count1 = count($rdates);
562 $rdates = array_unique($rdates);
563 $count2 = count($rdates);
564 $dups = $count1 - $count2;
565 $excount = 0;
566
567 foreach($this->exdates as $exdate)
568 {
569 if($pos = array_search($exdate,$rdates))
570 {
571 array_splice($rdates,$pos,1);
572 $excount++;
573 }
574 }
575 self::debug(1,"getDates() returned " . count($rdates) . " dates, removing $dups duplicates, $excount exceptions");
576
577
578 if($this->debug >= 2)
579 {
580 self::debug(2,"Recurring Dates:");
581 foreach($rdates as $rdate)
582 {
583 $d = getdate($rdate);
584 self::debug(2,ZDateHelper::toSQLDateTime($rdate) . " " . $d["wday"] );
585 }
586 self::debug(2,"Exception Dates:");
587 foreach($this->exdates as $exdate)
588 {
589 self::debug(2, ZDateHelper::toSQLDateTime($exdate));
590 }
591 //exit;
592 }
593
594 return $rdates;
595 }
596 }
597