From 9810077eab5d955de1f05428cd8ea634cd21fdc0 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 29 Mar 2017 19:31:53 +0200 Subject: [PATCH] * Calendar/CalDAV: fix wrong end-date of recurring events incl. whole-day events --- calendar/inc/class.calendar_boupdate.inc.php | 18 ++++++++++ calendar/inc/class.calendar_ical.inc.php | 38 ++++++++++++++++++-- calendar/inc/class.calendar_rrule.inc.php | 16 ++++----- calendar/inc/class.calendar_so.inc.php | 19 +--------- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 0138c96bf2..3979839b7a 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -1179,6 +1179,23 @@ class calendar_boupdate extends calendar_bo $old_event = null; } + // set recur-enddate/range-end to real end-date of last recurrence + if ($event['recur_type'] != MCAL_RECUR_NONE && $event['recur_enddate']) + { + $rrule = calendar_rrule::event2rrule($event, false); + $rrule->rewind(); + $enddate = $rrule->current(); + do + { + $rrule->next_no_exception(); + $occurrence = $rrule->current(); + } + while ($rrule->valid(true) && ($enddate = $occurrence)); + $enddate->modify(($event['end'] - $event['start']).' second'); + $event['recur_enddate'] = $enddate->format('ts'); + //error_log(__METHOD__."($event[title]) start=".Api\DateTime::to($event['start'],'string').', end='.Api\DateTime::to($event['end'],'string').', range_end='.Api\DateTime::to($event['recur_enddate'],'string')); + } + if (!isset($event['whole_day'])) $event['whole_day'] = $this->isWholeDay($event); $save_event = $event; if ($event['whole_day']) @@ -1204,6 +1221,7 @@ class calendar_boupdate extends calendar_bo if (!empty($event['recur_enddate'])) { $time = $this->so->startOfDay(new Api\DateTime($event['recur_enddate'], Api\DateTime::$user_timezone)); + $time->modify(($event['end'] - $event['start'] + 1).' seconds'); $event['recur_enddate'] = Api\DateTime::to($time, 'ts'); $time->setUser(); $save_event['recur_enddate'] = Api\DateTime::to($time, 'ts'); diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index e5330de9bd..07a8300341 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -636,8 +636,16 @@ class calendar_ical extends calendar_boupdate { self::$tz_cache['UTC'] = calendar_timezones::DateTimeZone('UTC'); } - $rrule['UNTIL']->setTimezone(self::$tz_cache['UTC']); - $rrule['UNTIL'] = $rrule['UNTIL']->format('Ymd\THis\Z'); + if (empty($event['whole_day'])) + { + $rrule['UNTIL']->setTimezone(self::$tz_cache['UTC']); + $rrule['UNTIL'] = $rrule['UNTIL']->format('Ymd\THis\Z'); + } + // for whole-day events UNTIL must be just the inclusive date + else + { + $rrule['UNTIL'] = $rrule['UNTIL']->format('Ymd'); + } } } if ($version == '1.0') @@ -2617,6 +2625,11 @@ class calendar_ical extends calendar_boupdate { unset($vcardData['recur_enddate']); } + else + { + // iCal defines enddate to be a time and eg. Apple sends 1s less then next recurance, if they split events + self::check_fix_endate($vcardData); + } } elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches)) { @@ -3262,6 +3275,27 @@ class calendar_ical extends calendar_boupdate return array(); } + /** + * iCal defines enddate to be a time and eg. Apple sends 1s less then next recurance, if they split events + * + * We need to fix that situation by moving end one day back. + * + * @param array& $vcardData values for keys "start" and "recur_enddate", later get's modified if neccessary + */ + static function check_fix_endate(array &$vcardData) + { + $end = new Api\DateTime($vcardData['recur_enddate']); + $start = new Api\DateTime($vcardData['start']); + $start->setDate($end->format('Y'), $end->format('m'), $end->format('d')); + + if ($end->format('ts') < $start->format('ts')) + { + $end->modify('-1day'); + $vcardData['recur_enddate'] = $end->format('ts'); + //error_log(__METHOD__."($vcardData[event_title]) fix recure_enddate to ".$end->format('Y-m-d H:i:s')); + } + } + /** * Create a freebusy vCal for the given user(s) * diff --git a/calendar/inc/class.calendar_rrule.inc.php b/calendar/inc/class.calendar_rrule.inc.php index f25f2588bb..0d5b21bb8a 100644 --- a/calendar/inc/class.calendar_rrule.inc.php +++ b/calendar/inc/class.calendar_rrule.inc.php @@ -130,12 +130,6 @@ class calendar_rrule implements Iterator * @var DateTime */ public $enddate; - /** - * Enddate of recurring event, as Ymd integer (eg. 20091111) - * - * @var int - */ - public $enddate_ymd; const SUNDAY = 1; const MONDAY = 2; @@ -295,7 +289,6 @@ class calendar_rrule implements Iterator { $enddate->setTimezone($this->time->getTimezone()); } - $this->enddate_ymd = (int)$enddate->format('Ymd'); // if no valid weekdays are given for weekly repeating, we use just the current weekday if (!($this->weekdays = (int)$weekdays) && ($type == self::WEEKLY || $type == self::MONTHLY_WDAY)) @@ -537,11 +530,16 @@ class calendar_rrule implements Iterator /** * Checks if current position is valid * + * @param boolean $use_just_date =false default use also time * @return boolean */ - public function valid () + public function valid($use_just_date=false) { - return $this->current->format('Ymd') <= $this->enddate_ymd; + if ($use_just_date) + { + return $this->current->format('Ymd') <= $this->enddate->format('Ymd'); + } + return $this->current->format('ts') < $this->enddate->format('ts'); } /** diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 9981150d1c..c2abc2f61c 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -351,7 +351,7 @@ class calendar_so { $cols .= ',range_start AS cal_start,(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id) AS cal_end'; } - $cols .= ',range_end AS recur_enddate'; + $cols .= ',range_end-1 AS recur_enddate'; $events =& $this->get_events($this->db->select($this->cal_table, $cols, $where, __LINE__, __FILE__, false, $group_by, 'calendar', 0, $join), $recur_date); @@ -1368,23 +1368,6 @@ ORDER BY cal_user_type, cal_usre_id $event['recur_interval'] = 1; } - // set recur-enddate/range-end to real end-date of last recurrence - if ($event['recur_type'] != MCAL_RECUR_NONE && $event['recur_enddate']) - { - $rrule = calendar_rrule::event2rrule($event, false); - $rrule->rewind(); - $enddate = $rrule->current(); - do - { - $rrule->next_no_exception(); - $occurrence = $rrule->current(); - } - while ($rrule->valid() && ($enddate = $occurrence)); - $enddate->modify(($event['end'] - $event['start']).' second'); - $event['recur_enddate'] = $enddate->format('server'); - //error_log(__METHOD__."($event[title]) start=".Api\DateTime::to($event['start'],'string').', end='.Api\DateTime::to($event['end'],'string').', range_end='.Api\DateTime::to($event['recur_enddate'],'string')); - } - // add colum prefix 'cal_' if there's not already a 'recur_' prefix foreach($event as $col => $val) {