diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 44465ebb..8667b4da 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -325,10 +325,13 @@ class kolab_calendar extends kolab_storage_folder_api // skip the first instance of a recurring event if listed in exdate if ($virtual && !empty($event['recurrence']['EXDATE'])) { $event_date = $event['start']->format('Ymd'); - $exdates = (array)$event['recurrence']['EXDATE']; + $event_tz = $event['start']->getTimezone(); - foreach ($exdates as $exdate) { - if ($exdate->format('Ymd') == $event_date) { + foreach ((array) $event['recurrence']['EXDATE'] as $exdate) { + $ex = clone $exdate; + $ex->setTimezone($event_tz); + + if ($ex->format('Ymd') == $event_date) { $add = false; break; } diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index 06e660b8..0a33798a 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -1072,19 +1072,18 @@ class libvcalendar implements Iterator // add EXDATEs each one per line (for Thunderbird Lightning) if (is_array($exdates)) { - foreach ($exdates as $ex) { - if ($ex instanceof \DateTime) { - $exd = clone $event['start']; - $exd->setDate($ex->format('Y'), $ex->format('n'), $ex->format('j')); - $exd->setTimeZone(new \DateTimeZone('UTC')); - $ve->add($this->datetime_prop($cal, 'EXDATE', $exd, true)); + foreach ($exdates as $exdate) { + if ($exdate instanceof DateTime) { + $ve->add($this->datetime_prop($cal, 'EXDATE', $exdate)); } } } // add RDATEs - if (!empty($rdates)) { - foreach ((array)$rdates as $rdate) { - $ve->add($this->datetime_prop($cal, 'RDATE', $rdate)); + if (is_array($rdates)) { + foreach ($rdates as $rdate) { + if ($ex instanceof DateTime) { + $ve->add($this->datetime_prop($cal, 'RDATE', $rdate)); + } } } } diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php index 4f55a502..cde67fad 100644 --- a/plugins/libkolab/lib/kolab_format.php +++ b/plugins/libkolab/lib/kolab_format.php @@ -203,17 +203,20 @@ abstract class kolab_format * @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object * @param DateTimeZone The timezone the date/time is in. Use global default if Null, local time if False * @param boolean True of the given date has no time component - * @return object The libkolabxml date/time object + * @param DateTimeZone The timezone to convert the date to before converting to cDateTime + * + * @return cDateTime The libkolabxml date/time object */ - public static function get_datetime($datetime, $tz = null, $dateonly = false) + public static function get_datetime($datetime, $tz = null, $dateonly = false, $dest_tz = null) { - // use timezone information from datetime of global setting + // use timezone information from datetime or global setting if (!$tz && $tz !== false) { if ($datetime instanceof DateTime) $tz = $datetime->getTimezone(); if (!$tz) $tz = self::$timezone; } + $result = new cDateTime(); try { @@ -225,14 +228,26 @@ abstract class kolab_format else if (is_string($datetime) && strlen($datetime)) { $datetime = $tz ? new DateTime($datetime, $tz) : new DateTime($datetime); } + else if ($datetime instanceof DateTime) { + $datetime = clone $datetime; + } } catch (Exception $e) {} if ($datetime instanceof DateTime) { + if ($dest_tz instanceof DateTimeZone && $dest_tz !== $datetime->getTimezone()) { + $datetime->setTimezone($dest_tz); + $tz = $dest_tz; + } + $result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j')); - if (!$dateonly) - $result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s')); + if ($dateonly) { + // Dates should be always in local time only + return $result; + } + + $result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s')); // libkolabxml throws errors on some deprecated timezone names $utc_aliases = array('UTC', 'GMT', '+00:00', 'Z', 'Etc/GMT', 'Etc/UTC'); @@ -254,16 +269,19 @@ abstract class kolab_format /** * Convert the given cDateTime into a PHP DateTime object * - * @param object cDateTime The libkolabxml datetime object - * @return object DateTime PHP datetime instance + * @param cDateTime The libkolabxml datetime object + * @param DateTimeZone The timezone to convert the date to + * + * @return DateTime PHP datetime instance */ - public static function php_datetime($cdt) + public static function php_datetime($cdt, $dest_tz = null) { - if (!is_object($cdt) || !$cdt->isValid()) + if (!is_object($cdt) || !$cdt->isValid()) { return null; + } $d = new DateTime; - $d->setTimezone(self::$timezone); + $d->setTimezone($dest_tz ?: self::$timezone); try { if ($tzs = $cdt->timezone()) { diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php index d6d34422..a08d0aa0 100644 --- a/plugins/libkolab/lib/kolab_format_xcal.php +++ b/plugins/libkolab/lib/kolab_format_xcal.php @@ -174,6 +174,10 @@ abstract class kolab_format_xcal extends kolab_format } } + if ($object['start'] instanceof DateTime) { + $start_tz = $object['start']->getTimezone(); + } + // read recurrence rule if (($rr = $this->obj->recurrenceRule()) && $rr->isValid()) { $rrule_type_map = array_flip($this->rrule_type_map); @@ -185,7 +189,7 @@ abstract class kolab_format_xcal extends kolab_format if (($count = $rr->count()) && $count > 0) { $object['recurrence']['COUNT'] = $count; } - else if ($until = self::php_datetime($rr->end())) { + else if ($until = self::php_datetime($rr->end(), $start_tz)) { $refdate = $this->get_reference_date(); if ($refdate && $refdate instanceof DateTime && !$refdate->_dateonly) { $until->setTime($refdate->format('G'), $refdate->format('i'), 0); @@ -214,16 +218,18 @@ abstract class kolab_format_xcal extends kolab_format if ($exdates = $this->obj->exceptionDates()) { for ($i=0; $i < $exdates->size(); $i++) { - if ($exdate = self::php_datetime($exdates->get($i))) + if ($exdate = self::php_datetime($exdates->get($i), $start_tz)) { $object['recurrence']['EXDATE'][] = $exdate; + } } } } if ($rdates = $this->obj->recurrenceDates()) { for ($i=0; $i < $rdates->size(); $i++) { - if ($rdate = self::php_datetime($rdates->get($i))) + if ($rdate = self::php_datetime($rdates->get($i), $start_tz)) { $object['recurrence']['RDATE'][] = $rdate; + } } } @@ -415,6 +421,10 @@ abstract class kolab_format_xcal extends kolab_format $this->obj->setOrganizer($organizer); } + if ($object['start'] instanceof DateTime) { + $start_tz = $object['start']->getTimezone(); + } + // save recurrence rule $rr = new RecurrenceRule; $rr->setFrequency(RecurrenceRule::FreqNone); @@ -470,13 +480,13 @@ abstract class kolab_format_xcal extends kolab_format if ($object['recurrence']['COUNT']) $rr->setCount(intval($object['recurrence']['COUNT'])); else if ($object['recurrence']['UNTIL']) - $rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true)); + $rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true, $start_tz)); if ($rr->isValid()) { // add exception dates (only if recurrence rule is valid) $exdates = new vectordatetime; foreach ((array)$object['recurrence']['EXDATE'] as $exdate) - $exdates->push(self::get_datetime($exdate, null, true)); + $exdates->push(self::get_datetime($exdate, null, true, $start_tz)); $this->obj->setExceptionDates($exdates); } else { @@ -494,7 +504,7 @@ abstract class kolab_format_xcal extends kolab_format if (!empty($object['recurrence']['RDATE'])) { $rdates = new vectordatetime; foreach ((array)$object['recurrence']['RDATE'] as $rdate) - $rdates->push(self::get_datetime($rdate, null, true)); + $rdates->push(self::get_datetime($rdate, null, true, $start_tz)); $this->obj->setRecurrenceDates($rdates); } @@ -760,7 +770,7 @@ abstract class kolab_format_xcal extends kolab_format $error_logged = true; rcube::raise_error(array( 'code' => 900, - 'message' => "required kolabcalendaring module not found" + 'message' => "Required kolabcalendaring module not found" ), true); }