Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request For Comments: Proposed rrule 3.0 API design #298

Open
davidgoli opened this issue Nov 11, 2018 · 7 comments
Open

Request For Comments: Proposed rrule 3.0 API design #298

davidgoli opened this issue Nov 11, 2018 · 7 comments
Assignees
Milestone

Comments

@davidgoli
Copy link
Collaborator

The original version of this library was a port of Python's dateutil.rrule library. The syntax was thus very "Pythonic" and not very JavaScript-y, thus leading to a lot of inconsistencies. For example:

  • rrulestr may return an RRule or an RRuleSet, which implement different interfaces
  • An RRule or RRuleSet instance is partially mutable (so calling some instance methods will change behavior of others), but not predictably so (so modifying instance state, eg rrule.options, may or may not have an effect on the output)
  • Additionally, around v2.3.1, I introduced a change to start bundling the NLP library along with the base RRule library, which more than doubled the bundle size.

Proposal for rrule 3.0

Better RFC compliance

The proposed approach centers around the options object. Its syntax structure should be consistent with the structure of the RFC spec:

{
  rrule: {
    freq: RRule.WEEKLY,
    interval: 5,
    byweekday: [RRule.MO, RRule.FR],
    until: new Date(Date.UTC(2012, 12, 31))
  },
  dtstart: new Date(Date.UTC(2012, 1, 1, 10, 30)),
}

Strings and options objects are interchangeable

const str = "DTSTART:20120201T023000Z
RRULE:FREQ=MONTHLY;COUNT=5
RDATE:20120701T023000Z,20120702T023000Z
EXRULE:FREQ=MONTHLY;COUNT=2
EXDATE:20120601T023000Z"

const options = rrulestr(str)
{
  dtstart: '2012-02-01T10:30:00.000Z',
  rrule: {
    freq: RRule.MONTHLY,
    count: 5,
  },
  rdate: [
    '2012-07-01T10:30:00.000Z',
    '2012-07-02T10:30:00.000Z',
  ],
  exrule: {
    freq: RRule.MONTHLY,
    count: 2,
  },
  exdate: [
    '2012-06-01T10:30:00.000Z',
  ]
}

all(str)
... same output as all(options)

between(str, start, end)
... same output as between(options, start, end)

Immutability

RRule should use a function-based, immutable approach, where options objects are just plain-old JS (POJS) objects (or their string equivalents), and a suite of functions is provided to generate JS dates, which are completely deterministic (the same options object or string always returns the same set of dates):

const options = {
  dtstart: new Date(Date.UTC(2012, 1, 1, 10, 30)),
  rrule: {
    freq: RRule.WEEKLY,
    interval: 5,
    byweekday: [RRule.MO, RRule.FR],
    until: new Date(Date.UTC(2012, 12, 31))
  }
}

all(options)
[ '2012-02-03T10:30:00.000Z',
  '2012-03-05T10:30:00.000Z',
  '2012-03-09T10:30:00.000Z',
  '2012-04-09T10:30:00.000Z',
  '2012-04-13T10:30:00.000Z',
  '2012-05-14T10:30:00.000Z',
  '2012-05-18T10:30:00.000Z',

 /* … */]

between(options, new Date(Date.UTC(2012, 7, 1)), new Date(Date.UTC(2012, 8, 1)))
['2012-08-27T10:30:00.000Z',
 '2012-08-31T10:30:00.000Z']

toString(options)
"DTSTART:20120201T093000Z
RRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR"

Question: How important is the Cache functionality? Could it be removed from this library and replaced in client code by memoization?

Remove RRuleSet

rrulestr will now simply return a parsed options object. The options object also supports the extensions formerly available only to RRuleSet, meaning that a complete RRuleSet can be specified declaratively in a single statement:

const options = {
  dtstart: new Date(Date.UTC(2012, 1, 1, 10, 30)),
  rrule: {
    freq: RRule.MONTHLY,
    count: 5,
  },
  rdate: [
    new Date(Date.UTC(2012, 6, 1, 10, 30)),
    new Date(Date.UTC(2012, 6, 2, 10, 30))
  ],
  // exrule has the same dtstart as the rest of the options
  exrule: {
    freq: RRule.MONTHLY,
    count: 2,
  },
  exdate: [
    new Date(Date.UTC(2012, 5, 1, 10, 30)),
  ]
}

