Branch 'roundcubemail-plugins-kolab-0.7' - 2 commits - plugins/calendar
Thomas Brüderli
bruederli at kolabsys.com
Tue Oct 30 12:01:39 CET 2012
plugins/calendar/calendar.php | 79 ++++++++++++++------
plugins/calendar/calendar_ui.js | 30 +++++--
plugins/calendar/drivers/calendar_driver.php | 6 -
plugins/calendar/drivers/kolab/kolab_calendar.php | 85 +++++++++++++---------
plugins/calendar/drivers/kolab/kolab_driver.php | 28 ++++---
plugins/calendar/lib/calendar_ical.php | 70 +++++++++++++-----
plugins/calendar/lib/calendar_recurrence.php | 23 +++--
plugins/calendar/lib/calendar_ui.php | 6 +
8 files changed, 217 insertions(+), 110 deletions(-)
New commits:
commit e60cc461ee0c83117b88b09a9c7483712715d378
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Tue Oct 30 12:00:36 2012 +0100
More timestamp -> DateTime adaption and fixes
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index c9a61cd..812dcf4 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1075,6 +1075,8 @@ class calendar extends rcube_plugin
$event['alarms_text'] = $this->_alarms_text($event['alarms']);
if ($event['recurrence'])
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
+ if ($event['recurrence']['UNTIL'])
+ $event['recurrence']['UNTIL'] = $this->toUserDateTime($event['recurrence']['UNTIL'])->format('c');
return array(
'_id' => $event['calendar'] . ':' . $event['id'], // unique identifier for fullcalendar
@@ -1322,8 +1324,8 @@ class calendar extends rcube_plugin
$this->driver->new_event(array(
'uid' => $this->generate_uid(),
- 'start' => $start,
- 'end' => $start + $duration,
+ 'start' => new DateTime('@'.$start),
+ 'end' => new DateTime('@'.($start + $duration)),
'allday' => $allday,
'title' => rtrim($title),
'free_busy' => $fb == 2 ? 'outofoffice' : ($fb ? 'busy' : 'free'),
@@ -1672,7 +1674,9 @@ class calendar extends rcube_plugin
public function event_date_text($event, $tzinfo = false)
{
$fromto = '';
- $duration = $event['end'] - $event['start'];
+ $event['start'] = $this->toUserDateTime($event['start']);
+ $event['end'] = $this->toUserDateTime($event['end']);
+ $duration = $event['end']->format('U') - $event['start']->format('U');
$this->date_format_defaults();
$date_format = self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format']));
@@ -1706,7 +1710,7 @@ class calendar extends rcube_plugin
*/
public static function format_date($date, $format=null)
{
- return format_date(is_a($date, 'DateTime') ? $date->format('c') : $date, $format);
+ return format_date(is_a($date, 'DateTime') ? $date->format('c') : $date, $format, false);
}
/**
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 75acc8f..c325c0a 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -557,7 +557,7 @@ function rcube_calendar_ui(settings)
recurrence = $('#edit-recurrence-frequency').val(event.recurrence ? event.recurrence.FREQ : '').change();
interval = $('select.edit-recurrence-interval').val(event.recurrence ? event.recurrence.INTERVAL : 1);
rrtimes = $('#edit-recurrence-repeat-times').val(event.recurrence ? event.recurrence.COUNT : 1);
- rrenddate = $('#edit-recurrence-enddate').val(event.recurrence && event.recurrence.UNTIL ? $.fullCalendar.formatDate(new Date(event.recurrence.UNTIL*1000), settings['date_format']) : '');
+ rrenddate = $('#edit-recurrence-enddate').val(event.recurrence && event.recurrence.UNTIL ? $.fullCalendar.formatDate($.fullCalendar.parseISO8601(event.recurrence.UNTIL,true), settings['date_format']) : '');
$('input.edit-recurrence-until:checked').prop('checked', false);
var weekdays = ['SU','MO','TU','WE','TH','FR','SA'];
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 4fe64d1..558b777 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -452,7 +452,8 @@ class kolab_calendar
$duration = $event['end']->format('U') - $event['start']->format('U');
$i = 0;
while ($rec_start = $recurrence->next_start()) {
- $rec_end = new DateTime('@' . ($rec_start->format('U') + $duration));
+ $rec_end = clone $rec_start;
+ $rec_end->modify('+'.$duration.' seconds');
$rec_id = $event['id'] . '-' . ++$i;
// add to output if in range
@@ -485,22 +486,21 @@ class kolab_calendar
$start_time = date('H:i:s', $rec['start-date']);
$allday = $rec['_is_all_day'] || ($start_time == '00:00:00' && $start_time == date('H:i:s', $rec['end-date']));
if ($allday) { // in Roundcube all-day events only go from 12:00 to 13:00
- $rec['start-date'] += 12 * 3600;
$dtstart = new DateTime('@'.$rec['start-date']);
- $dtstart->setTime(12, 0, 0);
+ $dtstart->modify('+12 hours');
$dtstart->setTimezone($this->cal->user_timezone);
+ $dtstart->setTime(12, 0, 0);
- $rec['end-date'] -= 11 * 3600;
$dtend = new DateTime('@'.$rec['end-date']);
- $dtend->setTime(13, 0, 0);
$dtend->setTimezone($this->cal->user_timezone);
-
- $rec['start-date'] = $dtstart;
- $rec['end-date'] = $dtend;
+ $dtend->modify('-11 hours');
+ $dtend->setTime(13, 0, 0);
// sanity check
- if ($rec['end-date'] <= $rec['start-date'])
- $dtend = new DateTime('@' . ($dtstart->format('U') + 90000));
+ if ($dtend <= $dtstart) {
+ $dtend = clone $dtstart;
+ $dtend->modify('+25 hours');
+ }
}
else {
$dtstart = new DateTime('@'.$rec['start-date']);
@@ -531,8 +531,10 @@ class kolab_calendar
if ($recurrence['range-type'] == 'number')
$rrule['COUNT'] = intval($recurrence['range']);
- else if ($recurrence['range-type'] == 'date')
+ else if ($recurrence['range-type'] == 'date') {
$rrule['UNTIL'] = new DateTime('@'.$recurrence['range']);
+ $rrule['UNTIL']->setTimezone($this->cal->user_timezone);
+ }
if ($recurrence['day']) {
$byday = array();
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index de9f99e..77b12cf 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -659,7 +659,8 @@ class kolab_driver extends calendar_driver
case 'future':
if ($master['id'] != $event['id']) {
// set until-date on master event
- $master['recurrence']['UNTIL'] = $old['start'] - 86400;
+ $master['recurrence']['UNTIL'] = clone $old['start'];
+ $master['recurrence']['UNTIL']->modify('-1 day');
unset($master['recurrence']['COUNT']);
$storage->update_event($master);
@@ -675,7 +676,7 @@ class kolab_driver extends calendar_driver
// remove fixed weekday, will be re-set to the new weekday in kolab_calendar::insert_event()
if (strlen($event['recurrence']['BYDAY']) == 2)
unset($event['recurrence']['BYDAY']);
- if ($master['recurrence']['BYMONTH'] == gmdate('n', $master['start']))
+ if ($master['recurrence']['BYMONTH'] == $master['start']->format('n'))
unset($event['recurrence']['BYMONTH']);
$success = $storage->insert_event($event);
@@ -687,26 +688,29 @@ class kolab_driver extends calendar_driver
$event['uid'] = $master['uid'];
// use start date from master but try to be smart on time or duration changes
- $old_start_date = date('Y-m-d', $old['start']);
- $old_start_time = date('H:i', $old['start']);
- $old_duration = $old['end'] - $old['start'];
+ $old_start_date = $old['start']->format('Y-m-d');
+ $old_start_time = $old['start']->format('H:i');
+ $old_duration = $old['end']->format('U') - $old['start']->format('U');
- $new_start_date = date('Y-m-d', $event['start']);
- $new_start_time = date('H:i', $event['start']);
- $new_duration = $event['end'] - $event['start'];
+ $new_start_date = $event['start']->format('Y-m-d');
+ $new_start_time = $event['start']->format('H:i');
+ $new_duration = $event['end']->format('U') - $event['start']->format('U');
$diff = $old_start_date != $new_start_date || $old_start_time != $new_start_time || $old_duration != $new_duration;
// shifted or resized
if ($diff && ($old_start_date == $new_start_date || $old_duration == $new_duration)) {
- $event['start'] = $master['start'] + ($event['start'] - $old['start']);
- $event['end'] = $event['start'] + $new_duration;
+ $offset = $event['start']->format('U') - $old['start']->format('U');
+ $event['start'] = clone $master['start'];
+ $event['start']->modify(($offset > 0 ? '+' : '') . $offset . ' seconds');
+ $event['end'] = clone $event['start'];
+ $event['end']->modify('+'.$new_duration.' seconds');
// remove fixed weekday, will be re-set to the new weekday in kolab_calendar::update_event()
if ($old_start_date != $new_start_date) {
if (strlen($event['recurrence']['BYDAY']) == 2)
unset($event['recurrence']['BYDAY']);
- if ($old['recurrence']['BYMONTH'] == gmdate('n', $old['start']))
+ if ($old['recurrence']['BYMONTH'] == $old['start']->format('n'))
unset($event['recurrence']['BYMONTH']);
}
}
diff --git a/plugins/calendar/lib/calendar_recurrence.php b/plugins/calendar/lib/calendar_recurrence.php
index 78ba7ef..0a3c296 100644
--- a/plugins/calendar/lib/calendar_recurrence.php
+++ b/plugins/calendar/lib/calendar_recurrence.php
@@ -53,6 +53,12 @@ class calendar_recurrence
// TODO: replace with something that has less than 6'000 lines of code
require_once($this->cal->home . '/lib/Horde_Date_Recurrence.php');
+ // shift until date by one day in order to trick the Horde_Date_Recurrence computation
+ if ($event['recurrence']['UNTIL']) {
+ $event['recurrence']['UNTIL'] = clone $event['recurrence']['UNTIL'];
+ $event['recurrence']['UNTIL']->modify('+1 day');
+ }
+
$this->event = $event;
$this->engine = new Horde_Date_Recurrence($dtstart->format('U'));
$this->engine->fromRRule20(calendar::to_rrule($event['recurrence']));
commit 5c36cf87f39dde5d1867a9e8e0b7b4ab2dbbf239
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 29 23:16:02 2012 +0100
Use DateTime objects instead of unix timestamps. Fixes various timezone and DST issues (including OTRS #1000068 and #1000069)
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index b3f0437..c9a61cd 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -45,6 +45,7 @@ class calendar extends rcube_plugin
public $urlbase;
public $timezone;
public $gmt_offset;
+ public $user_timezone;
public $ical;
public $ui;
@@ -859,7 +860,7 @@ class calendar extends rcube_plugin
$events = $this->get_ical()->import_from_file($_FILES['_data']['tmp_name']);
$count = $errors = 0;
- $rangestart = $_REQUEST['_range'] ? strtotime("now -" . intval($_REQUEST['_range']) . " months") : 0;
+ $rangestart = $_REQUEST['_range'] ? date_create("now -" . intval($_REQUEST['_range']) . " months") : 0;
foreach ($events as $event) {
// TODO: correctly handle recurring events which start before $rangestart
if ($event['end'] < $rangestart && (!$event['recurrence'] || ($event['recurrence']['until'] && $event['recurrence']['until'] < $rangestart)))
@@ -1030,12 +1031,22 @@ class calendar extends rcube_plugin
}
/**
- * Convert the given date string into a GMT-based time stamp
+ * Convert the given date value into a DateTime object in user's timezone
*/
- function fromGMT($datetime)
+ function toUserDateTime($datetime)
{
- $ts = is_numeric($datetime) ? $datetime : strtotime($datetime);
- return $ts + $this->gmt_offset;
+ if (is_a($datetime, 'DateTime')) {
+ $datetime->setTimezone($this->user_timezone);
+ return $datetime;
+ }
+ try {
+ $dt = new DateTime(is_numeric($datetime) ? '@'.$datetime : $datetime, new DateTimeZone('UTC'));
+ $dt->setTimezone($this->user_timezone);
+ }
+ catch (Exception $e) {
+ $dt = new DateTime('1970-01-01 00:00:00', $this->user_timezone);
+ }
+ return $dt;
}
/**
@@ -1067,8 +1078,8 @@ class calendar extends rcube_plugin
return array(
'_id' => $event['calendar'] . ':' . $event['id'], // unique identifier for fullcalendar
- 'start' => gmdate('c', $this->fromGMT($event['start'])), // client treats date strings as they were in users's timezone
- 'end' => gmdate('c', $this->fromGMT($event['end'])), // so shift timestamps to users's timezone and render a date string
+ 'start' => $this->toUserDateTime($event['start'])->format('c'), // client treats date strings as they were in users's timezone
+ 'end' => $this->toUserDateTime($event['end'])->format('c'), // so shift timestamps to users's timezone and render a date string
'description' => strval($event['description']),
'location' => strval($event['location']),
'className' => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'fc-event-cat-' . asciiwords(strtolower($event['categories']), true),
@@ -1086,8 +1097,8 @@ class calendar extends rcube_plugin
foreach ($alarms as $alarm) {
$out[] = array(
'id' => $alarm['id'],
- 'start' => gmdate('c', $this->fromGMT($alarm['start'])),
- 'end' => gmdate('c', $this->fromGMT($alarm['end'])),
+ 'start' => $this->toUserDateTime($alarm['start'])->format('c'),
+ 'end' => $this->toUserDateTime($alarm['end'])->format('c'),
'allDay' => ($alarm['allday'] == 1)?true:false,
'title' => $alarm['title'],
'location' => $alarm['location'],
@@ -1116,7 +1127,7 @@ class calendar extends rcube_plugin
}
if (preg_match('/@(\d+)/', $trigger, $m)) {
- $text .= ' ' . $this->gettext(array('name' => 'alarmat', 'vars' => array('datetime' => format_date($m[1]))));
+ $text .= ' ' . $this->gettext(array('name' => 'alarmat', 'vars' => array('datetime' => self::format_date($m[1]))));
}
else if ($val = self::parse_alaram_value($trigger)) {
$text .= ' ' . intval($val[0]) . ' ' . $this->gettext('trigger' . $val[1]);
@@ -1156,7 +1167,7 @@ class calendar extends rcube_plugin
if ($rrule['COUNT'])
$until = $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT'])));
else if ($rrule['UNTIL'])
- $until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
+ $until = $this->gettext('recurrencend') . ' ' . self::format_date($rrule['UNTIL'], self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
else
$until = $this->gettext('forever');
@@ -1198,11 +1209,11 @@ class calendar extends rcube_plugin
$k = strtoupper($k);
switch ($k) {
case 'UNTIL':
- $val = gmdate('Ymd\THis', $val);
+ $val = $val->format('Ymd\THis');
break;
case 'EXDATE':
foreach ((array)$val as $i => $ex)
- $val[$i] = gmdate('Ymd\THis', $ex);
+ $val[$i] = $ex->format('Ymd\THis');
$val = join(',', $val);
break;
}
@@ -1298,7 +1309,7 @@ class calendar extends rcube_plugin
$start -= 3600;
if ($allday) {
- $start = strtotime(date('Y-m-d 00:00:00', $start));
+ $start = date_create(date('Y-m-d 00:00:00', $start));
$duration = 86399;
}
@@ -1552,6 +1563,13 @@ class calendar extends rcube_plugin
*/
private function prepare_event(&$event, $action)
{
+ // convert dates into DateTime objects in user's current timezone
+ $event['start'] = new DateTime($event['start'], $this->user_timezone);
+ $event['end'] = new DateTime($event['end'], $this->user_timezone);
+
+ if ($event['recurrence']['UNTIL'])
+ $event['recurrence']['UNTIL'] = new DateTime($event['recurrence']['UNTIL'], $this->user_timezone);
+
$attachments = array();
$eventid = 'cal:'.$event['id'];
if (is_array($_SESSION['event_session']) && $_SESSION['event_session']['id'] == $eventid) {
@@ -1661,17 +1679,17 @@ class calendar extends rcube_plugin
$time_format = self::to_php_date_format($this->rc->config->get('calendar_time_format', $this->defaults['calendar_time_format']));
if ($event['allday']) {
- $fromto = format_date($event['start'], $date_format);
- if (($todate = format_date($event['end'], $date_format)) != $fromto)
+ $fromto = self::format_date($event['start'], $date_format);
+ if (($todate = self::format_date($event['end'], $date_format)) != $fromto)
$fromto .= ' - ' . $todate;
}
- else if ($duration < 86400 && gmdate('d', $event['start']) == gmdate('d', $event['end'])) {
- $fromto = format_date($event['start'], $date_format) . ' ' . format_date($event['start'], $time_format) .
- ' - ' . format_date($event['end'], $time_format);
+ else if ($duration < 86400 && $event['start']->format('d') == $event['end']->format('d')) {
+ $fromto = self::format_date($event['start'], $date_format) . ' ' . self::format_date($event['start'], $time_format) .
+ ' - ' . self::format_date($event['end'], $time_format);
}
else {
- $fromto = format_date($event['start'], $date_format) . ' ' . format_date($event['start'], $time_format) .
- ' - ' . format_date($event['end'], $date_format) . ' ' . format_date($event['end'], $time_format);
+ $fromto = self::format_date($event['start'], $date_format) . ' ' . self::format_date($event['start'], $time_format) .
+ ' - ' . self::format_date($event['end'], $date_format) . ' ' . self::format_date($event['end'], $time_format);
}
// add timezone information
@@ -1683,6 +1701,15 @@ class calendar extends rcube_plugin
}
/**
+ * Wrapper for Roundcube's internal format_date() function
+ * accepting DateTime objects as argument
+ */
+ public static function format_date($date, $format=null)
+ {
+ return format_date(is_a($date, 'DateTime') ? $date->format('c') : $date, $format);
+ }
+
+ /**
* Echo simple free/busy status text for the given user and time range
*/
public function freebusy_status()
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index a8eb2b8..75acc8f 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -208,7 +208,19 @@ function rcube_calendar_ui(settings)
date.setTime((ts + 3600) * 1000);
return date;
};
-
+
+ // turn the given date into an ISO 8601 date string understandable by PHPs strtotime()
+ var date2servertime = function(date)
+ {
+ return date.getFullYear()+'-'+zeropad(date.getMonth()+1)+'-'+zeropad(date.getDate())
+ + 'T'+zeropad(date.getHours())+':'+zeropad(date.getMinutes())+':'+zeropad(date.getSeconds());
+ }
+
+ var zeropad = function(num)
+ {
+ return (num < 10 ? '0' : '') + num;
+ }
+
// determine whether the given date is on a weekend
var is_weekend = function(date)
{
@@ -642,8 +654,8 @@ function rcube_calendar_ui(settings)
// post data to server
var data = {
calendar: event.calendar,
- start: date2unixtime(start),
- end: date2unixtime(end),
+ start: date2servertime(start),
+ end: date2servertime(end),
allday: allday.checked?1:0,
title: title.val(),
description: description.val(),
@@ -702,7 +714,7 @@ function rcube_calendar_ui(settings)
if (until == 'count')
data.recurrence.COUNT = rrtimes.val();
else if (until == 'until')
- data.recurrence.UNTIL = date2unixtime(parse_datetime(endtime.val(), rrenddate.val()));
+ data.recurrence.UNTIL = date2servertime(parse_datetime(endtime.val(), rrenddate.val()));
if (freq == 'WEEKLY') {
var byday = [];
@@ -2355,8 +2367,8 @@ function rcube_calendar_ui(settings)
var data = {
id: event.id,
calendar: event.calendar,
- start: date2unixtime(event.start),
- end: date2unixtime(event.end),
+ start: date2servertime(event.start),
+ end: date2servertime(event.end),
allday: allDay?1:0
};
update_event_confirm('move', event, data);
@@ -2373,8 +2385,8 @@ function rcube_calendar_ui(settings)
var data = {
id: event.id,
calendar: event.calendar,
- start: date2unixtime(event.start),
- end: date2unixtime(event.end)
+ start: date2servertime(event.start),
+ end: date2servertime(event.end)
};
update_event_confirm('resize', event, data);
},
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index babecd7..1729277 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -37,8 +37,8 @@
* 'id' => 'Event ID used for editing',
* 'uid' => 'Unique identifier of this event',
* 'calendar' => 'Calendar identifier to add event to or where the event is stored',
- * 'start' => <unixtime>, // Event start date/time as unix timestamp
- * 'end' => <unixtime>, // Event end date/time as unix timestamp
+ * 'start' => DateTime, // Event start date/time as unix timestamp
+ * 'end' => DateTime, // Event end date/time as unix timestamp
* 'allday' => true|false, // Boolean flag if this is an all-day event
* 'changed' => <unixtime>, // Last modification date of event
* 'title' => 'Event title/summary',
@@ -47,7 +47,7 @@
* 'recurrence' => array( // Recurrence definition according to iCalendar (RFC 2445) specification as list of key-value pairs
* 'FREQ' => 'DAILY|WEEKLY|MONTHLY|YEARLY',
* 'INTERVAL' => 1...n,
- * 'UNTIL' => <unixtime>,
+ * 'UNTIL' => DateTime,
* 'COUNT' => 1..n, // number of times
* // + more properties (see http://www.kanzaki.com/docs/ical/recur.html)
* 'EXDATE' => array(), // list of <unixtime>s of exception Dates/Times
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 0475ba1..4fe64d1 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -218,6 +218,10 @@ class kolab_calendar
*/
public function list_events($start, $end, $search = null, $virtual = 1)
{
+ // convert to DateTime for comparisons
+ $start = new DateTime('@'.$start);
+ $end = new DateTime('@'.$end);
+
$this->_fetch_events();
if (!empty($search))
@@ -443,14 +447,14 @@ class kolab_calendar
require_once($this->cal->home . '/lib/calendar_recurrence.php');
$recurrence = new calendar_recurrence($this->cal, $event);
-
+
$events = array();
- $duration = $event['end'] - $event['start'];
+ $duration = $event['end']->format('U') - $event['start']->format('U');
$i = 0;
while ($rec_start = $recurrence->next_start()) {
- $rec_end = $rec_start + $duration;
+ $rec_end = new DateTime('@' . ($rec_start->format('U') + $duration));
$rec_id = $event['id'] . '-' . ++$i;
-
+
// add to output if in range
if (($rec_start <= $end && $rec_end >= $start) || ($event_id && $rec_id == $event_id)) {
$rec_event = $event;
@@ -482,14 +486,27 @@ class kolab_calendar
$allday = $rec['_is_all_day'] || ($start_time == '00:00:00' && $start_time == date('H:i:s', $rec['end-date']));
if ($allday) { // in Roundcube all-day events only go from 12:00 to 13:00
$rec['start-date'] += 12 * 3600;
- $rec['end-date'] -= 11 * 3600;
- $rec['end-date'] -= $this->cal->gmt_offset - date('Z', $rec['end-date']); // shift times from server's timezone to user's timezone
- $rec['start-date'] -= $this->cal->gmt_offset - date('Z', $rec['start-date']); // because generated with mktime() in Horde_Kolab_Format_Date::decodeDate()
+ $dtstart = new DateTime('@'.$rec['start-date']);
+ $dtstart->setTime(12, 0, 0);
+ $dtstart->setTimezone($this->cal->user_timezone);
+
+ $rec['end-date'] -= 11 * 3600;
+ $dtend = new DateTime('@'.$rec['end-date']);
+ $dtend->setTime(13, 0, 0);
+ $dtend->setTimezone($this->cal->user_timezone);
+
+ $rec['start-date'] = $dtstart;
+ $rec['end-date'] = $dtend;
+
// sanity check
if ($rec['end-date'] <= $rec['start-date'])
- $rec['end-date'] += 86400;
+ $dtend = new DateTime('@' . ($dtstart->format('U') + 90000));
}
-
+ else {
+ $dtstart = new DateTime('@'.$rec['start-date']);
+ $dtend = new DateTime('@'.$rec['end-date']);
+ }
+
// convert alarm time into internal format
if ($rec['alarm']) {
$alarm_value = $rec['alarm'];
@@ -515,7 +532,7 @@ class kolab_calendar
if ($recurrence['range-type'] == 'number')
$rrule['COUNT'] = intval($recurrence['range']);
else if ($recurrence['range-type'] == 'date')
- $rrule['UNTIL'] = $recurrence['range'];
+ $rrule['UNTIL'] = new DateTime('@'.$recurrence['range']);
if ($recurrence['day']) {
$byday = array();
@@ -537,7 +554,7 @@ class kolab_calendar
if ($recurrence['exclusion']) {
foreach ((array)$recurrence['exclusion'] as $excl)
- $rrule['EXDATE'][] = strtotime($excl . date(' H:i:s', $rec['start-date'])); // use time of event start
+ $rrule['EXDATE'][] = new DateTime($excl . $dtstart->format(' H:i:s'), $this->cal->user_timezone); // use time of event start
}
}
@@ -586,8 +603,8 @@ class kolab_calendar
'title' => $rec['summary'],
'location' => $rec['location'],
'description' => $rec['body'],
- 'start' => $rec['start-date'],
- 'end' => $rec['end-date'],
+ 'start' => $dtstart,
+ 'end' => $dtend,
'allday' => $allday,
'recurrence' => $rrule,
'alarms' => $alarm_value . $alarm_unit,
@@ -611,7 +628,6 @@ class kolab_calendar
private function _from_rcube_event($event)
{
$priority_map = $this->priority_map;
- $tz_offset = $this->cal->gmt_offset;
$object = array(
// kolab => roundcube
@@ -620,8 +636,8 @@ class kolab_calendar
'location' => $event['location'],
'body' => $event['description'],
'categories' => $event['categories'],
- 'start-date' => $event['start'],
- 'end-date' => $event['end'],
+ 'start-date' => $event['start']->format('U'),
+ 'end-date' => $event['end']->format('U'),
'sensitivity' =>$this->sensitivity_map[$event['sensitivity']],
'show-time-as' => $event['free_busy'],
'priority' => $event['priority'],
@@ -655,7 +671,7 @@ class kolab_calendar
//Range Type
if ($ra['UNTIL']) {
$object['recurrence']['range-type'] = 'date';
- $object['recurrence']['range'] = $ra['UNTIL'];
+ $object['recurrence']['range'] = $ra['UNTIL']->format('U');
}
if ($ra['COUNT']) {
$object['recurrence']['range-type'] = 'number';
@@ -670,7 +686,7 @@ class kolab_calendar
}
else {
// use weekday of start date if empty
- $object['recurrence']['day'][] = strtolower(gmdate('l', $event['start'] + $tz_offset));
+ $object['recurrence']['day'][] = strtolower($event['start']->format('l'));
}
}
@@ -683,7 +699,7 @@ class kolab_calendar
$object['recurrence']['type'] = 'weekday';
}
else {
- $object['recurrence']['daynumber'] = date('j', $event['start']);
+ $object['recurrence']['daynumber'] = $event['start']->format('j');
$object['recurrence']['cycle'] = 'monthly';
$object['recurrence']['type'] = 'daynumber';
}
@@ -692,7 +708,7 @@ class kolab_calendar
//yearly
if ($ra['FREQ'] == 'YEARLY') {
if (!$ra['BYMONTH'])
- $ra['BYMONTH'] = gmdate('n', $event['start'] + $tz_offset);
+ $ra['BYMONTH'] = $event['start']->format('n');
$object['recurrence']['cycle'] = 'yearly';
$object['recurrence']['month'] = $this->month_map[intval($ra['BYMONTH'])];
@@ -704,29 +720,34 @@ class kolab_calendar
}
else {
$object['recurrence']['type'] = 'monthday';
- $object['recurrence']['daynumber'] = gmdate('j', $event['start'] + $tz_offset);
+ $object['recurrence']['daynumber'] = $event['start']->format('j');
}
}
//exclusions
foreach ((array)$ra['EXDATE'] as $excl) {
- $object['recurrence']['exclusion'][] = gmdate('Y-m-d', $excl + $tz_offset);
+ $object['recurrence']['exclusion'][] = $excl->format('Y-m-d');
}
}
// whole day event
if ($event['allday']) {
- $object['end-date'] += 12 * 3600; // end is at 13:00 => jump to the next day
- $object['end-date'] += $tz_offset - date('Z'); // shift 00 times from user's timezone to server's timezone
- $object['start-date'] += $tz_offset - date('Z'); // because Horde_Kolab_Format_Date::encodeDate() uses strftime()
+ // shift times from user's timezone to server's timezone
+ // because Horde_Kolab_Format_Date::encodeDate() uses strftime()
+ $server_tz = new DateTimeZone(date_default_timezone_get());
+ $event['start']->setTimezone($server_tz);
+ $event['end']->setTimezone($server_tz);
+
+ $event['start']->setTime(0,0,0);
+ $event['end']->setTime(0,0,0);
// create timestamps at exactly 00:00. This is also needed for proper re-interpretation in _to_rcube_event() after updating an event
- $object['start-date'] = mktime(0,0,0, date('n', $object['start-date']), date('j', $object['start-date']), date('Y', $object['start-date']));
- $object['end-date'] = mktime(0,0,0, date('n', $object['end-date']), date('j', $object['end-date']), date('Y', $object['end-date']));
+ $object['start-date'] = mktime(0,0,0, $event['start']->format('n'), $event['start']->format('j'), $event['start']->format('Y'));
+ $object['end-date'] = mktime(0,0,0, $event['end']->format('n'), $event['end']->format('j'), $event['end']->format('Y')) + 86400;
// sanity check: end date is same or smaller than start
if (date('Y-m-d', $object['end-date']) <= date('Y-m-d', $object['start-date']))
- $object['end-date'] = mktime(13,0,0, date('n', $object['start-date']), date('j', $object['start-date']), date('Y', $object['start-date'])) + 86400;
+ $object['end-date'] = mktime(13,0,0, $event['start']->format('n'), $event['start']->format('j'), $event['start']->format('Y')) + 86400;
$object['_is_all_day'] = 1;
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 83fd69d..de9f99e 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -794,7 +794,7 @@ class kolab_driver extends calendar_driver
foreach ($calendar->list_events($time, $time + 86400 * 365) as $e) {
// add to list if alarm is set
- if ($e['_alarm'] && ($notifyat = $e['start'] - $e['_alarm'] * 60) <= $time) {
+ if ($e['_alarm'] && ($notifyat = $e['start']->format('U') - $e['_alarm'] * 60) <= $time) {
$id = $e['id'];
$events[$id] = $e;
$events[$id]['notifyat'] = $notifyat;
diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index d2ec2c9..b9b9889 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -153,8 +153,8 @@ class calendar_ical
'uid' => $ve->getAttributeDefault('UID'),
'changed' => $ve->getAttributeDefault('DTSTAMP', 0),
'title' => $ve->getAttributeDefault('SUMMARY'),
- 'start' => $ve->getAttribute('DTSTART'),
- 'end' => $ve->getAttribute('DTEND'),
+ 'start' => $this->_date2time($ve->getAttribute('DTSTART')),
+ 'end' => $this->_date2time($ve->getAttribute('DTEND')),
// set defaults
'free_busy' => 'busy',
'priority' => 0,
@@ -162,15 +162,16 @@ class calendar_ical
);
// check for all-day dates
- if (is_array($event['start'])) {
- // create timestamp at 12:00 in user's timezone
- $event['start'] = $this->_date2time($event['start']);
+ if (is_array($ve->getAttribute('DTSTART')))
$event['allday'] = true;
- }
- if (is_array($event['end'])) {
- $event['end'] = $this->_date2time($event['end']) - 23 * 3600;
- }
-
+
+ if ($event['allday'])
+ $event['end'] = new DateTime('@'.($event['end']->format('U') - 23*3600));
+
+ // assign current timezone to event start/end
+ $event['start']->setTimezone($this->cal->user_timezone);
+ $event['end']->setTimezone($this->cal->user_timezone);
+
// map other attributes to internal fields
$_attendees = array();
foreach ($ve->getAllAttributes() as $attr) {
@@ -225,9 +226,15 @@ class calendar_ical
$params[$k] = $v;
}
if ($params['UNTIL'])
- $params['UNTIL'] = $ve->_parseDateTime($params['UNTIL']);
+ $params['UNTIL'] = date_create($params['UNTIL']);
if (!$params['INTERVAL'])
$params['INTERVAL'] = 1;
+ if ($params['EXDATE']) {
+ $exdates = array();
+ foreach (explode(',', $params['EXDATE']) as $excl)
+ $exdates[] = new DateTime($params['EXDATE'], $this->cal->user_timezone);
+ $params['EXDATE'] = $exdates;
+ }
$event['recurrence'] = $params;
break;
@@ -319,10 +326,14 @@ class calendar_ical
private function _date2time($prop)
{
// create timestamp at 12:00 in user's timezone
- return is_array($prop) ? strtotime(sprintf('%04d%02d%02dT120000%s', $prop['year'], $prop['month'], $prop['mday'], $this->timezone)) : $prop;
+ if (is_array($prop))
+ return date_create(sprintf('%04d%02d%02dT120000', $prop['year'], $prop['month'], $prop['mday']), $this->cal->user_timezone);
+ else if (is_numeric($prop))
+ return date_create('@'.$prop);
+
+ return $prop;
}
-
/**
* Free resources by clearing member vars
*/
@@ -358,15 +369,16 @@ class calendar_ical
foreach ($events as $event) {
$vevent = "BEGIN:VEVENT" . self::EOL;
$vevent .= "UID:" . self::escpape($event['uid']) . self::EOL;
- $vevent .= "DTSTAMP:" . gmdate('Ymd\THis\Z', $event['changed'] ? $event['changed'] : time()) . self::EOL;
+ $vevent .= $this->format_datetime("DTSTAMP", $event['changed'] ?: new DateTime(), false, true) . self::EOL;
// correctly set all-day dates
if ($event['allday']) {
- $vevent .= "DTSTART;VALUE=DATE:" . gmdate('Ymd', $event['start'] + $this->cal->gmt_offset) . self::EOL;
- $vevent .= "DTEND;VALUE=DATE:" . gmdate('Ymd', $event['end'] + $this->cal->gmt_offset + 86400) . self::EOL; // ends the next day
+ $event['end'] = new DateTime('@'.($event['end']->format('U') + 86400)); // ends the next day
+ $vevent .= $this->format_datetime("DTSTART", $event['start'], true) . self::EOL;
+ $vevent .= $this->format_datetime("DTEND", $event['end'], true) . self::EOL;
}
else {
- $vevent .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . self::EOL;
- $vevent .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . self::EOL;
+ $vevent .= $this->format_datetime("DTSTART", $event['start'], false) . self::EOL;
+ $vevent .= $this->format_datetime("DTEND", $event['end'], false) . self::EOL;
}
$vevent .= "SUMMARY:" . self::escpape($event['title']) . self::EOL;
$vevent .= "DESCRIPTION:" . self::escpape($event['description']) . self::EOL;
@@ -429,7 +441,27 @@ class calendar_ical
// fold lines to 75 chars
return rcube_vcard::rfc2425_fold($ical);
}
-
+
+ private function format_datetime($attr, $dt, $dateonly = false, $utc = false)
+ {
+ if (is_numeric($dt))
+ $dt = new DateTime('@'.$dt);
+
+ if ($utc)
+ $dt->setTimezone(new DateTimeZone('UTC'));
+
+ if ($dateonly) {
+ return $attr . ';VALUE=DATE:' . $dt->format('Ymd');
+ }
+ else {
+ // <ATTR>;TZID=Europe/Zurich:20120706T210000
+ $tz = $dt->getTimezone();
+ $tzname = $tz ? $tz->getName() : 'UTC';
+ $tzid = $tzname != 'UTC' && $tzname != '+00:00' ? ';TZID=' . $tzname : '';
+ return $attr . $tzid . ':' . $dt->format('Ymd\THis' . ($tzid ? '' : '\Z'));
+ }
+ }
+
private function escpape($str)
{
return preg_replace('/(?<!\\\\)([\:\;\,\\n\\r])/', '\\\$1', $str);
diff --git a/plugins/calendar/lib/calendar_recurrence.php b/plugins/calendar/lib/calendar_recurrence.php
index fac87fb..78ba7ef 100644
--- a/plugins/calendar/lib/calendar_recurrence.php
+++ b/plugins/calendar/lib/calendar_recurrence.php
@@ -35,7 +35,6 @@ class calendar_recurrence
private $event;
private $engine;
private $tz_offset = 0;
- private $dst_start = false;
private $hour = 0;
/**
@@ -47,44 +46,42 @@ class calendar_recurrence
function __construct($cal, $event)
{
$this->cal = $cal;
+ $dtstart = clone $event['start'];
+ $dtstart->setTimezone($cal->user_timezone);
// use Horde classes to compute recurring instances
// TODO: replace with something that has less than 6'000 lines of code
require_once($this->cal->home . '/lib/Horde_Date_Recurrence.php');
$this->event = $event;
- $this->engine = new Horde_Date_Recurrence($event['start']);
+ $this->engine = new Horde_Date_Recurrence($dtstart->format('U'));
$this->engine->fromRRule20(calendar::to_rrule($event['recurrence']));
if (is_array($event['recurrence']['EXDATE'])) {
foreach ($event['recurrence']['EXDATE'] as $exdate)
- $this->engine->addException(date('Y', $exdate), date('n', $exdate), date('j', $exdate));
+ $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j'));
}
$this->tz_offset = $event['allday'] ? $this->cal->gmt_offset - date('Z') : 0;
- $this->next = new Horde_Date($event['start'] + $this->tz_offset); # shift all-day times to server timezone because computation operates in local TZ
- $this->dst_start = $this->next->format('I');
+ $this->next = new Horde_Date($dtstart->format('U'));
$this->hour = $this->next->hour;
}
/**
* Get timestamp of the next occurence of this event
*
- * @return mixed Unix timestamp or False if recurrence ended
+ * @return mixed DateTime or False if recurrence ended
*/
public function next_start()
{
$time = false;
if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) {
- # consider difference in daylight saving between base event and recurring instance
- $dst_diff = ($this->dst_start - $next->format('I')) * 3600;
# fix time for all-day events
if ($this->event['allday']) {
$next->hour = $this->hour;
$next->min = 0;
- $dst_diff = 0;
}
- $time = $next->timestamp() - $this->tz_offset - $dst_diff;
+ $time = new DateTime($next->iso8601DateTime(), $this->cal->user_timezone);
$this->next = $next;
}
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 477d1e3..f381e48 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -337,9 +337,11 @@ class calendar_ui
$input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time', 'size' => 6));
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset'));
- foreach (array('-M','-H','-D','+M','+H','+D','@') as $trigger)
+ foreach (array('-M','-H','-D','+M','+H','+D') as $trigger)
$select_offset->add($this->cal->gettext('trigger' . $trigger), $trigger);
-
+ if (!is_a($this->cal->driver, 'kolab_driver'))
+ $select_offset->add($this->cal->gettext('trigger@'), '@');
+
// pre-set with default values from user settings
$preset = calendar::parse_alaram_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
$hidden = array('style' => 'display:none');
More information about the commits
mailing list