1
0
mirror of https://github.com/rlanvin/php-rrule.git synced 2024-11-28 05:24:10 +01:00

Add $dtstart optional arg when creating from a string

This commit is contained in:
rlanvin 2017-05-06 13:35:15 +01:00
parent 7437503a73
commit e2f42382d4
6 changed files with 100 additions and 20 deletions

View File

@ -8,11 +8,16 @@
- RSet constructor now accepts a string to build a RSET from a RFC string [#26](https://github.com/rlanvin/php-rrule/issues/26)
- New factory method `RRule::createFromRfcString()` to build either a RRule or a RSet from a string
- Add a `$limit` parameter to `getOccurrences()` and `getOccurrencesBetween()` to make working with infinite rule easier
- Add a `$dtstart` parameter to `RRule` and `RSet` constsructor to specify dtstart when working with a RFC string without DTSTART.
### Fixed
- When creating a RRule, the RFC parser will not accept multiple DTSTART or RRULE lines
### Deprecated
- `RRule::parseRfcString` is deprecated. Note: it wasn't part of the documentation in the first place, but just in case someone is using it, it's not removed yet.
## [1.4.2] - 2017-03-29
### Fixed

View File

@ -193,20 +193,25 @@ class RRule implements RRuleInterface
*
* @param mixed $parts An assoc array of parts, or a RFC string.
*/
public function __construct($parts)
public function __construct($parts, $dtstart = null)
{
if ( is_string($parts) ) {
$parts = RfcParser::parseRRule($parts);
$parts = array_change_key_case($parts, CASE_UPPER);
}
elseif ( is_array($parts) ) {
$parts = RfcParser::parseRRule($parts, $dtstart);
$parts = array_change_key_case($parts, CASE_UPPER);
}
else {
throw new \InvalidArgumentException(sprintf(
'The first argument must be a string or an array (%s provided)',
gettype($parts)
));
if ( $dtstart ) {
throw new \InvalidArgumentException('$dtstart argument has no effect if not constructing from a string');
}
if ( is_array($parts) ) {
$parts = array_change_key_case($parts, CASE_UPPER);
}
else {
throw new \InvalidArgumentException(sprintf(
'The first argument must be a string or an array (%s provided)',
gettype($parts)
));
}
}
// validate extra parts

View File

@ -58,7 +58,7 @@ class RSet implements RRuleInterface
*
* @param string $string a RFC compliant text block
*/
public function __construct($string = null)
public function __construct($string = null, $default_dtstart = null)
{
if ( $string && is_string($string) ) {
$string = trim($string);
@ -82,7 +82,7 @@ class RSet implements RRuleInterface
$property_name = $tmp[0];
switch ( strtoupper($property_name) ) {
case 'DTSTART':
if ( $dtstart !== null ) {
if ( $default_dtstart || $dtstart !== null ) {
throw new \InvalidArgumentException('Failed to parse RFC string, multiple DTSTART found');
}
$dtstart = $line;
@ -108,14 +108,14 @@ class RSet implements RRuleInterface
$rrule = $dtstart."\n".$rrule;
}
$this->addRRule($rrule);
$this->addRRule(new RRule($rrule, $default_dtstart));
}
foreach ( $exrules as $rrule ) {
if ( $dtstart ) {
$rrule = $dtstart."\n".$rrule;
}
$this->addExRule($rrule);
$this->addExRule(new RRule($rrule, $default_dtstart));
}
foreach ( $rdates as $date ) {

View File

@ -22,7 +22,6 @@ namespace RRule;
*/
class RfcParser
{
/**
* High level "line".
* Explode a line into property name, property parameters and property value
@ -63,10 +62,14 @@ class RfcParser
/**
* Parse both DTSTART and RRULE (and EXRULE).
*
* It's impossible to accuractly parse a RRULE in isolation (without the DTSTART)
* It's impossible to accuratly parse a RRULE in isolation (without the DTSTART)
* as some tests depends on DTSTART (notably the date format for UNTIL).
*
* @param string $string The RFC-like string
* @param mixed $dtstart The default dtstart to be used (if not in the string)
* @return array
*/
static public function parseRRule($string)
static public function parseRRule($string, $dtstart = null)
{
$string = trim($string);
$parts = array();
@ -76,6 +79,12 @@ class RfcParser
$nb_rrule = 0;
$lines = explode("\n", $string);
if ( $dtstart ) {
$nb_dtstart = 1;
$dtstart_type = 'tzid';
$parts['DTSTART'] = RRule::parseDate($dtstart);
}
foreach ( $lines as $line ) {
$property = self::parseLine($line, array(
'name' => sizeof($lines) > 1 ? null : 'RRULE' // allow missing property name for single-line RRULE

View File

@ -1726,12 +1726,12 @@ class RRuleTest extends PHPUnit_Framework_TestCase
));
$this->assertCount(1, $rrule->getOccurrences(1));
$this->assertEquals([date_create('2017-01-01')], $rrule->getOccurrences(1));
$this->assertEquals(array(date_create('2017-01-01')), $rrule->getOccurrences(1));
$this->assertCount(5, $rrule->getOccurrences(5));
$this->assertEquals([
$this->assertEquals(array(
date_create('2017-01-01'),date_create('2017-01-02'),date_create('2017-01-03'),
date_create('2017-01-04'),date_create('2017-01-05')
], $rrule->getOccurrences(5));
), $rrule->getOccurrences(5));
try {
$rrule->getOccurrences();
$this->fail('Expected exception (infinite rule) not thrown');
@ -1749,7 +1749,7 @@ class RRuleTest extends PHPUnit_Framework_TestCase
$this->assertCount(1, $rrule->getOccurrencesBetween('2017-01-01', null, 1));
$this->assertCount(1, $rrule->getOccurrencesBetween('2017-02-01', '2017-12-31', 1));
$this->assertEquals([date_create('2017-02-01')], $rrule->getOccurrencesBetween('2017-02-01', '2017-12-31', 1));
$this->assertEquals(array(date_create('2017-02-01')), $rrule->getOccurrencesBetween('2017-02-01', '2017-12-31', 1));
$this->assertCount(5, $rrule->getOccurrencesBetween('2017-01-01', null, 5));
try {
$rrule->getOccurrencesBetween('2017-01-01', null);
@ -1856,6 +1856,10 @@ class RRuleTest extends PHPUnit_Framework_TestCase
array("\nDTSTART:19970512\nRRULE:FREQ=YEARLY;COUNT=3\n\n",
array(date_create('1997-05-12'),date_create('1998-05-12'),date_create('1999-05-12'))
),
// CRLF
array("\r\nDTSTART:19970512\r\nRRULE:FREQ=YEARLY;COUNT=3\r\n\r\n",
array(date_create('1997-05-12'),date_create('1998-05-12'),date_create('1999-05-12'))
),
// no DTSTART
array("RRULE:FREQ=YEARLY;COUNT=3",
@ -1903,6 +1907,25 @@ class RRuleTest extends PHPUnit_Framework_TestCase
}
}
public function testRfcStringParserWithDtStart()
{
$rrule = new RRule('RRULE:FREQ=YEARLY');
$this->assertEquals(date_create(), $rrule[0]);
$rrule = new RRule('RRULE:FREQ=YEARLY', date_create('2017-01-01'));
$this->assertEquals(date_create('2017-01-01'), $rrule[0]);
$rrule = new RRule('RRULE:FREQ=YEARLY', '2017-01-01');
$this->assertEquals(date_create('2017-01-01'), $rrule[0]);
try {
$rrule = new RRule("DTSTART:19970512\nRRULE:FREQ=YEARLY", date_create('2017-01-01'));
$this->fail('Expected InvalidArgumentException (too many dtstart) not thrown');
} catch ( \InvalidArgumentException $e ) {
}
}
/**
* @see https://github.com/rlanvin/php-rrule/issues/25
*/

View File

@ -511,6 +511,44 @@ class RSetTest extends PHPUnit_Framework_TestCase
$this->assertEquals($occurrences, $object->getOccurrences());
}
public function testParseRfcStringWithDtStart()
{
$rset = new RSet(
"RRULE:FREQ=DAILY;COUNT=3\nEXRULE:FREQ=DAILY;INTERVAL=2;COUNT=1"
);
$this->assertEquals(array(
date_create('+1day'),
date_create('+2day')
), $rset->getOccurrences());
$rset = new RSet(
"RRULE:FREQ=DAILY;COUNT=3\nEXRULE:FREQ=DAILY;INTERVAL=2;COUNT=1",
'2017-01-01'
);
$this->assertEquals(array(
date_create('2017-01-02'),
date_create('2017-01-03')
), $rset->getOccurrences());
$rset = new RSet(
"RRULE:FREQ=DAILY;COUNT=3\nEXRULE:FREQ=DAILY;INTERVAL=2;COUNT=1",
date_create('2017-01-01')
);
$this->assertEquals(array(
date_create('2017-01-02'),
date_create('2017-01-03')
), $rset->getOccurrences());
try {
$rset = new RSet(
"DTSTART:DTSTART;TZID=America/New_York:19970901T090000\nRRULE:FREQ=DAILY;COUNT=3\nEXRULE:FREQ=DAILY;INTERVAL=2;COUNT=1",
date_create('2017-01-01')
);
$this->fail('Expected InvalidArgumentException (too many start date) not thrown');
} catch ( InvalidArgumentException $e ) {
}
}
public function quirkyRfcStrings()
{
return array(