tplaner / When

PHP Date Recursion library
https://github.com/tplaner/When
MIT License
513 stars 96 forks source link

1st Monday in April every year for 2 years #55

Closed Blindmikey closed 8 years ago

Blindmikey commented 8 years ago

20160404T090000Z FREQ=YEARLY;BYDAY=MO;BYSETPOS=1;BYMONTH=4;COUNT=2

I get:

2016-04-04 09:00:00.000000
2016-04-11 09:00:00.000000

When I would expect

2016-04-04 09:00:00.000000
2017-04-03 09:00:00.000000

Any idea why?

Thank you!

tplaner commented 8 years ago

Since you have bymonth the expression is expanded and it is giving you every Monday in April, here's the exact reason from the spec:

  Note 2:  Limit if BYYEARDAY or BYMONTHDAY is present; otherwise,
           special expand for WEEKLY if BYWEEKNO present; otherwise,
           special expand for MONTHLY if BYMONTH present; otherwise,
           special expand for YEARLY.

I think this is what you want:

$r = new \When\When();
$r->startDate(new \DateTime('20160404T090000Z'));
$r->rrule('FREQ=YEARLY;BYDAY=1MO;BYMONTH=4;COUNT=2');

$r->generateOccurrences();

print_r($r->occurrences);

// Produces:
// 2016-04-04 09:00:00.000000
// 2017-04-03 09:00:00.000000

This is saying "limit" to only the first Monday in April.

Blindmikey commented 8 years ago

Thank you for the fast and detailed reply!

I'm not brushed up on my RRULE spec, but I wonder if the inclusion of BYSETPOS=1; should limit the results by year in

FREQ=YEARLY;BYDAY=MO;BYSETPOS=1;BYMONTH=4;COUNT=2

According to this generator, this would be a correct way of specifying "1st Monday in April every year for 2 years" : https://www.textmagic.com/free-tools/rrule-generator and when I use the interpreter here I get the expected result : http://worthfreeman.com/projects/online-icalendar-recurrence-event-parser/

I do however get the same results as the When class if I ommit BYSETPOS=1; at the above link.

It could be that these other two site are not up to spec, but I'm wondering if this has something to do with BYSETPOS perhaps?

What do you think? Thanks!

tplaner commented 8 years ago

You're correct, we're not handling this correctly:

  If multiple BYxxx rule parts are specified, then after evaluating
  the specified FREQ and INTERVAL rule parts, the BYxxx rule parts
  are applied to the current set of evaluated occurrences in the
  following order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY,
  BYHOUR, BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are
  evaluated.

Therefore this should be limited not expanded.

Blindmikey commented 8 years ago

I'm attempting to work on this, using BYSETPOS to limit the results, but am running into an error thrown when running phpunit.

The error occurs on the test testGenerateOccurrencesErrorIgnored() of WhenCoreTest.php It expects 1997-10-07T09:00:00+0000 but receives 1997-10-21T09:00:00+0000

I'm having difficulty wrapping my head around this, as the rrule: FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3 seems to specify: Every 3rd Tue, Wed, & Thur of every Month

If so, then 10/21/1997 is in fact the third Tue in October 1997.

However http://worthfreeman.com/projects/online-icalendar-recurrence-event-parser/ seems to agree with your expectation that the dates encountered should be 10/07/1997 & 11/6/1997.

I wonder if I'm not understanding BYSETPOS well enough. Any insight you can provide as I tackle the spec documentation would be greatly appreciated.

Blindmikey commented 8 years ago

Ah - I think I understand.

Since more than one BYDAY is provided, we're iterating our count over each encountered Tues Wed & Thur.