volkanunsal / postgres-rrule

Recurring dates in Postgres.
MIT License
109 stars 25 forks source link

interval is ignored when byday is set for weekly frequencies #21

Closed dspasojevic closed 9 months ago

dspasojevic commented 1 year ago

When creating a rule to repeat every other Monday, rrule seems to suggest a rule like:

FREQ=WEEKLY;INTERVAL=2;BYDAY=MO

Interval works as I would expect when byday is not set:

postgres=# select occurrences('{"freq": "WEEKLY", "wkst": "MO", "until": "2023-08-01", "interval": 2}'::jsonb::rrule, '2023-06-19');
     occurrences
---------------------
 2023-06-19 00:00:00
 2023-07-03 00:00:00
 2023-07-17 00:00:00
 2023-07-31 00:00:00
(4 rows)

However, when byday is set, interval appears to be ignored:

postgres=# select occurrences('{"freq": "WEEKLY", "wkst": "MO", "byday": ["MO"], "until": "2023-08-01", "interval": 2}'::jsonb::rrule, '2023-06-19');
     occurrences
---------------------
 2023-06-19 00:00:00
 2023-06-26 00:00:00
 2023-07-03 00:00:00
 2023-07-10 00:00:00
 2023-07-17 00:00:00
 2023-07-24 00:00:00
 2023-07-31 00:00:00
(7 rows)

Do you expect byday and interval to work together?

dspasojevic commented 1 year ago

I don't think it is just that the interval is ignored.

When byday is Wednesday:

postgres=# select occurrences('{"freq": "WEEKLY", "wkst": "MO", "byday": ["WE"], "until": "2023-08-01", "interval": 2}'::jsonb::rrule, '2023-06-19');
     occurrences
---------------------
 2023-06-21 00:00:00
 2023-07-05 00:00:00
 2023-07-19 00:00:00
(3 rows)

When byday is Monday but the start date is set to the day before the first occurrence:

postgres=# select occurrences('{"freq": "WEEKLY", "wkst": "MO", "byday": ["MO"], "until": "2023-08-01", "interval": 2}'::jsonb::rrule, '2023-06-18');
     occurrences
---------------------
 2023-06-19 00:00:00
 2023-07-03 00:00:00
 2023-07-17 00:00:00
 2023-07-31 00:00:00
(4 rows)
dspasojevic commented 1 year ago

As noted in #22, the root cause of this seems to be this fragment of all_starts:

    SELECT "ts" FROM (
      SELECT "ts"
      FROM generate_series("dtstart", year_end, INTERVAL '1 day') "ts"
      WHERE (
        "ts"::_rrule.DAY = ANY("rrule"."byday")
      )
      AND "ts" <= ("dtstart" + INTERVAL '7 days')
    ) as "ts"

that will produce two starts when byday is the same day of the week as dtstart:

postgres=# select * from all_starts('RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU;COUNT=100'::text::rrule, '2023-07-04T10:00:00'::TIMESTAMP);
     all_starts
---------------------
 2023-07-04 10:00:00
 2023-07-11 10:00:00
(2 rows)

It isn't clear to me why AND "ts" <= ("dtstart" + INTERVAL '7 days') is used instead of AND "ts" < ("dtstart" + INTERVAL '7 days').

Are there circumstances in which it is necessary to include the seventh day?

thomastthai commented 9 months ago

This issue should too be closed after the merge?