mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-08-29 18:26:05 +00:00
Handle default timezones in getVCalendarRange
Also includes some PHPUnit-based tests for this function!
This commit is contained in:
parent
0c006b5c7c
commit
cf7de16e59
@ -1156,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);
|
||||
@ -1165,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);
|
||||
@ -1418,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()));
|
||||
}
|
||||
@ -1447,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;
|
||||
}
|
||||
@ -1473,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;
|
||||
@ -1483,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();
|
||||
@ -1502,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]);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user