mattlewis92 / angular-calendar

A flexible calendar component for angular 15.0+ that can display events on a month, week or day view.
https://mattlewis92.github.io/angular-calendar/
MIT License
2.73k stars 868 forks source link

Javascript recurring dates library #711

Closed jorroll closed 6 years ago

jorroll commented 6 years ago

So angular-calendar is awesome :)

I'm using it in a project I'm working on with some rather complex recurring schedules. I ended up making a new javascript (typescript) recurrence library which implements the ICAL spec to handle it: rSchedule.

This is really just an FYI, but I wanted to call out the fact that I think it pairs particularly well with your library for two reasons:

1.

After creating a recurring schedule, the library can automatically return ordered recurrences by "calendar month". This can either be grouped by the start and end of the actual month, or by the start of the first week of the month and end of the last week of the month (which can include dates in the preceding and next months). This makes it easy to get dates for the month view of angular-calendar.

const calendar = new Calendar({
  schedules: new RRule({
    start: new Date(),
    frequency: 'DAILY',
    byDayOfWeek: ['MO']
  })
})

const iterator = calendar.collections({
  granularity: 'MONTHLY',
  weekStart: 'MO'
})

for (const collection of iterator) {
  // yields a collection object containing all the dates for that month

  for (const date of collection.dates) {
    // do stuff
  }
}

// or just get the next collection

const collection = calendar.collections({
  start: new Date()
  granularity: 'MONTHLY'
  weekStart: 'MO'
}).next().value

2.

Like angular-calendar, rSchedule makes use of a DateAdapter interface so that it is date library agnostic. DateAdapters for the standard javascript Date object, as well as Moment and luxon DateTime objects exist. When using moment-timezone or luxon date adapters, rSchedule has full time zone support.

Anyway, thanks so much for your work maintaining angular-calendar!!!

You can learn more about rSchedule over in it's repo.

Is your feature request related to a problem? Please describe

Describe the solution you'd like

Describe your use case for implementing this feature

Additional context

jorroll commented 6 years ago

Oh also, almost forgot, you might be interested in how I implemented my date adapters as they basically do all the same stuff as your date adapters, yet they require the end user to implement way less if they're creating a custom date adapter.

What I did:

I created a generic DateTime object with equivalent functionality to everything you have in your date adapter interface (i.e. it has setHours, addHours, etc methods) .

If a user creates a custom date adapter, they extend my date adapter base class which provides methods like setHours.

When setHours is called on a custom child date adapter, the child date adapter returns super.setHours(this).

In the super call the date adapter instance is passed into a DateTime instance, the datetime extracts all the time info (year, month, day, hour, minute, second, millisecond) and instantiates a new UTC date with that time info new Date(Date.UTC()). It then manipulates the date however it needs to be manipulated (in this example, addHours) and then passes the new year, month, day, hour, minute, second, millisecond back to the custom child date adapter which uses the values to instantiate a new date object using whatever javascript date library is appropriate for that date adapter (e.g. Moment), replacing the old value. This allows me to provide most all the necessary date adapter functionality for the end user, greatly reducing the number of methods an end user needs to implement in a custom date adapter.

You can check out my base date adapter here: https://gitlab.com/john.carroll.p/rschedule/blob/master/packages/rschedule/src/lib/date-adapter/date-adapter-base.ts

The individual, non-base, date adapters which I provide (e.g. MomentDateAdapter) are kept in separate packages within the mono-repo.

mattlewis92 commented 6 years ago

Hey! Thanks for sharing your lib, it looks really great 😄 I'm curious as to the advantages of your lib over rrule?

jorroll commented 6 years ago

That's a great question. When I started working on rSchedule, a bit over a month ago. rrule development had been stagnant for over a year, there were a number of bugs, it didn't fully support the ical spec, and it didn't support time zones.

In the past month one new community member stepped up and basically fixed all of those grips (at the same time as I was making rSchedule). Had I known this would happen before making rSchedule, I wouldn't have made rSchedule. I think rSchedule is better, as I'll explain below (I'm definitely biased), but it's not so much better that I would have spent all the time on it had I known rrule would be reborn.

First, things rrule is better at:

  1. rrule offers some natural language processing, which rSchedule doesn't.
  2. rSchedule doesn't currently support the BYSETPOS, BYYEARDAY, or BYWEEKNO ical rules (I think rrule supports all of them, though I'm not sure about BYSETPOS). BYYEARDAY and BYSETPOS should be pretty easy to add to rSchedule (I don't need them). BYWEEKNO is super annoying though (definitely doable, but I imagine it would take someone longer then they'd like).

