ILIAS  release_7 Revision v7.30-3-g800a261c036
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilCalendarRecurrenceCalculator.php
Go to the documentation of this file.
1 <?php
2 /*
3  +-----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +-----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2006 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +-----------------------------------------------------------------------------+
22 */
23 
24 include_once './Services/Calendar/classes/class.ilCalendarRecurrence.php';
25 include_once('./Services/Calendar/classes/class.ilDateList.php');
26 include_once('./Services/Calendar/classes/class.ilTimeZone.php');
27 include_once('./Services/Calendar/classes/class.ilCalendarUtil.php');
28 include_once './Services/Calendar/interfaces/interface.ilCalendarRecurrenceCalculation.php';
29 
42 {
43  protected $timezone = null;
44  protected $log = null;
45 
46  protected $limit_reached = false;
47  protected $valid_dates = null;
48  protected $period_start = null;
49  protected $period_end = null;
50  protected $start = null;
51 
52  protected $event = null;
53  protected $duration = null;
54  protected $recurrence = null;
55 
56  protected $frequence_context = 0;
57 
66  {
67  $this->log = $GLOBALS['DIC']->logger()->cal();
68  $this->event = $entry;
69  $this->recurrence = $rec;
70 
71  $this->duration = $entry->getEnd()->get(IL_CAL_UNIX) - $entry->getStart()->get(IL_CAL_UNIX);
72  }
73 
78  protected function getDuration()
79  {
80  return $this->duration;
81  }
82 
91  public function calculateDateListByMonth($a_month, $a_year)
92  {
93  }
94 
95 
105  public function calculateDateList(ilDateTime $a_start, ilDateTime $a_end, $a_limit = -1)
106  {
107  # echo $a_start;
108  # echo $a_end;
109  # echo $this->event->getStart();
110  # echo $this->event->getEnd();
111 
112 
113  $this->valid_dates = $this->initDateList();
114 
115  // Check invalid settings: e.g no frequence given, invalid start/end dates ...
116  if (!$this->validateRecurrence()) {
117  $this->valid_dates->add($this->event->getStart());
118  return $this->valid_dates;
119  }
120 
121  // Performance fix: Switching the timezone for many dates seems to be
122  // quite time consuming.
123  // Therfore we adjust the timezone of all input dates (start,end, event start)
124  // to the same tz (UTC for fullday events, Recurrence tz for all others).
125  $this->adjustTimeZones($a_start, $a_end);
126 
127 
128 
129  // Add start of event if it is in the period
130  if ((ilDateTime::_after($this->event->getStart(), $this->period_start, IL_CAL_DAY) and
131  ilDateTime::_before($this->event->getStart(), $this->period_end, IL_CAL_DAY)) or
132  ilDateTime::_equals($this->event->getStart(), $this->period_start, IL_CAL_DAY)) {
133  // begin-patch aptar
134  $this->valid_dates->add($this->event->getStart());
135  #$this->valid_dates->add($this->event->getStart());
136  // end patch aptar
137  }
138 
139  // Calculate recurrences based on frequency (e.g. MONTHLY)
140  $time = microtime(true);
141 
142  $start = $this->optimizeStartingTime();
143  $end = $this->optimizeEndingTime();
144 
145  #echo "ZEIT: ADJUST: ".(microtime(true) - $time).'<br>';
146  $counter = 0;
147  do {
148  ++$counter;
149  // initialize context for applied rules
150  // E.g
151  // RRULE:FREQ=YEARLY;BYMONTH=1;BYWEEKNO=1,50 => context for BYWERKNO is monthly because it filters to the weeks in JAN
152  // RRULE:FREQ=YEARLY;BYWEEKNO=1,50 => context for BYWERKNO is yearly because it adds all weeks.
153  $this->frequence_context = $this->recurrence->getFrequenceType();
154 
155  $freq_res = $this->initDateList();
156  $freq_res->add($start);
157 
158  // Fixed sequence of applying rules (RFC 2445 4.3.10)
159  $freq_res = $this->applyBYMONTHRules($freq_res);
160  #echo "BYMONTH: ".$freq_res;
161 
162  $freq_res = $this->applyBYWEEKNORules($freq_res);
163  #echo "BYWEEKNO: ".$freq_res;
164 
165  $freq_res = $this->applyBYYEARDAYRules($freq_res);
166  #echo "BYYEARDAY: ".$freq_res;
167 
168 
169  $freq_res = $this->applyBYMONTHDAYRules($freq_res);
170  #echo "BYMONTHDAY: ".$freq_res;
171 
172  #$time = microtime(true);
173  $freq_res = $this->applyBYDAYRules($freq_res);
174  #echo "ZEIT: ".(microtime(true) - $time);
175  #echo "BYDAY: ".$freq_res;
176 
177 
178  $freq_res = $this->applyBYSETPOSRules($freq_res);
179  #echo "BYSETPOS: ".$freq_res;
180 
181  $freq_res = $this->applyLimits($freq_res);
182  #echo $freq_res;
183 
185 
186  if (ilDateTime::_after($start, $end) || $this->limit_reached) {
187  break;
188  }
189  } while (true);
190 
191  $this->applyExclusionDates();
192 
193  $this->applyDurationPeriod($this->valid_dates, $this->period_start, $this->period_end);
194 
195  $this->valid_dates->sort();
196 
197  // Restore default timezone
199  return $this->valid_dates;
200  }
201 
206  protected function applyDurationPeriod(ilDateList $list, ilDateTime $start, ilDateTime $end)
207  {
208  $list_copy = clone $list;
209  foreach ($list_copy as $start_date) {
210  $end_date = clone $start_date;
211  $end_date->increment(ilDateTime::MINUTE, $this->getDuration() / 60);
212 
213  if (
214  (
215  ilDateTime::_after($start_date, $this->period_end)
216  ) ||
217  (
218  ilDateTime::_before($end_date, $this->period_start)
219  )
220  ) {
221  $this->log->debug('Removed invalid date ' . (string) $start_date . ' <-> ' . (string) $end_date);
222  $list->remove($start_date);
223  }
224  }
225  }
226 
232  protected function adjustTimeZones(ilDateTime $a_start, ilDateTime $a_end)
233  {
234  $this->timezone = $this->event->isFullday() ? ilTimeZone::UTC : $this->recurrence->getTimeZone();
235  ilTimeZone::_setDefaultTimeZone($this->timezone);
236 
237  $this->period_start = clone $a_start;
238  $this->period_end = clone $a_end;
239  $this->start = clone $this->event->getStart();
240 
241  try {
242  if ($this->event->isFullday()) {
243  $this->period_start->switchTimeZone(ilTimeZone::UTC);
244  $this->period_end->switchTimeZone(ilTimeZone::UTC);
245  $this->start->switchTimeZone(ilTimeZone::UTC);
246  } else {
247  $this->period_start->switchTimeZone($this->recurrence->getTimeZone());
248  $this->period_end->switchTimeZone($this->recurrence->getTimeZone());
249  $this->start->switchTimeZone($this->recurrence->getTimeZone());
250  }
251  return true;
252  } catch (ilDateTimeException $e) {
253  $this->log->write(__METHOD__ . ': ' . $e->getMessage());
254  return false;
255  }
256  }
257 
263  protected function optimizeStartingTime()
264  {
265  $time = microtime(true);
266 
267  // starting time cannot be optimzed if RRULE UNTIL is given.
268  // In that case we have to calculate all dates until "UNTIL" is reached.
269  if ($this->recurrence->getFrequenceUntilCount() > 0) {
270  // Switch the date to the original defined timzone for this recurrence
271  return $this->createDate($this->start->get(IL_CAL_UNIX, '', $this->timezone));
272  }
273  $optimized = $start = $this->createDate($this->start->get(IL_CAL_UNIX, '', $this->timezone));
274  while (ilDateTime::_before($start, $this->period_start)) {
275  $optimized = clone $start;
276  $start = $this->incrementByFrequency($start);
277  }
278 
279  return $optimized;
280  }
281 
282  protected function optimizeEndingTime(): ilDateTime
283  {
284  $end = clone $this->period_start;
285  $end = $this->incrementByFrequency($end);
286  if (ilDateTime::_before($this->period_end, $end, ilDateTime::DAY)) {
287  return $end;
288  }
289  return $this->period_end;
290  }
291 
297  protected function incrementByFrequency($start)
298  {
299  global $DIC;
300 
301  $logger = $DIC->logger()->cal();
302 
303  switch ($this->recurrence->getFrequenceType()) {
305  $start->increment(ilDateTime::YEAR, $this->recurrence->getInterval());
306  break;
307 
309  $start->increment(ilDateTime::MONTH, $this->recurrence->getInterval());
310  break;
311 
313  $start->increment(ilDateTime::WEEK, $this->recurrence->getInterval());
314  break;
315 
317  $start->increment(ilDateTime::DAY, $this->recurrence->getInterval());
318  break;
319 
320  default:
321  $logger->warning('No frequence defined.');
322  break;
323  }
324  return $start;
325  }
326 
333  protected function applyBYMONTHRules(ilDateList $list)
334  {
335  // return unmodified, if no bymonth rules are available
336  if (!$this->recurrence->getBYMONTHList()) {
337  return $list;
338  }
339  $month_list = $this->initDateList();
340  foreach ($list->get() as $date) {
341  #echo "SEED: ".$seed;
342 
343  foreach ($this->recurrence->getBYMONTHList() as $month) {
344  #echo "RULW_MONTH:".$month;
345 
346  // YEARLY rules extend the seed to every month given in the BYMONTH rule
347  // Rules < YEARLY must match the month of the seed
348  if ($this->recurrence->getFrequenceType() == ilCalendarRecurrence::FREQ_YEARLY) {
349  $month_date = $this->createDate($date->get(IL_CAL_UNIX, '', $this->timezone));
350  $month_date->increment(ilDateTime::MONTH, -($date->get(IL_CAL_FKT_DATE, 'n', $this->timezone) - $month));
351 
352  #echo "BYMONTH: ".$month_date;
353  $month_list->add($month_date);
354  } elseif ($date->get(IL_CAL_FKT_DATE, 'n', $this->timezone) == $month) {
355  $month_list->add($date);
356  }
357  }
358  }
359  // decrease the frequence_context for YEARLY rules
360  if ($this->recurrence->getFrequenceType() == ilCalendarRecurrence::FREQ_YEARLY) {
361  $this->frequence_context = ilCalendarRecurrence::FREQ_MONTHLY;
362  }
363  return $month_list;
364  }
365 
372  protected function applyBYWEEKNORules(ilDateList $list)
373  {
374  if ($this->recurrence->getFrequenceType() != ilCalendarRecurrence::FREQ_YEARLY) {
375  return $list;
376  }
377  // return unmodified, if no byweekno rules are available
378  if (!$this->recurrence->getBYWEEKNOList()) {
379  return $list;
380  }
381  $weeks_list = $this->initDateList();
382  foreach ($list->get() as $seed) {
383  $weeks_in_year = date('W', mktime(0, 0, 0, 12, 28, $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone)));
384  $this->log->write(__METHOD__ . ': Year ' . $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone) . ' has ' . $weeks_in_year . ' weeks');
385  foreach ($this->recurrence->getBYWEEKNOList() as $week_no) {
386  $week_no = $week_no < 0 ? ($weeks_in_year + $week_no + 1) : $week_no;
387 
388  switch ($this->frequence_context) {
390  $this->log->write(__METHOD__ . ': Handling BYWEEKNO in MONTHLY context');
391  // Check if week matches
392  if ($seed->get(IL_CAL_FKT_DATE, 'W', $this->timezone) == $week_no) {
393  $weeks_list->add($seed);
394  }
395  break;
396 
398  $this->log->write(__METHOD__ . ': Handling BYWEEKNO in YEARLY context');
399  $week_diff = $week_no - $seed->get(IL_CAL_FKT_DATE, 'W', $this->timezone);
400 
401  // TODO: think about tz here
402  $new_week = $this->createDate($seed->get(IL_CAL_UNIX, '', $this->timezone));
403  $new_week->increment(ilDateTime::WEEK, $week_diff);
404  $weeks_list->add($new_week);
405  break;
406  }
407  }
408  }
409  $this->frequence_context = ilCalendarRecurrence::FREQ_WEEKLY;
410 
411  return $weeks_list;
412  }
413 
419  protected function applyBYYEARDAYRules(ilDateList $list)
420  {
421  // return unmodified, if no byweekno rules are available
422  if (!$this->recurrence->getBYYEARDAYList()) {
423  return $list;
424  }
425  $days_list = $this->initDateList();
426  foreach ($list->get() as $seed) {
427  $num_days = date('z', mktime(0, 0, 0, 12, 31, $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone)));
428  $this->log->write(__METHOD__ . ': Year ' . $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone) . ' has ' . $num_days . ' days.');
429 
430  foreach ($this->recurrence->getBYYEARDAYList() as $day_no) {
431  $day_no = $day_no < 0 ? ($num_days + $day_no + 1) : $day_no;
432 
433  $day_diff = $day_no - $seed->get(IL_CAL_FKT_DATE, 'z', $this->timezone);
434  $new_day = $this->createDate($seed->get(IL_CAL_UNIX, '', $this->timezone));
435  $new_day->increment(ilDateTime::DAY, $day_diff);
436 
437  switch ($this->frequence_context) {
439  // Check if day matches
440  if ($seed->get(IL_CAL_FKT_DATE, 'z', $this->timezone) == $day_no) {
441  $days_list->add($new_day);
442  }
443  break;
445  // Check if week matches
446  if ($seed->get(IL_CAL_FKT_DATE, 'W', $this->timezone) == $new_day->get(IL_CAL_FKT_DATE, 'W', $this->timezone)) {
447  $days_list->add($new_day);
448  }
449  break;
451  // Check if month matches
452  if ($seed->get(IL_CAL_FKT_DATE, 'n', $this->timezone) == $new_day->get(IL_CAL_FKT_DATE, 'n', $this->timezone)) {
453  $days_list->add($new_day);
454  }
455  break;
457  // Simply add
458  $days_list->add($new_day);
459  break;
460  }
461  }
462  }
463 
464  $this->frequence_context = ilCalendarRecurrence::FREQ_DAILY;
465  return $days_list;
466  }
467 
473  protected function applyBYMONTHDAYRules(ilDateList $list)
474  {
475  // return unmodified, if no byweekno rules are available
476  if (!$this->recurrence->getBYMONTHDAYList()) {
477  return $list;
478  }
479  $days_list = $this->initDateList();
480  foreach ($list->get() as $seed) {
482  $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone),
483  $seed->get(IL_CAL_FKT_DATE, 'n', $this->timezone)
484  );
485  /*
486  $num_days = cal_days_in_month(CAL_GREGORIAN,
487  $seed->get(IL_CAL_FKT_DATE,'n',$this->timezone),
488  $seed->get(IL_CAL_FKT_DATE,'Y',$this->timezone));
489  */
490  #$this->log->write(__METHOD__.': Month '.$seed->get(IL_CAL_FKT_DATE,'M',$this->timezone).' has '.$num_days.' days.');
491 
492  foreach ($this->recurrence->getBYMONTHDAYList() as $bymonth_no) {
493  $day_no = $bymonth_no < 0 ? ($num_days + $bymonth_no + 1) : $bymonth_no;
494  if ($this->frequence_context != ilCalendarRecurrence::FREQ_YEARLY) {
495  if ($day_no < 1 or $day_no > $num_days) {
496  $this->log->write(__METHOD__ . ': Ignoring BYMONTHDAY rule: ' . $day_no . ' for month ' .
497  $seed->get(IL_CAL_FKT_DATE, 'M', $this->timezone));
498  continue;
499  }
500  }
501  $day_diff = $day_no - $seed->get(IL_CAL_FKT_DATE, 'j', $this->timezone);
502  $new_day = $this->createDate($seed->get(IL_CAL_UNIX, '', $this->timezone));
503  $new_day->increment(ilDateTime::DAY, $day_diff);
504 
505  switch ($this->frequence_context) {
507  // Check if day matches
508  #var_dump("<pre>",$seed->get(IL_CAL_FKT_DATE,'z',$this->timezone),$day_no,"</pre>");
509  if ($seed->get(IL_CAL_FKT_DATE, 'j', $this->timezone) == $day_no) {
510  $days_list->add($new_day);
511  }
512  break;
513 
515  // Check if week matches
516  if ($seed->get(IL_CAL_FKT_DATE, 'W', $this->timezone) == $new_day->get(IL_CAL_FKT_DATE, 'W', $this->timezone)) {
517  $days_list->add($new_day);
518  }
519  break;
520 
522  // seed and new day are in the same month.
523  $days_list->add($new_day);
524  break;
525 
527  $h = $this->event->isFullday() ? 0 : $seed->get(IL_CAL_FKT_DATE, 'H', $this->timezone);
528  $i = $this->event->isFullday() ? 0 : $seed->get(IL_CAL_FKT_DATE, 'i', $this->timezone);
529  $s = $this->event->isFullday() ? 0 : $seed->get(IL_CAL_FKT_DATE, 's', $this->timezone);
530  $y = $seed->get(IL_CAL_FKT_DATE, 'Y', $this->timezone);
531 
532  // TODO: the chosen monthday has to added to all months
533  for ($month = 1;$month <= 12;$month++) {
534  #$num_days = cal_days_in_month(CAL_GREGORIAN,
535  # $month,
536  # $y);
538  $y,
539  $month
540  );
541  $day_no = $bymonth_no < 0 ? ($num_days + $bymonth_no + 1) : $bymonth_no;
542  if ($day_no < 1 or $day_no > $num_days) {
543  $this->log->write(__METHOD__ . ': Ignoring BYMONTHDAY rule: ' . $day_no . ' for month ' . $month);
544  } else {
545  $tz_obj = ilTimeZone::_getInstance($this->timezone);
546  $tz_obj->switchTZ();
547  $unix = mktime($h, $i, $s, $month, $day_no, $y);
548  $tz_obj->restoreTZ();
549  $new_day = $this->createDate($unix);
550  $days_list->add($new_day);
551  }
552  }
553  break;
554  }
555  }
556  }
557  $this->frequence_context = ilCalendarRecurrence::FREQ_DAILY;
558  return $days_list;
559  }
560 
561 
569  protected function applyBYDAYRules(ilDateList $list)
570  {
571  // return unmodified, if no byday rules are available
572  if (!$this->recurrence->getBYDAYList()) {
573  return $list;
574  }
575 
576  $days_list = $this->initDateList();
577 
578  // generate a list of e.g all Sundays for the given year
579  // or e.g a list of all week days in a give month (FREQ = MONTHLY,WEEKLY or DAILY)
580  foreach ($list->get() as $seed) {
581  $seed_info = $seed->get(IL_CAL_FKT_GETDATE);
582 
583  // TODO: maybe not correct in dst cases
584  $date_info = $seed->get(IL_CAL_FKT_GETDATE);
585  $date_info['mday'] = 1;
586  $date_info['mon'] = 1;
587  $start = $this->createDate($date_info, IL_CAL_FKT_GETDATE);
588 
589  switch ($this->frequence_context) {
591  $day_sequence = $this->getYearWeekDays($seed);
592  break;
593 
595  $day_sequence = $this->getMonthWeekDays($seed_info['year'], $seed_info['mon']);
596  break;
597 
599  // TODO or RFC bug: FREQ>WEEKLY;BYMONTH=1;BYDAY=FR returns FR 1.2.2008
600  // Ical says: apply BYMONTH rules and after that apply byday rules on that date list.
601  $day_sequence = $this->getWeekWeekDays($seed_info);
602  break;
603 
605  $day_sequence[strtoupper(substr($seed->get(IL_CAL_FKT_DATE, 'D'), 0, 2))] = array($seed_info['yday']);
606  break;
607 
608  }
609  foreach ($this->recurrence->getBYDAYList() as $byday) {
610  $year_day = array();
611  $day = strtoupper(substr($byday, -2));
612  $num_by_day = (int) $byday;
613 
614  if ($num_by_day) {
615  if ($num_by_day > 0) {
616  if (isset($day_sequence[$day][$num_by_day - 1])) {
617  $year_day = array($day_sequence[$day][$num_by_day - 1]);
618  }
619  } else {
620  if (isset($day_sequence[$day][count($day_sequence[$day]) + $num_by_day])) {
621  $year_day = array($day_sequence[$day][count($day_sequence[$day]) + $num_by_day]);
622  }
623  }
624  } else {
625  if (isset($day_sequence[$day])) {
626  $year_day = $day_sequence[$day];
627  }
628  }
629  foreach ($year_day as $day) {
630  switch ($this->frequence_context) {
635  $tmp_date = clone $start;
636  $tmp_date->increment(IL_CAL_DAY, $day);
637  $days_list->add($tmp_date);
638  break;
639  }
640  }
641  }
642  }
643  #echo $days_list;
644 
645  return $days_list;
646  }
647 
653  protected function getYearWeekDays(ilDateTime $seed)
654  {
655  $time = microtime(true);
656 
657  $year_days = array();
658 
659  $current_year = $seed->get(IL_CAL_FKT_DATE, 'Y');
660  $start = new ilDate($current_year . '-01-01', IL_CAL_DATE);
661  $offset = $start->get(IL_CAL_FKT_DATE, 'w');
662  $days = array(0 => 'SU',1 => 'MO',2 => 'TU',3 => 'WE',4 => 'TH',5 => 'FR',6 => 'SA');
663  for ($i = 0;$i < $offset;$i++) {
664  next($days);
665  }
666 
667  $num_days = ilCalendarUtil::_isLeapYear($current_year) ? 366 : 365;
668  for ($i = 0;$i < $num_days;$i++) {
669  if (($current_day = current($days)) == false) {
670  $current_day = reset($days);
671  }
672  $year_days[$current_day][] = $i;
673  next($days);
674  }
675  return $year_days;
676  }
677 
685  protected function getMonthWeekDays($year, $month)
686  {
687  static $month_days = array();
688 
689  if (isset($month_days[$year][$month])) {
690  return $month_days[$year][$month];
691  }
692 
693  $month_str = $month < 10 ? ('0' . $month) : $month;
694  $begin_month = new ilDate($year . '-' . $month_str . '-01', IL_CAL_DATE);
695  $begin_month_info = $begin_month->get(IL_CAL_FKT_GETDATE);
696 
697  $days = array(0 => 'SU',1 => 'MO',2 => 'TU',3 => 'WE',4 => 'TH',5 => 'FR',6 => 'SA');
698  for ($i = 0;$i < $begin_month_info['wday'];$i++) {
699  next($days);
700  }
701  for ($i = $begin_month_info['yday']; $i < $begin_month_info['yday'] + ilCalendarUtil::_getMaxDayOfMonth($year, $month) ; $i++) {
702  if (($current_day = current($days)) == false) {
703  $current_day = reset($days);
704  }
705  $month_days[$year][$month][$current_day][] = $i;
706  next($days);
707  }
708  return $month_days[$year][$month];
709  }
710 
718  protected function getWeekWeekDays($seed_info)
719  {
720  $days = array(0 => 'SU',1 => 'MO',2 => 'TU',3 => 'WE',4 => 'TH',5 => 'FR',6 => 'SA');
721 
722  $start_day = $seed_info['yday'] - $seed_info['wday'];
723  foreach ($days as $num => $day) {
724  $week_days[$day][] = $start_day++;
725  }
726  return $week_days;
727  }
728 
729 
737  protected function applyBYSETPOSRules(ilDateList $list)
738  {
739  // return unmodified, if no bysetpos rules are available
740  if (!$this->recurrence->getBYSETPOSList()) {
741  return $list;
742  }
743  $pos_list = $this->initDateList();
744  $list->sort();
745  $candidates = $list->get();
746  $candidates_count = count($candidates);
747  foreach ($this->recurrence->getBYSETPOSList() as $position) {
748  if ($position > 0 and $date = $list->getAtPosition($position)) {
749  $pos_list->add($date);
750  }
751  if ($position < 0 and $date = $list->getAtPosition($candidates_count + $position + 1)) {
752  $pos_list->add($date);
753  }
754  }
755  return $pos_list;
756  }
757 
765  protected function applyLimits(ilDateList $list)
766  {
767  $list->sort();
768 
769  #echo "list: ";
770  #echo $list;
771  #echo '<br />';
772 
773  // Check valid dates before starting time
774  foreach ($list->get() as $check_date) {
775  if (ilDateTime::_before($check_date, $this->event->getStart(), IL_CAL_DAY)) {
776  $this->log->debug('Removed invalid date: ' . (string) $check_date . ' before starting date: ' . (string) $this->event->getStart());
777  $list->remove($check_date);
778  }
779  }
780 
781  #echo 'Until date '.$this->recurrence->getFrequenceUntilDate();
782 
783  // Check count if given
784  if ($this->recurrence->getFrequenceUntilCount()) {
785  foreach ($list->get() as $res) {
786  // check smaller than since the start time counts as one
787  if (count($this->valid_dates->get()) < $this->recurrence->getFrequenceUntilCount()) {
788  $this->valid_dates->add($res);
789  } else {
790  $this->limit_reached = true;
791  return false;
792  }
793  }
794  return true;
795  } elseif ($this->recurrence->getFrequenceUntilDate()) {
796  #echo 'Until date '.$this->recurrence->getFrequenceUntilDate();
797  $date = $this->recurrence->getFrequenceUntilDate();
798  foreach ($list->get() as $res) {
799  #echo 'Check date '.$res;
800  if (ilDateTime::_after($res, $date, IL_CAL_DAY)) {
801  #echo 'Limit reached';
802  $this->limit_reached = true;
803  return false;
804  }
805  $this->valid_dates->add($res);
806  }
807  return true;
808  }
809 
810  $this->valid_dates->merge($list);
811  return true;
812  }
813 
819  protected function applyExclusionDates()
820  {
821  if (!$this->recurrence->getExclusionDates()) {
822  return true;
823  }
824  foreach ($this->recurrence->getExclusionDates() as $excl) {
825  $this->valid_dates->removeByDAY($excl->getDate());
826  }
827  }
828 
834  protected function initDateList()
835  {
836  return new ilDateList($this->event->isFullday() ? ilDateList::TYPE_DATE : ilDateList::TYPE_DATETIME);
837  }
838 
844  protected function createDate($a_date, $a_format_type = IL_CAL_UNIX)
845  {
846  if ($this->event->isFullday()) {
847  return new ilDate($a_date, $a_format_type);
848  } else {
849  // TODO: the timezone for this recurrence must be stored in the db
850  return new ilDateTime($a_date, $a_format_type, $this->timezone);
851  }
852  }
853 
860  protected function validateRecurrence()
861  {
862  return $this->recurrence->validate();
863  }
864 }
applyBYMONTHDAYRules(ilDateList $list)
Apply BYMONTHDAY rules.
remove(ilDateTime $remove)
remove from list
applyDurationPeriod(ilDateList $list, ilDateTime $start, ilDateTime $end)
Apply duration period.
applyBYDAYRules(ilDateList $list)
Apply BYDAY rules.
applyBYMONTHRules(ilDateList $list)
Apply BYMONTH rules.
static _after(ilDateTime $start, ilDateTime $end, $a_compare_field='', $a_tz='')
compare two dates and check start is after end This method does not consider tz offsets.
static _isLeapYear($a_year)
check if a given year is a leap year
static _before(ilDateTime $start, ilDateTime $end, $a_compare_field='', $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
switchTimeZone($a_timezone_identifier='')
Switch timezone.
Class for DateTime exceptions.
static _setDefaultTimeZone($a_tz)
set default timezone
sort()
Sort list.
const IL_CAL_UNIX
applyBYYEARDAYRules(ilDateList $list)
Apply BYYEARDAY rules.
static _getMaxDayOfMonth($a_year, $a_month)
get max day of month 2008,2 => 29
static _restoreDefaultTimeZone()
restore default timezone to server timezone
__construct(ilDatePeriod $entry, ilCalendarRecurrenceCalculation $rec)
public
getYearWeekDays(ilDateTime $seed)
get a list of year week days according to the BYMONTH rule
calculateDateList(ilDateTime $a_start, ilDateTime $a_end, $a_limit=-1)
calculate date list
calculateDateListByMonth($a_month, $a_year)
calculate day list by month(s) uses a cache of calculated recurring events public ...
const IL_CAL_DAY
static _equals(ilDateTime $start, ilDateTime $end, $a_compare_field='', $a_tz='')
Check if two date are equal.
adjustTimeZones(ilDateTime $a_start, ilDateTime $a_end)
Adjust timezone.
Class for single dates.
foreach($_POST as $key=> $value) $res
Calculates an ilDateList for a given calendar entry and recurrence rule.
List of dates.
getStart()
Interface method get start.
incrementByFrequency($start)
increment starting time by frequency
global $DIC
Definition: goto.php:24
const IL_CAL_FKT_DATE
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
applyBYSETPOSRules(ilDateList $list)
Apply BYSETPOST rules.
get($a_format, $a_format_str='', $a_tz='')
get formatted date
getAtPosition($a_pos)
get item at specific position
getEnd()
Interface method get end.
createDate($a_date, $a_format_type=IL_CAL_UNIX)
create date
const IL_CAL_FKT_GETDATE
static _getInstance($a_tz='')
get instance by timezone
const IL_CAL_DATE
applyLimits(ilDateList $list)
Apply limits (count or until)
getMonthWeekDays($year, $month)
get a list of month days
applyBYWEEKNORules(ilDateList $list)
Apply BYWEEKNO rules (1 to 53 and -1 to -53).
$i
Definition: metadata.php:24