jkbrzt / rrule

JavaScript library for working with recurrence rules for calendar dates as defined in the iCalendar RFC and more.
https://jkbrzt.github.io/rrule
Other
3.33k stars 511 forks source link

Similar work being done to get a modern rrule library #234

Closed jorroll closed 6 years ago

jorroll commented 6 years ago

So earlier this week I jumpted into the RRule source to contribute to the flurry of recent activity (thanks @davidgoli !), but ended up being scared off by the implementation. I imagine it's mostly because it's a port of a python library, a language I'm not familiar with.

I decided to start implementing a new library, in typescript, with a few goals:

Example usage

let rule = new Rule({
  freq: Options.Frequency.YEARLY,
  byMonthOfYear: [2, 6],
  byDayOfWeek: ['SU', ['MO', 3]],
  start: new StandardDateAdapter(new Date(2010,1,7))
})

let index = 0;
for (const date of rule.occurrences()) {
  date.toISOString()
  index++

  if (index > 10) break;
}

rule.occurrences({
  start: new StandardDateAdapter(new Date(2010,5,7)),
  take: 5
})
  .toArray()
  .map(date => date.toISOString())

const iterator = rule.occurrences()

iterator.next().value // occurrence
iterator.next().value // occurrence

You can see the work so far in https://gitlab.com/john.carroll.p/rschedule. I've borrowed the tests from this library and, currently, I've gotten 143/161 tests passing. Notably absent/failing: I haven't yet implemented bysetpos or proper wkst handling, and I see a few issues around daylight savings time.

While working on this, I found that a large portion of this library's tests violate the ICAL spec, which in hindsight wasn't that surprising because this library loosely adheres to the spec in some places. Note: mostly the violations are things that this library supports/allows which the spec forbids. I'm trying to stick to the spec however, so I removed those tests.

Anyway, this is still a WIP, but I figured I should share the repo seeing as I appear to be working parallel to other folks' goals.

I imagine any finished product would be largely complementary to this library, in that this library supports some things which I'm probably not interested in supporting, and that I've chosen a slightly different API (I would say a bit friendlier).

This being said, I'm not opposed to upstreaming some / all of my work, depending on what might be appropriate. But I'm also not sure if that will ever make sense, given the API differences.

Anyway, I'm open to thoughts / feedback.

Library viewable here:

At this point, the "meat" of the library is located in the src/pipes folder. At a high level: the PipeController object powers the public api getting occurrences out of a rule. When you request occurrences from the PipeController, the rule's start date is passed to the first pipe in the rule object's array of rule pipes (one rule pipe for each type of rule, i.e. ByDayOfWeek, ByMonthOfYear, etc). Each rule pipe applies filters / transforms to the date as appropriate, and then passes the date on to the next rule in the pipe. Occurrences which pass all the pipe validations are yielded to the user. This strategy might be more efficient than it sounds, because pipes are able to skip forward in time, as appropriate, rather than brute forcing occurrences by trying every date according to the frequency. At some point this should also make efficiently iterating "backwards" possible, as well as making it relatively straightforward to add custom rules.

(and sorry, there's not yet much in the way of code comments / the code is still pretty messy)

davidgoli commented 6 years ago

Thanks for the info! It looks like we're bringing this library back to life, but I'll be very interested to see where you go with yours. Cheers!

jorroll commented 6 years ago

So FYI, I've largely finished rSchedule. It's date library agnostic, and currently supports native javascript Date, momentjs Moment, as well as luxon DateTime objects. It supports parsing to/from ICAL.

You can read an overview here

And you can see the project repo here.

It has a few advanced features which I very much need:

  1. Occurrence stream operators
    • Think rxjs pipe operators but for a stream of datetime occurrences. They allow you to combine streams together, subtract streams from one another, dedup streams, etc.
  2. Calendar object
    • Can automatically group together occurrences from an array of individual schedules and return a single stream of dates in the proper order.
    • Can also automatically bundle occurrences by a given time span. For example, Calendar#collections({granularity: 'MONTHLY'}) can be used to group occurrences by month and iterate over the months.
      • This makes displaying occurrences in a calendar view very easy.
const calendar = new Calendar({
  schedules: [new Schedule(), new Calendar()],
  data: 'Holds anything I want',
})

for (const collection of calendar.collections({grandularity: 'MONTHLY'})) {
  for (const date of collection.dates) {
    const schedule = date.generators[1] // see generators property on IDateAdapter

    const data = schedule.data // see data property on IHasOccurrences

    if (data.scheduleName === 'My horrible event') continue;

    // else do stuff
  }
}

Anyway, you can see more on the project repo.