mirror of
https://github.com/rlanvin/php-rrule.git
synced 2025-02-21 10:54:14 +01:00
parent
e57f6eb0bc
commit
97d860e55e
@ -571,6 +571,9 @@ class RRule implements RRuleInterface
|
|||||||
|
|
||||||
$string = trim($string);
|
$string = trim($string);
|
||||||
|
|
||||||
|
$dtstart_type = 'date';
|
||||||
|
$rfc_date_regexp = '/\d{6}(T\d{6})?Z?/'; // a bit loose
|
||||||
|
|
||||||
foreach ( explode("\n", $string) as $line ) {
|
foreach ( explode("\n", $string) as $line ) {
|
||||||
$line = trim($line);
|
$line = trim($line);
|
||||||
if ( strpos($line,':') === false ) {
|
if ( strpos($line,':') === false ) {
|
||||||
@ -586,7 +589,7 @@ class RRule implements RRuleInterface
|
|||||||
array_splice($tmp,0,1);
|
array_splice($tmp,0,1);
|
||||||
foreach ( $tmp as $pair ) {
|
foreach ( $tmp as $pair ) {
|
||||||
if ( strpos($pair,'=') === false ) {
|
if ( strpos($pair,'=') === false ) {
|
||||||
throw new \InvalidArgumentException('Failed to parse RFC string, invlaid property parameters: '.$pair);
|
throw new \InvalidArgumentException('Failed to parse RFC string, invalid property parameters: '.$pair);
|
||||||
}
|
}
|
||||||
list($key,$value) = explode('=',$pair);
|
list($key,$value) = explode('=',$pair);
|
||||||
$property_params[$key] = $value;
|
$property_params[$key] = $value;
|
||||||
@ -595,15 +598,72 @@ class RRule implements RRuleInterface
|
|||||||
switch ( $property_name ) {
|
switch ( $property_name ) {
|
||||||
case 'DTSTART':
|
case 'DTSTART':
|
||||||
$tmp = null;
|
$tmp = null;
|
||||||
|
if ( ! preg_match($rfc_date_regexp, $property_value) ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid DTSTART property: date or date time format incorrect'
|
||||||
|
);
|
||||||
|
}
|
||||||
if ( isset($property_params['TZID']) ) {
|
if ( isset($property_params['TZID']) ) {
|
||||||
|
// TZID must only be specified if this is a date-time (see section 3.3.4 & 3.3.5 of RFC 5545)
|
||||||
|
if ( strpos($property_value, 'T') === false ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid DTSTART property: TZID should not be specified if there is no time component'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// The "TZID" property parameter MUST NOT be applied to DATE-TIME
|
||||||
|
// properties whose time values are specified in UTC.
|
||||||
|
if ( strpos($property_value, 'Z') !== false ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid DTSTART property: TZID must not be applied when time is specified in UTC'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$dtstart_type = 'tzid';
|
||||||
$tmp = new \DateTimeZone($property_params['TZID']);
|
$tmp = new \DateTimeZone($property_params['TZID']);
|
||||||
}
|
}
|
||||||
|
elseif ( strpos($property_value, 'T') !== false ) {
|
||||||
|
if ( strpos($property_value, 'Z') === false ) {
|
||||||
|
$dtstart_type = 'localtime'; // no timezone
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$dtstart_type = 'utc';
|
||||||
|
}
|
||||||
|
}
|
||||||
$parts['DTSTART'] = new \DateTime($property_value, $tmp);
|
$parts['DTSTART'] = new \DateTime($property_value, $tmp);
|
||||||
break;
|
break;
|
||||||
case 'RRULE':
|
case 'RRULE':
|
||||||
foreach ( explode(';',$property_value) as $pair ) {
|
foreach ( explode(';',$property_value) as $pair ) {
|
||||||
list($key, $value) = explode('=', $pair);
|
list($key, $value) = explode('=', $pair);
|
||||||
if ( $key === 'UNTIL' ) {
|
if ( $key === 'UNTIL' ) {
|
||||||
|
if ( ! preg_match($rfc_date_regexp, $value) ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid DTSTART property: date or date time format incorrect'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch ( $dtstart_type ) {
|
||||||
|
case 'date':
|
||||||
|
if ( strpos($value, 'T') !== false) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid UNTIL property: The value of the UNTIL rule part MUST be a date if DTSTART is a date.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'localtime':
|
||||||
|
if ( strpos($value, 'T') === false || strpos($value, 'Z') !== false ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid UNTIL property: if the "DTSTART" property is specified as a date with local time, then the UNTIL rule part MUST also be specified as a date with local time'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'tzid':
|
||||||
|
case 'utc':
|
||||||
|
if ( strpos($value, 'T') === false || strpos($value, 'Z') === false ) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid UNTIL property: if the "DTSTART" property is specified as a date with UTC time or a date with local time and time zone reference, then the UNTIL rule part MUST be specified as a date with UTC time.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$value = new \DateTime($value);
|
$value = new \DateTime($value);
|
||||||
}
|
}
|
||||||
$parts[$key] = $value;
|
$parts[$key] = $value;
|
||||||
|
@ -15,6 +15,8 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array(array()),
|
array(array()),
|
||||||
|
array(array('FOOBAR' => 'DAILY')),
|
||||||
|
|
||||||
array(array('FREQ' => 'foobar')),
|
array(array('FREQ' => 'foobar')),
|
||||||
array(array('FREQ' => 'DAILY', 'INTERVAL' => -1)),
|
array(array('FREQ' => 'DAILY', 'INTERVAL' => -1)),
|
||||||
array(array('FREQ' => 'DAILY', 'UNTIL' => 'foobar')),
|
array(array('FREQ' => 'DAILY', 'UNTIL' => 'foobar')),
|
||||||
@ -1688,7 +1690,7 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Other tests
|
// RFC Strings
|
||||||
|
|
||||||
public function rfcStrings()
|
public function rfcStrings()
|
||||||
{
|
{
|
||||||
@ -1713,8 +1715,14 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1,1'),
|
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1,1'),
|
||||||
array(' DTSTART;TZID=America/New_York:19970512T090000
|
array(' DTSTART;TZID=America/New_York:19970512T090000
|
||||||
RRULE:FREQ=YEARLY;BYWEEKNO=20,30,40;BYDAY=MO'),
|
RRULE:FREQ=YEARLY;BYWEEKNO=20,30,40;BYDAY=MO'),
|
||||||
array(' DTSTART;TZID=America/New_York:19970512T090000
|
array('DTSTART;TZID=America/New_York:19970512T090000
|
||||||
RRULE:FREQ=YEARLY;BYYEARDAY=1,-1,10,-50;BYDAY=MO')
|
RRULE:FREQ=YEARLY;BYYEARDAY=1,-1,10,-50;BYDAY=MO'),
|
||||||
|
array('DTSTART:19970512T090000Z
|
||||||
|
RRULE:FREQ=YEARLY'),
|
||||||
|
array('DTSTART:19970512T090000
|
||||||
|
RRULE:FREQ=YEARLY'),
|
||||||
|
array('DTSTART:19970512
|
||||||
|
RRULE:FREQ=YEARLY'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,11 +1732,56 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testRfcStrings($str)
|
public function testRfcStrings($str)
|
||||||
{
|
{
|
||||||
$rule = new RRule($str);
|
$rule = new RRule($str);
|
||||||
|
|
||||||
// test that parsing the string produces the same result
|
// test that parsing the string produces the same result
|
||||||
// as generating the string from a rule
|
// as generating the string from a rule
|
||||||
$this->assertEquals($rule, new RRule($rule->rfcString()));
|
$this->assertEquals($rule, new RRule($rule->rfcString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function invalidRfcStrings()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
// test invalid date formats
|
||||||
|
array('DTSTART:2006-06-24
|
||||||
|
RRULE:FREQ=DAILY'),
|
||||||
|
array('DTSTART:2006-06-24 12:00:00
|
||||||
|
RRULE:FREQ=DAILY'),
|
||||||
|
array('DTSTART:20060624
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=2006-06-24'),
|
||||||
|
|
||||||
|
// test combinations of DTSTART and UNTIL which are invalid
|
||||||
|
array('DTSTART;TZID=Australia/Sydney:20160624
|
||||||
|
RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20160628'),
|
||||||
|
array('DTSTART;TZID=America/New_York:19970512T090000Z
|
||||||
|
RRULE:FREQ=YEARLY'),
|
||||||
|
array('DTSTART;TZID=America/New_York:19970512T090000
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512'),
|
||||||
|
array('DTSTART;TZID=America/New_York:19970512T090000
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512T090000'),
|
||||||
|
array('DTSTART:19970512T090000
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512'),
|
||||||
|
array('DTSTART:19970512T090000Z
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512'),
|
||||||
|
array('DTSTART:19970512
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512T090000'),
|
||||||
|
array('DTSTART:19970512
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=19970512T090000Z'),
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException InvalidArgumentException
|
||||||
|
* @dataProvider invalidRfcStrings
|
||||||
|
*/
|
||||||
|
public function testInvalidRfcStrings($str)
|
||||||
|
{
|
||||||
|
$rule = new RRule($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Timezone
|
||||||
|
|
||||||
public function testTimezoneIsKeptIdentical()
|
public function testTimezoneIsKeptIdentical()
|
||||||
{
|
{
|
||||||
$rrule = new RRule(array(
|
$rrule = new RRule(array(
|
||||||
@ -1812,6 +1865,56 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertTrue($rrule->occursAt(date_create('2015-12-02 07:00:00',new DateTimeZone('UTC'))), 'During winter time, Europe/Helsinki is UTC+2 (uncached)');
|
$this->assertTrue($rrule->occursAt(date_create('2015-12-02 07:00:00',new DateTimeZone('UTC'))), 'During winter time, Europe/Helsinki is UTC+2 (uncached)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rulesWithMismatchedTimezones()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
array('DTSTART' => new DateTime('20160624Z'),'FREQ' => 'DAILY','INTERVAL' => 1,'UNTIL' => '20160628'),
|
||||||
|
array(
|
||||||
|
date_create('20160624Z'),
|
||||||
|
date_create('20160625Z'),
|
||||||
|
date_create('20160626Z'),
|
||||||
|
date_create('20160627Z'),
|
||||||
|
// date_create('20160628Z') // will not return this due to timezone mismatch (unless default timezone is utc)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array('DTSTART' => new DateTime('20160624Z'),'FREQ' => 'DAILY','INTERVAL' => 1,'UNTIL' => '28-06-2016'),
|
||||||
|
array(
|
||||||
|
date_create('20160624Z'),
|
||||||
|
date_create('20160625Z'),
|
||||||
|
date_create('20160626Z'),
|
||||||
|
date_create('20160627Z'),
|
||||||
|
// date_create('20160628Z') // will not return this due to timezone mismatch (unless default timezone is utc)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array('DTSTART' => new DateTime('20160624Z'),'FREQ' => 'DAILY','INTERVAL' => 1,'UNTIL' => new DateTime('20160628', new DateTimeZone('Europe/Paris'))),
|
||||||
|
array(
|
||||||
|
date_create('20160624Z'),
|
||||||
|
date_create('20160625Z'),
|
||||||
|
date_create('20160626Z'),
|
||||||
|
date_create('20160627Z'),
|
||||||
|
// date_create('20160628Z') // will not return this due to timezone mismatch (unless default timezone is utc)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test bug issue #13
|
||||||
|
* @see https://github.com/rlanvin/php-rrule/issues/13
|
||||||
|
* @dataProvider rulesWithMismatchedTimezones
|
||||||
|
*/
|
||||||
|
public function testRulesWithMismatchedTimezones($rule, $occurrences)
|
||||||
|
{
|
||||||
|
$rrule = new RRule($rule);
|
||||||
|
$this->assertEquals($occurrences, $rrule->getOccurrences(), 'Mismatched timezones makes for strange results');
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Other tests
|
||||||
|
|
||||||
public function testIsFinite()
|
public function testIsFinite()
|
||||||
{
|
{
|
||||||
$rrule = new RRule(array(
|
$rrule = new RRule(array(
|
||||||
@ -2084,7 +2187,7 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
$rrule->humanReadable(array(
|
$rrule->humanReadable(array(
|
||||||
'locale' => 'xx',
|
'locale' => 'xx',
|
||||||
'fallback' => 'xx'
|
'fallback' => 'xx'
|
||||||
));
|
)); // the locales are correctly formatted, but not such file exist, so this should throw a RuntimeException
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user