* Calendar/CalDAV: fix wrong end-date of recurring events incl. whole-day events

This commit is contained in:
Ralf Becker 2017-03-29 19:31:53 +02:00
parent 9dcc52b37d
commit 9810077eab
4 changed files with 62 additions and 29 deletions

View File

@ -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');

View File

@ -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)
*

View File

@ -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');
}
/**

View File

@ -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)
{