all(options)
[ '2012-02-01T10:30:00.000Z',
  '2012-05-01T10:30:00.000Z',
  '2012-07-01T10:30:00.000Z',
  '2012-07-02T10:30:00.000Z' ]

between(options, new Date(Date.UTC(2012, 2, 1)), new Date(Date.UTC(2012, 6, 2)))
[ '2012-05-01T10:30:00.000Z', '2012-07-01T10:30:00.000Z' ]

toString(options)
"DTSTART:20120201T023000Z
RRULE:FREQ=MONTHLY;COUNT=5
RDATE:20120701T023000Z,20120702T023000Z
EXRULE:FREQ=MONTHLY;COUNT=2
EXDATE:20120601T023000Z"

Question: EXRULE has been deprecated in RFC 5545. Is it important to continue supporting it?

Future enhancement: RRULE and EXRULE could support arrays of options in addition to options.

Move NLP to its own npm package

rrule-nlp provides useful, if neglected, functionality. Its methods are concerned not with describing sets of dates (the output), but describing the rrule options itself (the input). It should be treated as its own package and removed from the main rrule package in the interest of size.

I expect to have a working implementation of this proposal by February 1, 2019. I'm opening a comment period on this proposal until that time. Thank you for your interest in this library!

@davidgoli davidgoli added this to the 3.0 milestone Nov 11, 2018
@davidgoli davidgoli self-assigned this Nov 11, 2018
@jkbrzt
Copy link
Owner

jkbrzt commented Nov 28, 2018

Question: How important is the Cache functionality? Could it be removed from this library and replaced in client code by memoization?

I’m for removing it.

Question: EXRULE has been deprecated in RFC 5545. Is it important to continue supporting it?

I’m for removing this as well.

With options being so central and existing only as a plain object with no constructor, I'd consider exposing an explicit validate(options) (or something along those lines).

Excellent proposal, thanks @davidgoli!

@jorroll
Copy link

jorroll commented Nov 30, 2018

I'm not sure if this is something you'd be interested in adding to this library, but a killer feature I need (which I implemented in rSchedule) is the ability to combine/manipulate multiple RRule objects (recurrence streams).

Some examples:

  1. Given two RRule objects, find the intersection of them.
  2. Given one RRule object, add a second RRule object to it and return a stream containing the union of the two RRule objects.
  3. Given two RRule objects, add them together (get the union) and return a deduplicated stream of the result.
  4. Add three RRule objects, de-duplicate the resulting union stream, then subtract a third RRule object from that union stream.
  5. etc.

If you implemented operator functions like these, then someone could create their own RRuleSet object pretty easily. All they would need to do is

  1. Add together all RRules designated RRule
  2. Subtract all RRules designated EXRule
  3. Add all dates designated RDate
  4. Subtract all dates designated EXDate
  5. De-duplicate the result

For me, implementing this in rrulejs might allow me to remove the recurrence logic from rSchedule--allowing me to turn the rSchedule library into a simple collection of wrapper classes (i.e. Calendar, Schedule, RRule).

I tend to think of these transformations as akin to rxjs pipe transformations.

Building off the api you outlined above, an implementation might look like

all(
  combineOptions(
    add(rruleOptionOne, rruleOptionTwo),
    subtract(exruleOption),
    add(rdateOne, rdateTwo, rdateThree),
    subtract(exdateOne),
    unique(),
  )
)

// might return:
[ '2012-02-01T10:30:00.000Z',
  '2012-05-01T10:30:00.000Z',
  '2012-07-01T10:30:00.000Z',
  '2012-07-02T10:30:00.000Z' ]

@msageryd
Copy link

@davidgoli This is a great libray! How is v3 coming along? Did you abandon the idea of a new API? I'm curious to know because I'm building a task scheduler based on rrule and I'd like my api to be as future proof as possible. One thing would be to not support RRuleSet in my api if you're planning to remove it anyway.

https://github.com/msageryd/node-schedule-rrule

@rofrol
Copy link

rofrol commented Jun 1, 2021

How is v3 coming along?

@jorroll
Copy link

jorroll commented Jun 1, 2021

For @rofrol and others, I can't immediately find the comment, but I seem to remember that @davidgoli mentioned he no longer uses rrule and is no longer actively working on it. At the moment I believe this library is not actively maintained.

@matheusbaumgart
Copy link

What are people using instead?

@jorroll
Copy link

jorroll commented Jan 13, 2023

What are people using instead?

rSchedule @matheusbaumgart. Though I made it, so I'm definitely biased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants