Occurrence Operators
This library exports a selection of occurrence stream operators for manipulating a stream of occurrences. Current operators are:
Each of these operator functions is used as an argument to OccurrenceGenerator#pipe()
.
If you look at the source code for Schedule
and Calendar
, you'll see that, internally, their iteration logic is implemented with these operators. You can use these operators to create complex custom schedules.
For example:
const rrules = new Rule({
// ...rule options
});
const exrules = new Rule({
// ...rule options
});
const rdates = new Dates({ dates: [new Date()] });
const exdates = new Dates({ dates: [new Date()] });
// This replicates the functionality of the `Schedule` object.
const schedule = new Calendar().pipe(
add(rrule),
subtract(exrule),
add(rdates),
subtract(exdates),
unique(),
);
schedule.occurrences({ take: 5 }).toArray(); // occurrences;
Operators
Add
An operator function which accepts a spread of occurrence generators and adds their occurrences to the output. This operator ignores duration.
Example:
new Calendar().pipe(add(scheduleOne, scheduleTwo));
Subtract
An operator function which accepts a spread of occurrence generators and removes their occurrences from the output. This operator ignores duration.
Example:
scheduleOne.pipe(subtract(scheduleTwo));
Intersection
An operator function which takes a spread of occurrence generators and only returns the dates which intersect every occurrence generator. This operator ignores duration.
Because it's possible for all the generators to never intersect, and because the intersection operator can't detect this lack of intersection, you must call intersection()
with a {maxFailedIterations: number}
argument if it is built from occurrence generators of infinite length. For convenience, you can globally set IntersectionOperator.defaultMaxFailedIterations
.
- Without further information, I'd probably set
defaultMaxFailedIterations = 50
.
The maxFailedIterations
argument caps the number of iterations the operator will run through without finding a single valid occurrence. If this number is reached, the operator will stop iterating (preventing a possible infinite loop).
- Note:
maxFailedIterations
caps the number of iterations which fail to turn up a single valid occurrence. Every time a valid occurrence is returned, the current iteration count is reset to 0. - If the occurrence generators feeding the IntersectionOperator are not infinite, the
maxFailedIterations
number is ignored.
Example:
scheduleOne.pipe(
intersection({
streams: [scheduleTwo],
maxFailedIterations: 50,
}),
);
Unique
An operator function which removes duplicate dates from the occurrence stream. This operator ignores duration.
Example:
new Calendar({
schedules: [scheduleOne, scheduleTwo],
}).pipe(unique());
MergeDuration
Note: only usable on streams where all occurrences have a duration
An operator function which takes an occurrence stream with hasDuration === true
and merges occurrences which have overlapping start and end times.
You must provide a maxDuration
argument that represents the maximum possible duration for a single occurrence. If this duration is exceeded, a MergeDurationOperatorError
will be thrown.
Example:
const MILLISECONDS_IN_HOUR = 1000 * 60 * 60;
const dates = new Dates({
dates: [
new StandardDateAdapter(new Date(2010, 10, 10, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
new StandardDateAdapter(new Date(2010, 10, 11, 13), { duration: MILLISECONDS_IN_HOUR * 2 }),
new StandardDateAdapter(new Date(2010, 10, 11, 14), { duration: MILLISECONDS_IN_HOUR * 2 }),
new StandardDateAdapter(new Date(2010, 10, 12, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
],
}).pipe(
mergeDuration({
maxDuration: MILLISECONDS_IN_HOUR * 24,
}),
);
expect(dates.occurrences().toArray()).toEqual([
new StandardDateAdapter(new Date(2010, 10, 10, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
new StandardDateAdapter(new Date(2010, 10, 11, 13), { duration: MILLISECONDS_IN_HOUR * 3 }),
new StandardDateAdapter(new Date(2010, 10, 12, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
]);
SplitDuration
Note: only usable on streams where all occurrences have a duration
An operator function which takes an occurrence stream with hasDuration === true
and passes occurrences through a splitting function. One usecase for this operator is to dynamically break up occurrences with a large duration into several smaller occurrences.
You must provide a maxDuration
argument that represents the maximum possible duration for a single occurrence. If this duration is exceeded, a SplitDurationOperatorError
will be thrown.
Usage example:
const MILLISECONDS_IN_HOUR = 1000 * 60 * 60;
const splitFn = (date: DateTime) => {
if (date.duration > MILLISECONDS_IN_HOUR) {
const diff = date.duration! / 2;
return [date.set('duration', diff), date.add(diff, 'millisecond').set('duration', diff)];
}
return [date];
};
const dates = new Dates({
dates: [
new StandardDateAdapter(new Date(2010, 10, 10, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
new StandardDateAdapter(new Date(2010, 10, 11, 13), { duration: MILLISECONDS_IN_HOUR * 2 }),
],
}).pipe(
splitDuration({
splitFn,
maxDuration: MILLISECONDS_IN_HOUR * 1,
}),
);
expect(dates.occurrences().toArray()).toEqual([
new StandardDateAdapter(new Date(2010, 10, 10, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
new StandardDateAdapter(new Date(2010, 10, 11, 13), { duration: MILLISECONDS_IN_HOUR * 1 }),
new StandardDateAdapter(new Date(2010, 10, 11, 14), { duration: MILLISECONDS_IN_HOUR * 1 }),
]);