Advantages to rSchedule

  1. It uses modern ES6 concepts and is built around the javascript generator protocol. The generator protocol makes the API friendly, as you can do things like call .next() to iterate through occurrences one by one, or iterate using a for loop and have access to keywords like continue and break.

  2. rrule (I think) needs to iterate through every date according to a rule's frequency, and it filters out dates which are invalid. I.e. if you have a "DAILY" rule which only happens on monday's, rrule will iterate through every day and just keep mondays. rSchedule, on the other hand, will just iterate over mondays, skipping the other dates. rSchedule's logic is implemented as a series of pipe transformations that should be somewhat easy for others to understand, as well as opening up the possibility that someone could fork the library and add their own, custom occurrence rules.

    1. In the future, I hope to make the recurrence rules (and pipe operators--explained below) tree-shakable, which would greatly reduce the library's minimum bundle size (currently the minzip size is ~13kb, which is the same as rrule).
  3. rSchedule uses a date adapter interface, allowing it to work with any date library. If your chosen date library supports time zones, rSchedule supports time zones. I've created date adapters for moment, moment-timezone, luxon, js-joda, and the standard Date. rrule now supports time zones via the luxon library, but you're forced to use the luxon library. rrule also forces you to work entirely with "floating" UTC dates (if this sounds strange, it is. Check out the rrule repo for more info) -- rSchedule's dates are normal.

  4. rSchedule supports durations (e.g. an event starts at 5pm and ends at 7pm, every Monday). This includes support for predicate queries which are duration aware (e.g. does the 5-7pm event occur at 6pm?). rrule has no duration support.

  5. One of my favorite features, and something that no other date library offers (so far as I am aware): rSchedule objects are composable using occurrence stream operators. Occurrence stream operators are heavily inspired by rxjs operators, and they allow you to combine and manipulate rSchedule objects in various ways. For example, you could use the intersection operator to find out when two different people were mutually available (and display a calendar of their mutual availability). I wrote a blog post which talks a bit more about occurrence stream operators.

  6. rSchedule offers several quality of life methods, like the Calendar#collections() method I mentioned above, as well as the ability to call occursOn() with a weekday argument. e.g. schedule.occursOn({weekday: 'MO', after: new Date()}). Also, all of the rSchedule generators (i.e. Rule, Schedule, Calendar, and Dates objects) each have a data property that a user can attach arbitrary data to.

  7. rSchedule objects are immutable (rrule's are not) which helps prevent many usage bugs.

Anyway. I'm definitely a power user case and, for me, the occurrence stream operators are the killer feature (basically allowing you to create schedules of any complexity). But for most users, they definitely can't go wrong with either library.

mattlewis92 commented 6 years ago

Sweet, thanks for taking the time to explain that, it makes a lot of sense! I've added a link to your lib in the recurring demo as an alternative to rrule ✌️

meiuqer commented 5 years ago

A basic example with this library would be more then welcome! Can seem to get this library working.

jorroll commented 5 years ago

@meiuqer when you say "this library," are you referring to angular-calendar or rSchedule?

meiuqer commented 5 years ago

I'm sorry i meant for rSchedule. Just a simple recurring event made with rSchedule implemented in the angular-calender.

jorroll commented 5 years ago

@meiuqer so here's a simple stackblitz example which may help: https://stackblitz.com/edit/github-zmc1k2?file=src/app/app.component.ts

Take a look at the app.component.ts file. I assume you are somewhat familiar with the ical spec, which rSchedule is made to work with.

I based it off the angular-calendar angular-cli build-tool-example. Unfortunately, there appears to be a bug in the current angular-cli build-tool-example which prevents it from working in stackblitz (simply importing the angular-cli example into stackblitz, without making any changes, produces an error for me in stackblitz). This problem seems to be unrelated to rSchedule and I'll open a separate issue about it (update: seperate issue tracked in #821).

Anyway, this bug means that the stackblitz example doesn't actually work. I think the example will work if you build it locally though, and regardless, hopefully you get the idea. If you need help using rSchedule "in general," you should check out the rSchedule docs.

Hopefully this answers your questions.

meiuqer commented 5 years ago

Thanks! I will check it out! But it's a great addition to your already great post! 👍