mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-01-27 00:33:34 +00:00
Merge branch 'JJJollyjim/davical-refactor-freebusy'
This commit is contained in:
commit
bcdf59ae2e
@ -29,6 +29,7 @@ test:
|
||||
artifacts:
|
||||
paths:
|
||||
- testing/report.xml
|
||||
- apache2_log/*
|
||||
reports:
|
||||
junit: testing/report.xml
|
||||
when:
|
||||
@ -68,4 +69,5 @@ test:
|
||||
- apache2ctl start
|
||||
- useradd testrunner
|
||||
- cd testing && su testrunner -c 'IS_CI=yes ALLSUITES="regression-suite binding carddav scheduling" ./run_regressions.sh all x'
|
||||
|
||||
after_script:
|
||||
- cp -r /var/log/apache2 apache2_log
|
||||
|
||||
@ -167,8 +167,8 @@ CREATE TABLE calendar_item (
|
||||
completed TIMESTAMP WITH TIME ZONE,
|
||||
dav_id INT8 UNIQUE,
|
||||
collection_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
|
||||
first_instance_start TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
|
||||
last_instance_end TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
|
||||
first_instance_start TIMESTAMP WITH TIME ZONE DEFAULT NULL,
|
||||
last_instance_end TIMESTAMP WITH TIME ZONE DEFAULT NULL,
|
||||
|
||||
-- Cascade updates / deletes from the caldav_data table
|
||||
CONSTRAINT caldav_exists FOREIGN KEY ( user_no, dav_name )
|
||||
@ -489,4 +489,4 @@ CREATE SEQUENCE metrics_count_delticket;
|
||||
CREATE SEQUENCE metrics_count_bind;
|
||||
CREATE SEQUENCE metrics_count_unknown;
|
||||
|
||||
SELECT new_db_revision(1,3,2, 'Luty' );
|
||||
SELECT new_db_revision(1,3,3, 'Marzec' );
|
||||
|
||||
23
dba/patches/1.3.3.sql
Normal file
23
dba/patches/1.3.3.sql
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
-- Notable enhancement: change first_instance_start and last_instance_end to be timezone-aware
|
||||
|
||||
BEGIN;
|
||||
SELECT check_db_revision(1,3,2);
|
||||
|
||||
ALTER TABLE calendar_item
|
||||
ALTER COLUMN first_instance_start
|
||||
TYPE TIMESTAMP WITH TIME ZONE
|
||||
-- I don't believe the column has ever been populated, but if it has, we want
|
||||
-- to throw away that data anyway because we don't know its timezone
|
||||
USING NULL::timestamptz;
|
||||
|
||||
ALTER TABLE calendar_item
|
||||
ALTER COLUMN last_instance_end
|
||||
TYPE TIMESTAMP WITH TIME ZONE
|
||||
USING NULL::timestamptz; -- As above
|
||||
|
||||
-- http://blogs.transparent.com/polish/names-of-the-months-and-their-meaning/
|
||||
SELECT new_db_revision(1,3,3, 'Marzec' );
|
||||
|
||||
COMMIT;
|
||||
ROLLBACK;
|
||||
@ -1295,6 +1295,15 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the timezone for this collection, or the collection containing this resource
|
||||
*/
|
||||
function timezone_name() {
|
||||
if ( !isset($this->collection) ) $this->FetchCollection();
|
||||
return $this->collection->timezone;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the database row for this resource
|
||||
*/
|
||||
|
||||
@ -305,6 +305,18 @@ class RepeatRuleDateTime extends DateTime {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function withFallbackTzid( $date, $fallback_tzid ) {
|
||||
// Floating times or dates (either VALUE=DATE or with no TZID) can default to the collection's tzid, if one is set
|
||||
|
||||
if ($date->GetParameterValue('VALUE') == 'DATE' && isset($fallback_tzid)) {
|
||||
return new RepeatRuleDateTime($date->Value()."T000000", new RepeatRuleTimeZone($fallback_tzid));
|
||||
} else if ($date->GetParameterValue('TZID') === null && isset($fallback_tzid)) {
|
||||
return new RepeatRuleDateTime($date->Value(), new RepeatRuleTimeZone($fallback_tzid));
|
||||
} else {
|
||||
return new RepeatRuleDateTime($date);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function __toString() {
|
||||
return (string)parent::format(self::$Format) . ' ' . parent::getTimeZone()->getName();
|
||||
@ -1144,7 +1156,7 @@ function rdate_expand( $dtstart, $property, $component, $range_end = null, $is_d
|
||||
*
|
||||
* @return array An array keyed on the UTC dates, referring to the component
|
||||
*/
|
||||
function rrule_expand( $dtstart, $property, $component, $range_end, $is_date=null, $return_floating_times=false ) {
|
||||
function rrule_expand( $dtstart, $property, $component, $range_end, $is_date=null, $return_floating_times=false, $fallback_tzid=null ) {
|
||||
$expansion = array();
|
||||
|
||||
$recur = $component->GetProperty($property);
|
||||
@ -1153,7 +1165,7 @@ function rrule_expand( $dtstart, $property, $component, $range_end, $is_date=nul
|
||||
|
||||
$this_start = $component->GetProperty('DTSTART');
|
||||
if ( isset($this_start) ) {
|
||||
$this_start = new RepeatRuleDateTime($this_start);
|
||||
$this_start = RepeatRuleDateTime::withFallbackTzid($this_start, $fallback_tzid);
|
||||
}
|
||||
else {
|
||||
$this_start = clone($dtstart);
|
||||
@ -1406,12 +1418,12 @@ function expand_event_instances( vComponent $vResource, $range_start = null, $ra
|
||||
* @throws Exception (1) When DTSTART is not present but the RFC says MUST and (2) when we get an unsupported component
|
||||
* @return RepeatRuleDateRange
|
||||
*/
|
||||
function getComponentRange(vComponent $comp) {
|
||||
function getComponentRange(vComponent $comp, string $fallback_tzid = null) {
|
||||
$dtstart_prop = $comp->GetProperty('DTSTART');
|
||||
$duration_prop = $comp->GetProperty('DURATION');
|
||||
if ( isset($duration_prop) ) {
|
||||
if ( !isset($dtstart_prop) ) throw new Exception('Invalid '.$comp->GetType().' containing DURATION without DTSTART', 0);
|
||||
$dtstart = new RepeatRuleDateTime($dtstart_prop);
|
||||
$dtstart = RepeatRuleDateTime::withFallbackTzid($dtstart_prop, $fallback_tzid);
|
||||
$dtend = clone($dtstart);
|
||||
$dtend->modify(new Rfc5545Duration($duration_prop->Value()));
|
||||
}
|
||||
@ -1435,17 +1447,17 @@ function getComponentRange(vComponent $comp) {
|
||||
}
|
||||
|
||||
if ( isset($dtstart_prop) )
|
||||
$dtstart = new RepeatRuleDateTime($dtstart_prop);
|
||||
$dtstart = RepeatRuleDateTime::withFallbackTzid($dtstart_prop, $fallback_tzid);
|
||||
else
|
||||
$dtstart = null;
|
||||
|
||||
if ( isset($dtend_prop) )
|
||||
$dtend = new RepeatRuleDateTime($dtend_prop);
|
||||
$dtend = RepeatRuleDateTime::withFallbackTzid($dtend_prop, $fallback_tzid);
|
||||
else
|
||||
$dtend = null;
|
||||
|
||||
if ( isset($completed_prop) ) {
|
||||
$completed = new RepeatRuleDateTime($completed_prop);
|
||||
$completed = RepeatRuleDateTime::withFallbackTzid($completed_prop, $fallback_tzid);
|
||||
if ( !isset($dtstart) || (isset($dtstart) && $completed < $dtstart) ) $dtstart = $completed;
|
||||
if ( !isset($dtend) || (isset($dtend) && $completed > $dtend) ) $dtend = $completed;
|
||||
}
|
||||
@ -1461,7 +1473,7 @@ function getComponentRange(vComponent $comp) {
|
||||
* @param object $vResource A vComponent which is a VCALENDAR containing components needing expansion
|
||||
* @return RepeatRuleDateRange Representing the range of time covered by the event.
|
||||
*/
|
||||
function getVCalendarRange( $vResource ) {
|
||||
function getVCalendarRange( $vResource, string $fallback_tzid = null ) {
|
||||
$components = $vResource->GetComponents();
|
||||
|
||||
$dtstart = null;
|
||||
@ -1471,7 +1483,7 @@ function getVCalendarRange( $vResource ) {
|
||||
$has_repeats = false;
|
||||
foreach( $components AS $k => $comp ) {
|
||||
if ( $comp->GetType() == 'VTIMEZONE' ) continue;
|
||||
$range = getComponentRange($comp);
|
||||
$range = getComponentRange($comp, $fallback_tzid);
|
||||
$dtstart = $range->from;
|
||||
if ( !isset($dtstart) ) continue;
|
||||
$duration = $range->getDuration();
|
||||
@ -1490,7 +1502,7 @@ function getVCalendarRange( $vResource ) {
|
||||
$range_end = new RepeatRuleDateTime();
|
||||
$range_end->modify('+150 years');
|
||||
}
|
||||
$instances += rrule_expand($dtstart, 'RRULE', $comp, $range_end);
|
||||
$instances += rrule_expand($dtstart, 'RRULE', $comp, $range_end, null, false, $fallback_tzid);
|
||||
$instances += rdate_expand($dtstart, 'RDATE', $comp, $range_end);
|
||||
foreach ( rdate_expand($dtstart, 'EXDATE', $comp, $range_end) AS $k => $v ) {
|
||||
unset($instances[$k]);
|
||||
|
||||
@ -219,16 +219,23 @@ class WritableCollection extends DAVResource {
|
||||
$calitem_params[':due'] = $first->GetPValue('DUE');
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
|
||||
$range = getVCalendarRange($vcal, $this->timezone_name());
|
||||
$calitem_params[':first_instance_start'] = isset($range->from) ? $range->from->UTC() : null;
|
||||
$calitem_params[':last_instance_end'] = isset($range->until) ? $range->until->UTC() : null;
|
||||
|
||||
if ( $create_resource ) {
|
||||
$sql = <<<EOSQL
|
||||
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
|
||||
dtstart, dtend, summary, location, class, transp,
|
||||
description, rrule, tz_id, last_modified, url, priority,
|
||||
created, due, percent_complete, status, collection_id )
|
||||
created, due, percent_complete, status, collection_id,
|
||||
first_instance_start, last_instance_end )
|
||||
VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp,
|
||||
:dtstart, $dtend, :summary, :location, :class, :transp,
|
||||
:description, :rrule, :tzid, :modified, :url, :priority,
|
||||
:created, :due, :percent_complete, :status, $collection_id )
|
||||
:created, :due, :percent_complete, :status, $collection_id,
|
||||
:first_instance_start, :last_instance_end)
|
||||
EOSQL;
|
||||
$sync_change = 201;
|
||||
}
|
||||
@ -237,7 +244,8 @@ EOSQL;
|
||||
UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
|
||||
dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location, class=:class, transp=:transp,
|
||||
description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
|
||||
created=:created, due=:due, percent_complete=:percent_complete, status=:status
|
||||
created=:created, due=:due, percent_complete=:percent_complete, status=:status,
|
||||
first_instance_start=:first_instance_start, last_instance_end=:last_instance_end
|
||||
WHERE user_no=:user_no AND dav_name=:dav_name
|
||||
EOSQL;
|
||||
$sync_change = 200;
|
||||
|
||||
@ -1215,6 +1215,10 @@ EOSQL;
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
|
||||
// Intentionally not populating first_instance_start and last_instance_end
|
||||
// here, As they may depend on the default tzid of the collection, which as
|
||||
// I understand it hasn't yet been set
|
||||
|
||||
if ( $inserting ) {
|
||||
$created = $first->GetPValue('CREATED');
|
||||
if ( $created == '00001231T000000Z' ) $created = '20001231T000000Z';
|
||||
@ -1596,7 +1600,11 @@ function write_resource( DAVResource $resource, $caldav_data, DAVResource $colle
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
|
||||
// force re-render the object (to get the same representation for all attendiees)
|
||||
$range = getVCalendarRange($vcal, $resource->timezone_name());
|
||||
$calitem_params[':first_instance_start'] = isset($range->from) ? $range->from->UTC() : null;
|
||||
$calitem_params[':last_instance_end'] = isset($range->until) ? $range->until->UTC() : null;
|
||||
|
||||
// force re-render the object (to get the same representation for all attendees)
|
||||
$vcal->Render(null, true);
|
||||
|
||||
if ( !$collection->IsSchedulingCollection() ) {
|
||||
@ -1632,11 +1640,13 @@ function write_resource( DAVResource $resource, $caldav_data, DAVResource $colle
|
||||
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
|
||||
dtstart, dtend, summary, location, class, transp,
|
||||
description, rrule, tz_id, last_modified, url, priority,
|
||||
created, due, percent_complete, status, collection_id )
|
||||
created, due, percent_complete, status, collection_id,
|
||||
first_instance_start, last_instance_end )
|
||||
VALUES ( :user_no, :dav_name, :dav_id, :etag, :uid, :dtstamp,
|
||||
:dtstart, $dtend, :summary, :location, :class, :transp,
|
||||
:description, :rrule, :tzid, :modified, :url, :priority,
|
||||
:created, :due, :percent_complete, :status, :collection_id )
|
||||
:created, :due, :percent_complete, :status, $collection_id,
|
||||
:first_instance_start, :last_instance_end)
|
||||
EOSQL;
|
||||
$sync_change = 201;
|
||||
$calitem_params[':collection_id'] = $collection_id;
|
||||
@ -1650,7 +1660,8 @@ UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
|
||||
dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location,
|
||||
class=:class, transp=:transp, description=:description, rrule=:rrule,
|
||||
tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
|
||||
due=:due, percent_complete=:percent_complete, status=:status
|
||||
due=:due, percent_complete=:percent_complete, status=:status,
|
||||
first_instance_start=:first_instance_start, last_instance_end=:last_instance_end
|
||||
WHERE dav_id=:dav_id
|
||||
EOSQL;
|
||||
$sync_change = 200;
|
||||
|
||||
@ -20,7 +20,10 @@ function get_freebusy( $path_match, $range_start, $range_end, $bin_privs = null
|
||||
}
|
||||
$params = array( ':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
|
||||
$where = ' WHERE caldav_data.dav_name ~ :path_match ';
|
||||
$where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) ';
|
||||
$where .= "AND (";
|
||||
$where .= " (calendar_item.first_instance_start <= :end AND (:start <= calendar_item.last_instance_end OR calendar_item.last_instance_end IS NULL)) ";
|
||||
$where .= " OR (calendar_item.first_instance_start IS NULL AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end)) ";
|
||||
$where .= ") ";
|
||||
$where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) ";
|
||||
$where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) ";
|
||||
$where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) ";
|
||||
@ -66,13 +69,7 @@ function get_freebusy( $path_match, $range_start, $range_end, $bin_privs = null
|
||||
}
|
||||
|
||||
// Floating dtstarts (either VALUE=DATE or with no TZID) can default to the collection's tzid, if one is set
|
||||
if ($start_date->GetParameterValue('VALUE') == 'DATE' && isset($collection_tzid)) {
|
||||
$start_date = new RepeatRuleDateTime($start_date->Value()."T000000", new RepeatRuleTimeZone($collection_tzid));
|
||||
} else if ($start_date->GetParameterValue('TZID') === null && isset($collection_tzid)) {
|
||||
$start_date = new RepeatRuleDateTime($start_date->Value(), new RepeatRuleTimeZone($collection_tzid));
|
||||
} else {
|
||||
$start_date = new RepeatRuleDateTime($start_date);
|
||||
}
|
||||
$start_date = RepeatRuleDateTime::withFallbackTzid( $start_date, $collection_tzid );
|
||||
|
||||
$duration = $v->GetProperty('DURATION');
|
||||
$duration = ( !isset($duration) ? 'P1D' : $duration->Value());
|
||||
|
||||
119
testing/phpunit/RangeTest.php
Normal file
119
testing/phpunit/RangeTest.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
set_include_path(get_include_path() . PATH_SEPARATOR . '/usr/share/awl/inc' . PATH_SEPARATOR . 'inc');
|
||||
|
||||
require_once('RRule.php');
|
||||
require_once('vCalendar.php');
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class RangeTest extends TestCase
|
||||
{
|
||||
public function testGetVCalendarRange() {
|
||||
$cal = new vCalendar("BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Asia/Baku
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0400
|
||||
TZOFFSETTO:+0400
|
||||
TZNAME:+04
|
||||
DTSTART:19700101T000000
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20181218T045608Z
|
||||
LAST-MODIFIED:20181218T045951Z
|
||||
DTSTAMP:20181218T045951Z
|
||||
UID:8eeee169-420f-4645-9546-9ea8293a1c6d
|
||||
SUMMARY:Blep
|
||||
RRULE:FREQ=DAILY;UNTIL=20190102T050000Z;BYDAY=MO,TU,WE,TH,FR
|
||||
DTSTART;TZID=Asia/Baku:20181226T090000
|
||||
DTEND;TZID=Asia/Baku:20181226T112000
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
END:VCALENDAR");
|
||||
|
||||
$range = getVCalendarRange($cal);
|
||||
self::assertEquals("20181226T050000Z", (string) $range->from->UTC());
|
||||
self::assertEquals("20190102T072000Z", (string) $range->until->UTC());
|
||||
|
||||
// TZ is specified in the event, so this should be unaffected by passing in a fallback timezone:
|
||||
$range = getVCalendarRange($cal, "Asia/Baku");
|
||||
self::assertEquals("20181226T050000Z", (string) $range->from->UTC());
|
||||
self::assertEquals("20190102T072000Z", (string) $range->until->UTC());
|
||||
}
|
||||
|
||||
public function testGetVCalendarRangeTwoDayAllDay() {
|
||||
$cal = new vCalendar("BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20181218T052603Z
|
||||
LAST-MODIFIED:20181218T052734Z
|
||||
DTSTAMP:20181218T052734Z
|
||||
UID:7e76efc0-b1ed-4b68-b0ed-9e34889c30bd
|
||||
SUMMARY:New Event
|
||||
RRULE:FREQ=WEEKLY;COUNT=3;BYDAY=TU
|
||||
DTSTART;VALUE=DATE:20181225
|
||||
DTEND;VALUE=DATE:20181227
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR");
|
||||
|
||||
$range = getVCalendarRange($cal, "Asia/Baku");
|
||||
self::assertEquals("20181224T200000Z", (string) $range->from->UTC());
|
||||
self::assertEquals("20190109T200000Z", (string) $range->until->UTC());
|
||||
}
|
||||
|
||||
public function testGetVCalendarRangeFloating() {
|
||||
// When interpreted as being in Greece, this event crosses the daylight savings boundary!
|
||||
// TODO deal with how that affects all-day events...
|
||||
$cal = new vCalendar("BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20181218T052603Z
|
||||
LAST-MODIFIED:20181218T052734Z
|
||||
DTSTAMP:20181218T052734Z
|
||||
UID:7e76efc0-b1ed-4b68-b0ed-9e34889c30bd
|
||||
SUMMARY:New Event
|
||||
RRULE:FREQ=MONTHLY;COUNT=4;INTERVAL=2;BYMONTHDAY=10
|
||||
DTSTART:20180411T140000
|
||||
DTEND:20180411T150000
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR");
|
||||
|
||||
$range = getVCalendarRange($cal, "Europe/Athens");
|
||||
self::assertEquals("20180411T110000Z", (string) $range->from->UTC());
|
||||
self::assertEquals("20181210T130000Z", (string) $range->until->UTC());
|
||||
}
|
||||
|
||||
public function testGetVCalendarRangeAllDayAcrossDST() {
|
||||
// When interpreted as being in Greece, this event crosses the daylight savings boundary!
|
||||
|
||||
$cal = new vCalendar("BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20181218T052603Z
|
||||
LAST-MODIFIED:20181218T052734Z
|
||||
DTSTAMP:20181218T052734Z
|
||||
UID:7e76efc0-b1ed-4b68-b0ed-9e34889c30bd
|
||||
SUMMARY:New Event
|
||||
RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=2;BYMONTHDAY=10
|
||||
DTSTART;VALUE=DATE:20180410
|
||||
DTEND;VALUE=DATE:20180411
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR");
|
||||
|
||||
$range = getVCalendarRange($cal, "Europe/Athens");
|
||||
self::assertEquals("20180409T210000Z", (string) $range->from->UTC());
|
||||
|
||||
// This is correctly at a different UTC time-of-day to the original, since DST has changed
|
||||
self::assertEquals("20181210T220000Z", (string) $range->until->UTC());
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
HTTP/1.1 200 OK
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
Content-Length: 14720
|
||||
Content-Length: 14764
|
||||
Content-Type: text/calendar;charset=UTF-8
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
@ -11,6 +11,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20060930T110000Z
|
||||
DTEND:correct
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
FREEBUSY:20061003T210000Z/20061003T220000Z
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
HTTP/1.1 200 OK
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
Content-Length: 14720
|
||||
Content-Length: 14764
|
||||
Content-Type: text/calendar;charset=UTF-8
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
@ -11,6 +11,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20060930T110000Z
|
||||
DTEND:20070630T115959Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
FREEBUSY:20061003T210000Z/20061003T220000Z
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
HTTP/1.1 200 OK
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
Content-Length: 14720
|
||||
Content-Length: 14764
|
||||
Content-Type: text/calendar;charset=UTF-8
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
@ -11,6 +11,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20060930T110000Z
|
||||
DTEND:20070630T115959Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
FREEBUSY:20061003T210000Z/20061003T220000Z
|
||||
|
||||
@ -14,6 +14,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20081021T110000Z
|
||||
DTEND:20081106T110000Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20081021T180000Z/20081022T040000Z
|
||||
FREEBUSY:20081022T180000Z/20081023T040000Z
|
||||
FREEBUSY:20081022T184500Z/20081022T193000Z
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
HTTP/1.1 200 OK
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
Content-Length: 14720
|
||||
Content-Length: 14764
|
||||
Content-Type: text/calendar;charset=UTF-8
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
@ -11,6 +11,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20060930T110000Z
|
||||
DTEND:20070630T115959Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
FREEBUSY:20061003T210000Z/20061003T220000Z
|
||||
|
||||
@ -14,6 +14,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:correct
|
||||
DTEND:correct
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20060930T210000Z/20060930T220000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
HTTP/1.1 200 OK
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
Content-Length: 14720
|
||||
Content-Length: 14764
|
||||
Content-Type: text/calendar;charset=UTF-8
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
@ -11,6 +11,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20060930T110000Z
|
||||
DTEND:20070630T115959Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20061001T210000Z/20061001T220000Z
|
||||
FREEBUSY:20061002T210000Z/20061002T220000Z
|
||||
FREEBUSY:20061003T210000Z/20061003T220000Z
|
||||
|
||||
@ -14,6 +14,7 @@ BEGIN:VFREEBUSY
|
||||
DTSTAMP:yyyymmddThhmmssZ
|
||||
DTSTART:20080730T110000Z
|
||||
DTEND:20080803T110000Z
|
||||
FREEBUSY:20031231T230000Z/20100217T000000Z
|
||||
FREEBUSY:20080730T190000Z/20080731T050000Z
|
||||
FREEBUSY:20080730T194500Z/20080730T203000Z
|
||||
FREEBUSY:20080730T210000Z/20080730T220000Z
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
The database is version XX currently at revision 1.3.2.
|
||||
The database is version XX currently at revision 1.3.3.
|
||||
No patches were applied.
|
||||
Supported locales updated.
|
||||
Updated view: dav_principal.sql applied.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user