/**
  * @param $EventId int Id of event.
  * @return array
  *	- rdates
  *	- rrules
  *	- exdates
  *	- exrules
  * @todo Optional date limits
  */
 function SelectRecurByEvent($EventId)
 {
     static $date_fields = array('event_date_start			AS start', 'event_date_time_associated	AS time_associated', 'event_date_duration		AS duration', 'event_date_exclude			AS exclude');
     // Get dates
     $this->db->select(implode(',', $date_fields));
     $this->db->from('event_dates');
     $this->db->where(array('event_date_event_id' => $EventId));
     /// @todo Limit by time using range of selection (min <= date <= max)
     $dates_query = $this->db->get();
     $dates = $dates_query->result_array();
     $include_ranges = array();
     $exclude_ranges = array();
     foreach ($dates as $date) {
         $start_parts = explode(' ', $date['start']);
         $start_date = str_replace('-', '', $start_parts[0]);
         // YYYYMMDD
         if ($date['time_associated']) {
             $start_time = str_replace(':', '', $start_parts[1]);
             // HHMMSS
         } else {
             $start_time = NULL;
         }
         if (NULL === $date['duration']) {
             $duration = NULL;
         } else {
             $duration = (int) $date['duration'];
         }
         if (!$date['exclude']) {
             // include
             if (!array_key_exists($start_date, $include_ranges)) {
                 $include_ranges[$start_date] = array();
             }
             $include_ranges[$start_date][$start_time] = $duration;
         } else {
             // exclude (never a range)
             if (!array_key_exists($start_date, $exclude_ranges)) {
                 $exclude_ranges[$start_date] = array();
             }
             $exclude_ranges[$start_date][$start_time] = NULL;
         }
     }
     static $rule_fields = array('event_recur_rule_id			AS rule_id', 'event_recur_rule_event_id		AS event_id', 'event_recur_rule_exclude		AS exclude', 'event_recur_rule_frequency		AS frequency', 'UNIX_TIMESTAMP(event_recur_rule_until)	AS until', 'event_recur_rule_count			AS count', 'event_recur_rule_interval		AS recur_interval', 'event_recur_rule_week_start	AS week_start', 'event_recur_rule_by_by			AS by_by', 'event_recur_rule_by_primary	AS by_primary', 'event_recur_rule_by_secondary	AS by_secondary');
     /// @todo Limit by time using range of selection (until > min)
     // Get recurrence rules
     $this->db->select(implode(',', $rule_fields));
     $this->db->from('event_recur_rules');
     $this->db->join('event_recur_rule_by', 'event_recur_rule_by_event_recur_rule_id = event_recur_rule_id', 'left');
     $this->db->where(array('event_recur_rule_event_id' => $EventId));
     $rules_query = $this->db->get();
     // Turn each rule into an object
     $rrules = array();
     $exrules = array();
     $results = $rules_query->result_array();
     foreach ($results as $rule_data) {
         $category_name = $rule_data['exclude'] ? 'exrules' : 'rrules';
         // If not already created, do so now
         $recur_id = (int) $rule_data['rule_id'];
         if (!array_key_exists($recur_id, ${$category_name})) {
             $rule = new CalendarRecurRule();
             $rule->SetFrequency($rule_data['frequency']);
             if (NULL !== $rule_data['until']) {
                 $rule->SetUntil((int) $rule_data['until']);
             } elseif (NULL !== $rule_data['count']) {
                 $rule->SetCount((int) $rule_data['count']);
             }
             if (NULL !== $rule_data['recur_interval']) {
                 $rule->SetInterval((int) $rule_data['recur_interval']);
             }
             if (NULL !== $rule_data['week_start']) {
                 $rule->SetWkSt(CalendarRecurRule::$sWeekdays[strtoupper($rule_data['week_start'])]);
             }
             if ($rule_data['exclude']) {
                 $exrules[$recur_id] = $rule;
             } else {
                 $rrules[$recur_id] = $rule;
             }
         }
         // If by data included, add it
         if (NULL !== $rule_data['by_by']) {
             $primary = (int) $rule_data['by_primary'];
             $optional = $rule_data['by_secondary'];
             if (NULL !== $optional) {
                 if ($optional != 0) {
                     $optional = (int) $optional;
                 } else {
                     $optional = NULL;
                 }
             }
             if ($rule_data['exclude']) {
                 $rule =& $exrules[$recur_id];
             } else {
                 $rule =& $rrules[$recur_id];
             }
             switch ($rule_data['by_by']) {
                 case 'second':
                     $rule->SetBySecond($primary);
                     break;
                 case 'minute':
                     $rule->SetByMinute($primary);
                     break;
                 case 'hour':
                     $rule->SetByHour($primary);
                     break;
                 case 'day':
                     $rule->SetByDay($primary, $optional);
                     break;
                 case 'monthday':
                     $rule->SetByMonthDay($primary);
                     break;
                 case 'yearday':
                     $rule->SetByYearDay($primary);
                     break;
                 case 'weekno':
                     $rule->SetByWeekNo($primary);
                     break;
                 case 'month':
                     $rule->SetByMonth($primary);
                     break;
                 case 'setpos':
                     $rule->SetBySetPos($primary);
                     break;
                 case 'term':
                     $rule->SetByTerm($primary);
                     break;
                 case 'termday':
                     $rule->SetByTermDay($primary);
                     break;
                 case 'termweek':
                     $rule->SetByTermWeek($primary);
                     break;
                 case 'easter':
                     $rule->SetByEaster($primary);
                     break;
             }
         }
     }
     return array($include_ranges, $rrules, $exclude_ranges, $exrules);
 }
 static function validate_recurrence_rule_data(&$simple, &$errors)
 {
     // If no recurrence is enabled, just return NULL
     if (!isset($simple['enable'])) {
         return NULL;
     }
     // Otherwise, create the recurrence rule.
     $recur = new CalendarRecurRule();
     // Interval
     if (isset($simple['interval'])) {
         $interval = $simple['interval'];
         if (is_numeric($interval)) {
             $recur->SetInterval((int) $interval);
         } else {
             $errors[] = array('field' => 'interval', 'text' => "Non-numeric interval: {$interval}");
         }
     }
     // Frequency dependent
     if (isset($simple['freq'])) {
         switch ($simple['freq']) {
             case 'daily':
                 $recur->SetFrequency('daily');
                 break;
             case 'weekly':
                 $recur->SetFrequency('weekly');
                 if (isset($simple['weekly_byday']) && is_array($simple['weekly_byday'])) {
                     foreach ($simple['weekly_byday'] as $day => $dummy) {
                         if (is_numeric($day) && $day >= 0 && $day < 7) {
                             $recur->SetByDay($day);
                         }
                     }
                 }
                 break;
             case 'monthly':
                 $recur->SetFrequency('monthly');
                 if (isset($simple['monthly_method'])) {
                     switch ($simple['monthly_method']) {
                         case 'monthday':
                             if (isset($simple['monthly_monthday']) && is_array($simple['monthly_monthday'])) {
                                 $monthday =& $simple['monthly_monthday'];
                                 $monthday_fail = false;
                                 if (!isset($monthday['monthday']) || !is_numeric($monthday['monthday'])) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'monthly_monthday', 'text' => 'Invalid monthly day.');
                                 } elseif ($monthday['monthday'] < -31 || $monthday['monthday'] > 31 || !$monthday['monthday']) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'monthly_monthday', 'text' => 'Monthly day out of range');
                                 }
                                 if (!$monthday_fail) {
                                     $recur->SetByMonthDay((int) $monthday['monthday']);
                                 }
                             }
                             break;
                         case 'weekday':
                             if (isset($simple['monthly_weekday']) && is_array($simple['monthly_weekday'])) {
                                 $weekday =& $simple['monthly_weekday'];
                                 $weekday_fail = false;
                                 if (!isset($weekday['day'])) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'monthly_weekday', 'text' => 'Invalid monthly day.');
                                 } else {
                                     $days_split = split(',', $weekday['day']);
                                     $monthly_weekday_days = array();
                                     foreach ($days_split as $day) {
                                         if (!is_numeric($day) || $day < 0 || $day > 6) {
                                             $weekday_fail = true;
                                         } else {
                                             $monthly_weekday_days[] = (int) $day;
                                         }
                                     }
                                     if ($weekday_fail) {
                                         $errors[] = array('field' => 'monthly_weekday', 'text' => 'Monthly day(s) out of range');
                                     }
                                 }
                                 if (!isset($weekday['week']) || !is_numeric($weekday['week'])) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'monthly_weekday', 'text' => 'Invalid monthly week.');
                                 } elseif ($weekday['week'] < -5 || $weekday['week'] > 5 || !$weekday['week']) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'monthly_weekday', 'text' => 'Monthly week out of range');
                                 }
                                 if (!$weekday_fail) {
                                     if (count($monthly_weekday_days) == 1) {
                                         $recur->SetByDay($monthly_weekday_days[0], (int) $weekday['week']);
                                     } else {
                                         foreach ($monthly_weekday_days as $day) {
                                             $recur->SetByDay($day);
                                         }
                                         $recur->SetBySetPos((int) $weekday['week']);
                                     }
                                 }
                             }
                             break;
                     }
                 }
                 break;
             case 'yearly':
                 $recur->SetFrequency('yearly');
                 if (isset($simple['yearly_method'])) {
                     switch ($simple['yearly_method']) {
                         case 'monthday':
                             if (isset($simple['yearly_monthday']) && is_array($simple['yearly_monthday'])) {
                                 $monthday =& $simple['yearly_monthday'];
                                 $monthday_fail = false;
                                 if (!isset($monthday['monthday']) || !is_numeric($monthday['monthday'])) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'Invalid yearly day.');
                                 } elseif ($monthday['monthday'] < -31 || $monthday['monthday'] > 31 || !$monthday['monthday']) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'yearly day out of range');
                                 }
                                 if (!isset($monthday['month']) || !is_numeric($monthday['month'])) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'Invalid yearly month.');
                                 } elseif ($monthday['month'] < 1 || $monthday['month'] > 12) {
                                     $monthday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'yearly month out of range');
                                 }
                                 if (!$monthday_fail) {
                                     $recur->SetByMonthDay((int) $monthday['monthday']);
                                     $recur->SetByMonth((int) $monthday['month']);
                                 }
                             }
                             break;
                         case 'weekday':
                             if (isset($simple['yearly_weekday']) && is_array($simple['yearly_weekday'])) {
                                 $weekday =& $simple['yearly_weekday'];
                                 $weekday_fail = false;
                                 if (!isset($weekday['week']) || !is_numeric($weekday['week'])) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_weekday', 'text' => 'Invalid yearly week.');
                                 } elseif ($weekday['week'] < -5 || $weekday['week'] > 5 || !$weekday['week']) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_weekday', 'text' => 'yearly week out of range');
                                 }
                                 if (!isset($weekday['day']) || !is_numeric($weekday['day'])) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_weekday', 'text' => 'Invalid yearly day.');
                                 } elseif ($weekday['day'] < 0 || $weekday['day'] > 6) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_weekday', 'text' => 'yearly day out of range');
                                 }
                                 if (!isset($weekday['month']) || !is_numeric($weekday['month'])) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'Invalid yearly month.');
                                 } elseif ($weekday['month'] < 1 || $weekday['month'] > 12) {
                                     $weekday_fail = true;
                                     $errors[] = array('field' => 'yearly_monthday', 'text' => 'yearly month out of range');
                                 }
                                 if (!$weekday_fail) {
                                     // use monthly with bymonth and byday
                                     /// @note jh559: using monthly frequency here doesn't work coz yearly interval would be wrong
                                     // 										$recur->SetFrequency('monthly');
                                     // 										$recur->SetByMonth((int)$weekday['month']);
                                     // 										$recur->SetByDay((int)$weekday['day'], (int)$weekday['week']);
                                     // use yearly with bymonth and bysetpos
                                     $recur->SetByDay((int) $weekday['day'], (int) $weekday['week']);
                                     $recur->SetByMonth((int) $weekday['month']);
                                     //$recur->SetBySetpos((int)$weekday['week']);
                                 }
                             }
                             break;
                         case 'yearday':
                             if (isset($simple['yearly_yearday']) && is_array($simple['yearly_yearday'])) {
                                 $yearday =& $simple['yearly_yearday'];
                                 if (!isset($yearday['yearday']) || !is_numeric($yearday['yearday'])) {
                                     $errors[] = array('field' => 'yearly_yearday', 'text' => 'Invalid yearly day of year.');
                                 } elseif ($yearday['yearday'] < -365 || $yearday['yearday'] > 365 || !$yearday['yearday']) {
                                     $errors[] = array('field' => 'yearly_weekday', 'text' => 'yearly day of year out of range');
                                 } else {
                                     $recur->SetByYearday((int) $yearday['yearday']);
                                 }
                             }
                             break;
                     }
                 }
                 break;
             default:
                 $errors[] = array('field' => 'freq', 'text' => 'Unrecognised frequency');
                 break;
         }
     } else {
         $errors[] = array('text' => 'Recurrence frequency missing');
     }
     // Range
     if (isset($simple['range_method'])) {
         switch ($simple['range_method']) {
             case 'count':
                 if (isset($simple['count']) && is_numeric($simple['count'])) {
                     if ($simple['count'] > 0) {
                         $recur->SetCount((int) $simple['count']);
                     } else {
                         $recur->SetCount(1);
                         $errors[] = array('field' => 'count', 'text' => 'Occurrence count should be positive and non-zero');
                     }
                 } else {
                     $recur->SetCount(10);
                     $errors[] = array('field' => 'count', 'text' => 'Non-numeric occurrence count');
                 }
                 break;
             case 'until':
                 if (isset($simple['until']) && is_array($simple['until'])) {
                     $until =& $simple['until'];
                     $until_fail = false;
                     if (!isset($until['year']) || !is_numeric($until['year'])) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Invalid until year');
                     } elseif ($until['year'] < 2000 || $until['year'] > 2100) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Until year out of range');
                     }
                     if (!isset($until['month']) || !is_numeric($until['month'])) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Invalid until month');
                     } elseif ($until['month'] < 1 || $until['month'] > 12) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Until month out of range');
                     }
                     if (!isset($until['monthday']) || !is_numeric($until['monthday'])) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Invalid until day of month');
                     } elseif ($until['monthday'] < 1 || $until['monthday'] > 31) {
                         $until_fail = true;
                         $errors[] = array('field' => 'until', 'text' => 'Year out of range');
                     }
                     if (!$until_fail) {
                         $until_date = mktime(23, 59, 59, (int) $until['month'], (int) $until['monthday'], (int) $until['year']);
                         $recur->SetUntil($until_date);
                     }
                 } else {
                     $errors[] = array('field' => 'until', 'text' => 'Invalid until date');
                 }
                 break;
         }
     }
     return $recur;
 }