Page MenuHomePhorge

D536.1775940095.diff
No OneTemporary

Authored By
Unknown
Size
25 KB
Referenced Files
None
Subscribers
None

D536.1775940095.diff

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -857,8 +857,8 @@
if ($success && $reload)
$this->rc->output->command('plugin.reload_view');
}
-
-
+
+
/**
* Dispatcher for event actions initiated by the client
*/
@@ -867,7 +867,7 @@
$action = rcube_utils::get_input_value('action', rcube_utils::INPUT_GPC);
$event = rcube_utils::get_input_value('e', rcube_utils::INPUT_POST, true);
$success = $reload = $got_msg = false;
-
+
// force notify if hidden + active
if ((int)$this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']) === 1)
$event['_notify'] = 1;
@@ -887,8 +887,10 @@
case "new":
// create UID for new event
$event['uid'] = $this->generate_uid();
- $this->write_preprocess($event, $action);
- if ($success = $this->driver->new_event($event)) {
+ if (!$this->write_preprocess($event, $action)) {
+ $got_msg = true;
+ }
+ else if ($success = $this->driver->new_event($event)) {
$event['id'] = $event['uid'];
$event['_savemode'] = 'all';
$this->cleanup_event($event);
@@ -898,8 +900,10 @@
break;
case "edit":
- $this->write_preprocess($event, $action);
- if ($success = $this->driver->edit_event($event)) {
+ if (!$this->write_preprocess($event, $action)) {
+ $got_msg = true;
+ }
+ else if ($success = $this->driver->edit_event($event)) {
$this->cleanup_event($event);
$this->event_save_success($event, $old, $action, $success);
}
@@ -907,19 +911,23 @@
break;
case "resize":
- $this->write_preprocess($event, $action);
- if ($success = $this->driver->resize_event($event)) {
+ if (!$this->write_preprocess($event, $action)) {
+ $got_msg = true;
+ }
+ else if ($success = $this->driver->resize_event($event)) {
$this->event_save_success($event, $old, $action, $success);
}
$reload = $event['_savemode'] ? 2 : 1;
break;
case "move":
- $this->write_preprocess($event, $action);
- if ($success = $this->driver->move_event($event)) {
+ if (!$this->write_preprocess($event, $action)) {
+ $got_msg = true;
+ }
+ else if ($success = $this->driver->move_event($event)) {
$this->event_save_success($event, $old, $action, $success);
}
- $reload = $success && $event['_savemode'] ? 2 : 1;
+ $reload = $success && $event['_savemode'] ? 2 : 1;
break;
case "remove":
@@ -1184,7 +1192,7 @@
// unlock client
$this->rc->output->command('plugin.unlock_saving');
- // update event object on the client or trigger a complete refretch if too complicated
+ // update event object on the client or trigger a complete refresh if too complicated
if ($reload) {
$args = array('source' => $event['calendar']);
if ($reload > 1)
@@ -1993,12 +2001,31 @@
// start/end is all we need for 'move' action (#1480)
if ($action == 'move') {
- return;
+ return true;
}
// convert the submitted recurrence settings
if (is_array($event['recurrence'])) {
$event['recurrence'] = $this->lib->from_client_recurrence($event['recurrence'], $event['start']);
+
+ // align start date with the first occurrence
+ if (!empty($event['recurrence']) && !empty($event['syncstart'])
+ && (empty($event['_savemode']) || $event['_savemode'] == 'all')
+ ) {
+ $next = $this->find_first_occurrence($event);
+
+ if (!$next) {
+ $this->rc->output->show_message('calendar.recurrenceerror', 'error');
+ return false;
+ }
+ else if ($event['start'] != $next) {
+ $diff = $event['start']->diff($event['end'], true);
+
+ $event['start'] = $next;
+ $event['end'] = clone $next;
+ $event['end']->add($diff);
+ }
+ }
}
// convert the submitted alarm values
@@ -2075,6 +2102,8 @@
$event['url'] = $event['vurl'];
unset($event['vurl']);
}
+
+ return true;
}
/**
@@ -3448,6 +3477,35 @@
}
/**
+ * Find first occurrence of a recurring event excluding start date
+ *
+ * @param array $event Event data (with 'start' and 'recurrence')
+ *
+ * @return DateTime Date of the first occurrence
+ */
+ public function find_first_occurrence($event)
+ {
+ // Make sure libkolab plugin is loaded in case of Kolab driver
+ $this->load_driver();
+
+ // Use libkolab to compute recurring events (and libkolab plugin)
+ // Horde-based fallback has many bugs
+ if (class_exists('kolabformat') && class_exists('kolabcalendaring') && class_exists('kolab_date_recurrence')) {
+ $object = kolab_format::factory('event', 3.0);
+ $object->set($event);
+
+ $recurrence = new kolab_date_recurrence($object);
+ }
+ else {
+ // fallback to libcalendaring (Horde-based) recurrence implementation
+ require_once(__DIR__ . '/lib/calendar_recurrence.php');
+ $recurrence = new calendar_recurrence($this, $event);
+ }
+
+ return $recurrence->first_occurrence();
+ }
+
+ /**
* Magic getter for public access to protected members
*/
public function __get($name)
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -678,7 +678,7 @@
var freebusy = $('#edit-free-busy').val(event.free_busy);
var priority = $('#edit-priority').val(event.priority);
var sensitivity = $('#edit-sensitivity').val(event.sensitivity);
-
+ var syncstart = $('#edit-recurrence-syncstart input');
var duration = Math.round((event.end.getTime() - event.start.getTime()) / 1000);
var startdate = $('#edit-startdate').val($.fullCalendar.formatDate(event.start, settings['date_format'])).data('duration', duration);
var starttime = $('#edit-starttime').val($.fullCalendar.formatDate(event.start, settings['time_format'])).show();
@@ -898,6 +898,9 @@
data._fromcalendar = event.calendar;
}
+ if (data.recurrence && syncstart.is(':checked'))
+ data.syncstart = 1;
+
update_event(action, data);
$dialog.dialog("close");
} // end click:
@@ -3974,8 +3977,15 @@
$('#edit-attendees-form .attendees-invitebox').show();
}
}
+
// reset autocompletion on tab change (#3389)
rcmail.ksearch_blur();
+
+ // display recurrence warning in recurrence tab only
+ if (tab == 'recurrence')
+ $('#edit-recurrence-frequency').change();
+ else
+ $('#edit-recurrence-syncstart').hide();
}
});
$('#edit-enddate').datepicker(datepicker_settings);
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -659,14 +659,7 @@
}
// use libkolab to compute recurring events
- if (class_exists('kolabcalendaring')) {
- $recurrence = new kolab_date_recurrence($object);
- }
- else {
- // fallback to local recurrence implementation
- require_once($this->cal->home . '/lib/calendar_recurrence.php');
- $recurrence = new calendar_recurrence($this->cal, $event);
- }
+ $recurrence = new kolab_date_recurrence($object);
$i = 0;
while ($next_event = $recurrence->next_instance()) {
@@ -717,7 +710,7 @@
if (++$i > 100000)
break;
}
-
+
return $events;
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -1784,16 +1784,16 @@
*/
private function get_recurrence_count($event, $dtstart)
{
- // use libkolab to compute recurring events
- if (class_exists('kolabcalendaring') && $event['_formatobj']) {
- $recurrence = new kolab_date_recurrence($event['_formatobj']);
- }
- else {
- // fallback to local recurrence implementation
- require_once($this->cal->home . '/lib/calendar_recurrence.php');
- $recurrence = new calendar_recurrence($this->cal, $event);
+ // load the given event data into a libkolabxml container
+ if (!$event['_formatobj']) {
+ $event_xml = new kolab_format_event();
+ $event_xml->set($event);
+ $event['_formatobj'] = $event_xml;
}
+ // use libkolab to compute recurring events
+ $recurrence = new kolab_date_recurrence($event['_formatobj']);
+
$count = 0;
while (($next_event = $recurrence->next_instance()) && $next_event['start'] <= $dtstart && $count < 1000) {
$count++;
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -92,6 +92,7 @@
$this->cal->register_handler('plugin.resource_calendar', array($this, 'resource_calendar'));
$this->cal->register_handler('plugin.attendees_freebusy_table', array($this, 'attendees_freebusy_table'));
$this->cal->register_handler('plugin.edit_attendees_notify', array($this, 'edit_attendees_notify'));
+ $this->cal->register_handler('plugin.edit_recurrence_sync', array($this, 'edit_recurrence_sync'));
$this->cal->register_handler('plugin.edit_recurring_warning', array($this, 'recurring_event_warning'));
$this->cal->register_handler('plugin.event_rsvp_buttons', array($this, 'event_rsvp_buttons'));
$this->cal->register_handler('plugin.angenda_options', array($this, 'angenda_options'));
@@ -473,7 +474,7 @@
}
/**
- *
+ * Render HTML for attendee notification warning
*/
function edit_attendees_notify($attrib = array())
{
@@ -482,6 +483,15 @@
}
/**
+ * Render HTML for recurrence option to align start date with the recurrence rule
+ */
+ function edit_recurrence_sync($attrib = array())
+ {
+ $checkbox = new html_checkbox(array('name' => '_start_sync', 'value' => 1));
+ return html::div($attrib, html::label(null, $checkbox->show(1) . ' ' . $this->cal->gettext('eventstartsync')));
+ }
+
+ /**
* Generate the form for recurrence settings
*/
function recurring_event_warning($attrib = array())
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -125,6 +125,7 @@
$labels['invitationsdeclined'] = 'Declined invitations';
$labels['changepartstat'] = 'Change participant status';
$labels['rsvpcomment'] = 'Invitation text';
+$labels['eventstartsync'] = 'Move the event start date to the first occurrence';
// agenda view
$labels['listrange'] = 'Range to display:';
@@ -267,6 +268,7 @@
$labels['futurevents'] = 'Future';
$labels['allevents'] = 'All';
$labels['saveasnew'] = 'Save as new';
+$labels['recurrenceerror'] = 'Unable to resolve recurrence rule for specified start date.';
// birthdays calendar
$labels['birthdays'] = 'Birthdays';
diff --git a/plugins/calendar/skins/larry/templates/eventedit.html b/plugins/calendar/skins/larry/templates/eventedit.html
--- a/plugins/calendar/skins/larry/templates/eventedit.html
+++ b/plugins/calendar/skins/larry/templates/eventedit.html
@@ -127,7 +127,8 @@
</div>
</form>
+ <roundcube:object name="plugin.edit_recurrence_sync" id="edit-recurrence-syncstart" class="event-dialog-message" style="display:none" />
<roundcube:object name="plugin.edit_attendees_notify" id="edit-attendees-notify" class="event-dialog-message" style="display:none" />
<roundcube:object name="plugin.edit_recurring_warning" class="event-dialog-message edit-recurring-warning" style="display:none" />
<div id="edit-localchanges-warning" class="event-dialog-message" style="display:none"><roundcube:label name="calendar.localchangeswarning" /></div>
</div>
\ No newline at end of file
diff --git a/plugins/libcalendaring/lib/libcalendaring_recurrence.php b/plugins/libcalendaring/lib/libcalendaring_recurrence.php
--- a/plugins/libcalendaring/lib/libcalendaring_recurrence.php
+++ b/plugins/libcalendaring/lib/libcalendaring_recurrence.php
@@ -152,4 +152,83 @@
return $last;
}
+ /**
+ * Find date/time of the first occurrence (excluding start date)
+ */
+ public function first_occurrence()
+ {
+ $start = clone $this->start;
+ $orig_start = clone $this->start;
+ $r = $this->recurrence;
+ $interval = intval($r['INTERVAL'] ?: 1);
+
+ switch ($this->recurrence['FREQ']) {
+ case 'WEEKLY':
+ if (empty($this->recurrence['BYDAY'])) {
+ return $start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}W"));
+ break;
+
+ case 'MONTHLY':
+ if (empty($this->recurrence['BYDAY']) && empty($this->recurrence['BYMONTHDAY'])) {
+ return $start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}M"));
+ break;
+
+ case 'YEARLY':
+ if (empty($this->recurrence['BYDAY']) && empty($this->recurrence['BYMONTH'])) {
+ return $start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}Y"));
+ break;
+
+ default:
+ return $start;
+ }
+
+ $r = $this->recurrence;
+ $r['INTERVAL'] = $interval;
+ if ($r['COUNT']) {
+ // Increase count so we do not stop the loop to early
+ $r['COUNT'] += 100;
+ }
+
+ // Create recurrence that starts in the past
+ $recurrence = new self($this->lib);
+ $recurrence->init($r, $start);
+
+ // find the first occurrence
+ $found = false;
+ while ($next = $recurrence->next()) {
+ $start = $next;
+ if ($next >= $orig_start) {
+ $found = true;
+ break;
+ }
+ }
+
+ if (!$found) {
+ rcube::raise_error(array(
+ 'file' => __FILE__,
+ 'line' => __LINE__,
+ 'message' => sprintf("Failed to find a first occurrence. Start: %s, Recurrence: %s",
+ $orig_start->format(DateTime::ISO8601), json_encode($r)),
+ ), true);
+
+ return null;
+ }
+
+ if ($start Instanceof Horde_Date) {
+ $start = $start->toDateTime();
+ }
+
+ $start->_dateonly = $this->dateonly;
+
+ return $start;
+ }
}
diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php
--- a/plugins/libkolab/lib/kolab_date_recurrence.php
+++ b/plugins/libkolab/lib/kolab_date_recurrence.php
@@ -138,4 +138,89 @@
return false;
}
+
+ /**
+ * Find date/time of the first occurrence (excluding start date)
+ */
+ public function first_occurrence()
+ {
+ $event = $this->object->to_array();
+ $start = clone $this->start;
+ $orig_start = clone $this->start;
+ $interval = intval($event['recurrence']['INTERVAL'] ?: 1);
+
+ switch ($event['recurrence']['FREQ']) {
+ case 'WEEKLY':
+ if (empty($event['recurrence']['BYDAY'])) {
+ return $orig_start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}W"));
+ break;
+
+ case 'MONTHLY':
+ if (empty($event['recurrence']['BYDAY']) && empty($event['recurrence']['BYMONTHDAY'])) {
+ return $orig_start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}M"));
+ break;
+
+ case 'YEARLY':
+ if (empty($event['recurrence']['BYDAY']) && empty($event['recurrence']['BYMONTH'])) {
+ return $orig_start;
+ }
+
+ $start->sub(new DateInterval("P{$interval}Y"));
+ break;
+
+ case 'DAILY':
+ if (!empty($event['recurrence']['BYMONTH'])) {
+ break;
+ }
+
+ default:
+ return $orig_start;
+ }
+
+ $event['start'] = $start;
+ $event['recurrence']['INTERVAL'] = $interval;
+ if ($event['recurrence']['COUNT']) {
+ // Increase count so we do not stop the loop to early
+ $event['recurrence']['COUNT'] += 100;
+ }
+
+ // Create recurrence that starts in the past
+ $object_type = $this->object instanceof kolab_format_task ? 'task' : 'event';
+ $object = kolab_format::factory($object_type, 3.0);
+ $object->set($event);
+ $recurrence = new self($object);
+
+ // find the first occurrence
+ $found = false;
+ while ($next = $recurrence->next_start()) {
+ $start = $next;
+ if ($next >= $orig_start) {
+ $found = true;
+ break;
+ }
+ }
+
+ if (!$found) {
+ rcube::raise_error(array(
+ 'file' => __FILE__,
+ 'line' => __LINE__,
+ 'message' => sprintf("Failed to find a first occurrence. Start: %s, Recurrence: %s",
+ $orig_start->format(DateTime::ISO8601), json_encode($event['recurrence'])),
+ ), true);
+
+ return null;
+ }
+
+ if ($orig_start->_dateonly) {
+ $start->_dateonly = true;
+ }
+
+ return $start;
+ }
}
diff --git a/plugins/libkolab/tests/kolab_date_recurrence.php b/plugins/libkolab/tests/kolab_date_recurrence.php
new file mode 100644
--- /dev/null
+++ b/plugins/libkolab/tests/kolab_date_recurrence.php
@@ -0,0 +1,213 @@
+<?php
+
+/**
+ * kolab_date_recurrence tests
+ *
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ *
+ * Copyright (C) 2017, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+class kolab_date_recurrence_test extends PHPUnit_Framework_TestCase
+{
+ function setUp()
+ {
+ $rcube = rcmail::get_instance();
+ $rcube->plugins->load_plugin('libkolab', true, true);
+ }
+
+ /**
+ * kolab_date_recurrence::first_occurrence()
+ *
+ * @dataProvider data_first_occurrence
+ */
+ function test_first_occurrence($recurrence_data, $start, $expected)
+ {
+ $start = new DateTime($start);
+ if (!empty($recurrence_data['UNTIL'])) {
+ $recurrence_data['UNTIL'] = new DateTime($recurrence_data['UNTIL']);
+ }
+
+ $event = array('start' => $start, 'recurrence' => $recurrence_data);
+ $object = kolab_format::factory('event', 3.0);
+ $object->set($event);
+
+ $recurrence = new kolab_date_recurrence($object);
+ $first = $recurrence->first_occurrence();
+
+ $this->assertEquals($expected, $first ? $first->format('Y-m-d H:i:s') : '');
+ }
+
+ /**
+ * Data for test_first_occurrence()
+ */
+ function data_first_occurrence()
+ {
+ // TODO: BYYEARDAY, BYWEEKNO, BYSETPOS, WKST
+
+ return array(
+ // non-recurring
+ array(
+ array(), // recurrence data
+ '2017-08-31 11:00:00', // start date
+ '2017-08-31 11:00:00', // expected result
+ ),
+ // daily
+ array(
+ array('FREQ' => 'DAILY', 'INTERVAL' => '1'), // recurrence data
+ '2017-08-31 11:00:00', // start date
+ '2017-08-31 11:00:00', // expected result
+ ),
+ // TODO: this one is not supported by the Calendar UI
+ array(
+ array('FREQ' => 'DAILY', 'INTERVAL' => '1', 'BYMONTH' => 1),
+ '2017-08-31 11:00:00',
+ '2018-01-01 11:00:00',
+ ),
+ // weekly
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-08-31 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1', 'BYDAY' => 'WE'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-09-06 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1', 'BYDAY' => 'TH'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-08-31 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1', 'BYDAY' => 'FR'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-09-01 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '2'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-08-31 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '3', 'BYDAY' => 'WE'),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-09-20 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1', 'BYDAY' => 'WE', 'COUNT' => 1),
+ '2017-08-31 11:00:00', // Thursday
+ '2017-09-06 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'WEEKLY', 'INTERVAL' => '1', 'BYDAY' => 'WE', 'UNTIL' => '2017-09-01'),
+ '2017-08-31 11:00:00', // Thursday
+ '',
+ ),
+ // monthly
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '1'),
+ '2017-09-08 11:00:00',
+ '2017-09-08 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '1', 'BYMONTHDAY' => '8,9'),
+ '2017-08-31 11:00:00',
+ '2017-09-08 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '1', 'BYMONTHDAY' => '8,9'),
+ '2017-09-08 11:00:00',
+ '2017-09-08 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '1', 'BYDAY' => '1WE'),
+ '2017-08-16 11:00:00',
+ '2017-09-06 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '1', 'BYDAY' => '-1WE'),
+ '2017-08-16 11:00:00',
+ '2017-08-30 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '2'),
+ '2017-09-08 11:00:00',
+ '2017-09-08 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'MONTHLY', 'INTERVAL' => '2', 'BYMONTHDAY' => '8'),
+ '2017-08-31 11:00:00',
+ '2017-09-08 11:00:00', // ??????
+ ),
+ // yearly
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1'),
+ '2017-08-16 11:00:00',
+ '2017-08-16 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1', 'BYMONTH' => '8'),
+ '2017-08-16 11:00:00',
+ '2017-08-16 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1', 'BYDAY' => '-1MO'),
+ '2017-08-16 11:00:00',
+ '2017-12-25 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1', 'BYMONTH' => '8', 'BYDAY' => '-1MO'),
+ '2017-08-16 11:00:00',
+ '2017-08-28 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1', 'BYMONTH' => '1', 'BYDAY' => '1MO'),
+ '2017-08-16 11:00:00',
+ '2018-01-01 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '1', 'BYMONTH' => '1,9', 'BYDAY' => '1MO'),
+ '2017-08-16 11:00:00',
+ '2017-09-04 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '2'),
+ '2017-08-16 11:00:00',
+ '2017-08-16 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '2', 'BYMONTH' => '8'),
+ '2017-08-16 11:00:00',
+ '2017-08-16 11:00:00',
+ ),
+ array(
+ array('FREQ' => 'YEARLY', 'INTERVAL' => '2', 'BYDAY' => '-1MO'),
+ '2017-08-16 11:00:00',
+ '2017-12-25 11:00:00',
+ ),
+ // on dates (FIXME: do we really expect the first occurrence to be on the start date?)
+ array(
+ array('RDATE' => array (new DateTime('2017-08-10 11:00:00 Europe/Warsaw'))),
+ '2017-08-01 11:00:00',
+ '2017-08-01 11:00:00',
+ ),
+ );
+ }
+
+}
+

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 11, 8:41 PM (12 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18861321
Default Alt Text
D536.1775940095.diff (25 KB)

Event Timeline