UNPKG

@rschedule/rschedule

Version:

A typescript library for working with recurring dates and events.

1,241 lines (1,222 loc) 232 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.rSchedule = {})); }(this, function (exports) { 'use strict'; var IntersectionOperatorConfig = /** @class */ (function () { function IntersectionOperatorConfig() { } return IntersectionOperatorConfig; }()); var RuleConfig = /** @class */ (function () { function RuleConfig() { } return RuleConfig; }()); var MergeDurationOperatorConfig = /** @class */ (function () { function MergeDurationOperatorConfig() { } return MergeDurationOperatorConfig; }()); var SplitDurationOperatorConfig = /** @class */ (function () { function SplitDurationOperatorConfig() { } return SplitDurationOperatorConfig; }()); var RScheduleConfig = /** @class */ (function () { function RScheduleConfig() { } RScheduleConfig.Rule = RuleConfig; RScheduleConfig.IntersectionOperator = IntersectionOperatorConfig; RScheduleConfig.MergeDurationOperator = MergeDurationOperatorConfig; RScheduleConfig.SplitDurationOperator = SplitDurationOperatorConfig; return RScheduleConfig; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } var ArgumentError = /** @class */ (function (_super) { __extends(ArgumentError, _super); function ArgumentError() { return _super !== null && _super.apply(this, arguments) || this; } return ArgumentError; }(Error)); var InfiniteLoopError = /** @class */ (function (_super) { __extends(InfiniteLoopError, _super); function InfiniteLoopError() { return _super !== null && _super.apply(this, arguments) || this; } return InfiniteLoopError; }(Error)); // Taken from https://stackoverflow.com/a/53985533/5490505 // export type TupleUnshift<A, B extends readonly [...any[]]> = ((a: A, ...r: ForcedTuple<B>) => void) extends ( // ...a: infer R // ) => any // ? R // : never; // type ForcedTuple<T> = T extends [ // infer A, // infer B, // infer C, // infer D, // infer E, // infer F, // infer G, // infer H, // infer I, // infer J, // infer K, // infer L, // infer M, // infer N, // infer O, // infer P, // infer Q, // infer R, // infer S, // infer T, // infer U, // infer V, // infer W, // infer X, // infer Y, // infer Z // ] // ? [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] // : T; function numberSortComparer(a, b) { if (a > b) { return 1; } else if (b > a) { return -1; } else { return 0; } } function freqToGranularity(freq) { switch (freq) { case 'YEARLY': return 'year'; case 'MONTHLY': return 'month'; case 'WEEKLY': return 'week'; case 'DAILY': return 'day'; case 'HOURLY': return 'hour'; case 'MINUTELY': return 'minute'; case 'SECONDLY': return 'second'; default: return 'millisecond'; } } function cloneJSON(json) { return JSON.parse(JSON.stringify(json)); } var _a; var WEEKDAYS = [ 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', ]; var MILLISECONDS_IN_SECOND = 1000; var MILLISECONDS_IN_MINUTE = MILLISECONDS_IN_SECOND * 60; var MILLISECONDS_IN_HOUR = MILLISECONDS_IN_MINUTE * 60; var MILLISECONDS_IN_DAY = MILLISECONDS_IN_HOUR * 24; var MILLISECONDS_IN_WEEK = MILLISECONDS_IN_DAY * 7; var InvalidDateTimeError = /** @class */ (function (_super) { __extends(InvalidDateTimeError, _super); function InvalidDateTimeError() { return _super !== null && _super.apply(this, arguments) || this; } return InvalidDateTimeError; }(Error)); var DATETIME_ID = Symbol.for('b1231462-3560-4770-94f0-d16295d5965c'); var DateTime = /** @class */ (function () { function DateTime(date, timezone, duration) { /** * This property contains an ordered array of the generator objects * responsible for producing this DateAdapter. * * - If this DateAdapter was produced by a `Rule` object, this array * will just contain the `Rule` object. * - If this DateAdapter was produced by a `Schedule` object, this * array will contain the `Schedule` object as well as the `Rule` * or `Dates` object which generated it. * - If this DateAdapter was produced by a `Calendar` object, this * array will contain, at minimum, the `Calendar`, `Schedule`, and * `Rule`/`Dates` objects which generated it. */ this.generators = []; this[_a] = true; this.date = new Date(date); this.timezone = timezone || null; this.duration = duration; this.assertIsValid(); } /** * Similar to `Array.isArray()`, `isInstance()` provides a surefire method * of determining if an object is a `DateTime` by checking against the * global symbol registry. */ DateTime.isInstance = function (object) { return !!(object && object[DATETIME_ID]); }; DateTime.fromJSON = function (json) { var date = new Date(Date.UTC(json.year, json.month - 1, json.day, json.hour, json.minute, json.second, json.millisecond)); return new DateTime(date, json.timezone, json.duration); }; DateTime.fromDateAdapter = function (adapter) { return DateTime.fromJSON(adapter.toJSON()); }; Object.defineProperty(DateTime.prototype, "end", { /** * Returns `undefined` if `this.duration` is falsey. Else returns * the `end` date. */ get: function () { if (!this.duration) return; if (this._end) return this._end; this._end = this.add(this.duration, 'millisecond'); return this._end; }, enumerable: true, configurable: true }); // While we constrain the argument to be another DateAdapter in typescript // we handle the case of someone passing in another type of object in javascript DateTime.prototype.isEqual = function (object) { if (!object) { return false; } assertSameTimeZone(this, object); return this.valueOf() === object.valueOf(); }; DateTime.prototype.isBefore = function (object) { assertSameTimeZone(this, object); return this.valueOf() < object.valueOf(); }; DateTime.prototype.isBeforeOrEqual = function (object) { assertSameTimeZone(this, object); return this.valueOf() <= object.valueOf(); }; DateTime.prototype.isAfter = function (object) { assertSameTimeZone(this, object); return this.valueOf() > object.valueOf(); }; DateTime.prototype.isAfterOrEqual = function (object) { assertSameTimeZone(this, object); return this.valueOf() >= object.valueOf(); }; DateTime.prototype.isOccurring = function (object) { if (!this.duration) { throw new Error('DateTime#isOccurring() is only applicable to DateTimes with durations'); } assertSameTimeZone(this, object); return (object.isAfterOrEqual(this) && object.isBeforeOrEqual(this.add(this.duration, 'millisecond'))); }; DateTime.prototype.add = function (amount, unit) { switch (unit) { case 'year': return this.forkDateTime(addUTCYears(this.date, amount)); case 'month': return this.forkDateTime(addUTCMonths(this.date, amount)); case 'week': return this.forkDateTime(addUTCWeeks(this.date, amount)); case 'day': return this.forkDateTime(addUTCDays(this.date, amount)); case 'hour': return this.forkDateTime(addUTCHours(this.date, amount)); case 'minute': return this.forkDateTime(addUTCMinutes(this.date, amount)); case 'second': return this.forkDateTime(addUTCSeconds(this.date, amount)); case 'millisecond': return this.forkDateTime(addUTCMilliseconds(this.date, amount)); default: throw new ArgumentError('Invalid unit provided to `DateTime#add`'); } }; DateTime.prototype.subtract = function (amount, unit) { switch (unit) { case 'year': return this.forkDateTime(subUTCYears(this.date, amount)); case 'month': return this.forkDateTime(subUTCMonths(this.date, amount)); case 'week': return this.forkDateTime(subUTCWeeks(this.date, amount)); case 'day': return this.forkDateTime(subUTCDays(this.date, amount)); case 'hour': return this.forkDateTime(subUTCHours(this.date, amount)); case 'minute': return this.forkDateTime(subUTCMinutes(this.date, amount)); case 'second': return this.forkDateTime(subUTCSeconds(this.date, amount)); case 'millisecond': return this.forkDateTime(subUTCMilliseconds(this.date, amount)); default: throw new ArgumentError('Invalid unit provided to `DateTime#subtract`'); } }; DateTime.prototype.get = function (unit) { switch (unit) { case 'year': return this.date.getUTCFullYear(); case 'month': return (this.date.getUTCMonth() + 1); case 'yearday': return getUTCYearDay(this.date); case 'weekday': return WEEKDAYS[this.date.getUTCDay()]; case 'day': return this.date.getUTCDate(); case 'hour': return this.date.getUTCHours(); case 'minute': return this.date.getUTCMinutes(); case 'second': return this.date.getUTCSeconds(); case 'millisecond': return this.date.getUTCMilliseconds(); default: throw new ArgumentError('Invalid unit provided to `DateTime#set`'); } }; DateTime.prototype.set = function (unit, value) { if (unit === 'duration') { return new DateTime(this.date, this.timezone, value); } var date = new Date(this.date); switch (unit) { case 'year': date.setUTCFullYear(value); break; case 'month': { // If the current day of the month // is greater than days in the month we are moving to, we need to also // set the day to the end of that month. var length_1 = monthLength(value, date.getUTCFullYear()); var day = date.getUTCDate(); if (day > length_1) { date.setUTCDate(1); date.setUTCMonth(value); date = subUTCDays(date, 1); } else { date.setUTCMonth(value - 1); } break; } case 'day': date.setUTCDate(value); break; case 'hour': date.setUTCHours(value); break; case 'minute': date.setUTCMinutes(value); break; case 'second': date.setUTCSeconds(value); break; case 'millisecond': date.setUTCMilliseconds(value); break; default: throw new ArgumentError('Invalid unit provided to `DateTime#set`'); } return this.forkDateTime(date); }; DateTime.prototype.granularity = function (granularity, opt) { if (opt === void 0) { opt = {}; } var date = this.forkDateTime(this.date); switch (granularity) { case 'year': date = date.set('month', 1); case 'month': date = date.set('day', 1); break; case 'week': date = setDateToStartOfWeek(date, opt.weekStart); } switch (granularity) { case 'year': case 'month': case 'week': case 'day': date = date.set('hour', 0); case 'hour': date = date.set('minute', 0); case 'minute': date = date.set('second', 0); case 'second': date = date.set('millisecond', 0); case 'millisecond': return date; default: throw new ArgumentError('Invalid granularity provided to `DateTime#granularity`: ' + granularity); } }; DateTime.prototype.endGranularity = function (granularity, opt) { if (opt === void 0) { opt = {}; } var date = this.forkDateTime(this.date); switch (granularity) { case 'year': date = date.set('month', 12); case 'month': date = date.set('day', monthLength(date.get('month'), date.get('year'))); break; case 'week': date = setDateToEndOfWeek(date, opt.weekStart); } switch (granularity) { case 'year': case 'month': case 'week': case 'day': date = date.set('hour', 23); case 'hour': date = date.set('minute', 59); case 'minute': date = date.set('second', 59); case 'second': date = date.set('millisecond', 999); case 'millisecond': return date; default: throw new ArgumentError('Invalid granularity provided to `DateTime#granularity`: ' + granularity); } }; DateTime.prototype.toISOString = function () { return this.date.toISOString(); }; DateTime.prototype.toDateTime = function () { return this; }; DateTime.prototype.toJSON = function () { return { timezone: this.timezone, duration: this.duration, year: this.get('year'), month: this.get('month'), day: this.get('day'), hour: this.get('hour'), minute: this.get('minute'), second: this.get('second'), millisecond: this.get('millisecond'), }; }; DateTime.prototype.valueOf = function () { return this.date.valueOf(); }; DateTime.prototype.assertIsValid = function () { if (isNaN(this.valueOf())) { throw new InvalidDateTimeError('DateTime has invalid date.'); } return true; }; DateTime.prototype.forkDateTime = function (date) { return new DateTime(date, this.timezone, this.duration); }; return DateTime; }()); _a = DATETIME_ID; function assertSameTimeZone(x, y) { if (x.timezone !== y.timezone) { throw new InvalidDateTimeError('Attempted to compare a datetime to another date in a different timezone: ' + JSON.stringify(x) + ' and ' + JSON.stringify(y)); } return true; } function setDateToStartOfWeek(date, wkst) { var index = orderedWeekdays(wkst).indexOf(date.get('weekday')); return date.subtract(index, 'day'); } function setDateToEndOfWeek(date, wkst) { var index = orderedWeekdays(wkst).indexOf(date.get('weekday')); return date.add(6 - index, 'day'); } function dateTimeSortComparer(a, b) { if (a.isAfter(b)) return 1; if (a.isBefore(b)) return -1; if (a.duration && b.duration) { if (a.duration > b.duration) return 1; if (a.duration < b.duration) return -1; } return 0; } function uniqDateTimes(dates) { return Array.from(new Map(dates.map(function (date) { return [date.toISOString(), date]; })).values()); } function orderedWeekdays(wkst) { if (wkst === void 0) { wkst = 'SU'; } var wkdays = WEEKDAYS.slice(); var index = wkdays.indexOf(wkst); while (index !== 0) { shiftArray(wkdays); index--; } return wkdays; } function shiftArray(array, from) { if (from === void 0) { from = 'first'; } if (array.length === 0) { return array; } else if (from === 'first') { array.push(array.shift()); } else { array.unshift(array.pop()); } return array; } /** * Returns the days in the given month. * * @param month base-1 * @param year */ function monthLength(month, year) { var block = { 1: 31, 2: getDaysInFebruary(year), 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31, }; return block[month]; } function getDaysInFebruary(year) { return isLeapYear(year) ? 29 : 28; } // taken from date-fn function isLeapYear(year) { return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0); } function getDaysInYear(year) { return isLeapYear(year) ? 366 : 365; } function getUTCYearDay(now) { var start = new Date(Date.UTC(now.getUTCFullYear(), 0, 1)); var diff = now.valueOf() - start.valueOf(); return 1 + Math.floor(diff / MILLISECONDS_IN_DAY); } /** * These functions are basically lifted from `date-fns`, but changed * to use the UTC date methods, which `date-fns` doesn't support. */ function toInteger(input) { if (input === null || input === true || input === false) { return NaN; } var int = Number(input); if (isNaN(int)) { return int; } return int < 0 ? Math.ceil(int) : Math.floor(int); } function addMilliseconds(dirtyDate, dirtyAmount) { if (arguments.length < 2) { throw new TypeError('2 arguments required, but only ' + arguments.length + ' present'); } var timestamp = dirtyDate.valueOf(); var amount = toInteger(dirtyAmount); return new Date(timestamp + amount); } function addUTCYears(date, input) { var amount = toInteger(input); return addUTCMonths(date, amount * 12); } function addUTCMonths(date, input) { var amount = toInteger(input); date = new Date(date); var desiredMonth = date.getUTCMonth() + amount; var dateWithDesiredMonth = new Date(0); dateWithDesiredMonth.setUTCFullYear(date.getUTCFullYear(), desiredMonth, 1); dateWithDesiredMonth.setUTCHours(0, 0, 0, 0); var daysInMonth = monthLength(dateWithDesiredMonth.getUTCMonth() + 1, dateWithDesiredMonth.getUTCFullYear()); // Set the last day of the new month // if the original date was the last day of the longer month date.setUTCMonth(desiredMonth, Math.min(daysInMonth, date.getUTCDate())); return date; } function addUTCWeeks(date, input) { var amount = toInteger(input); var days = amount * 7; return addUTCDays(date, days); } function addUTCDays(date, input) { // by adding milliseconds rather than days, we supress the native Date object's automatic // daylight savings time conversions which we don't want in UTC mode return addUTCMilliseconds(date, toInteger(input) * MILLISECONDS_IN_DAY); } function addUTCHours(date, input) { var amount = toInteger(input); return addMilliseconds(date, amount * MILLISECONDS_IN_HOUR); } function addUTCMinutes(date, input) { var amount = toInteger(input); return addMilliseconds(date, amount * MILLISECONDS_IN_MINUTE); } function addUTCSeconds(date, input) { var amount = toInteger(input); return addMilliseconds(date, amount * MILLISECONDS_IN_SECOND); } function addUTCMilliseconds(date, input) { var amount = toInteger(input); var timestamp = date.getTime(); return new Date(timestamp + amount); } function subUTCYears(date, amount) { return addUTCYears(date, -amount); } function subUTCMonths(date, amount) { return addUTCMonths(date, -amount); } function subUTCWeeks(date, amount) { return addUTCWeeks(date, -amount); } function subUTCDays(date, amount) { return addUTCDays(date, -amount); } function subUTCHours(date, amount) { return addUTCHours(date, -amount); } function subUTCMinutes(date, amount) { return addUTCMinutes(date, -amount); } function subUTCSeconds(date, amount) { return addUTCSeconds(date, -amount); } function subUTCMilliseconds(date, amount) { return addUTCMilliseconds(date, -amount); } var InvalidDateAdapterError = /** @class */ (function (_super) { __extends(InvalidDateAdapterError, _super); function InvalidDateAdapterError() { return _super !== null && _super.apply(this, arguments) || this; } return InvalidDateAdapterError; }(Error)); var DATE_ADAPTER_ID = Symbol.for('9d2c0b75-7a72-4f24-b57f-c27e131e37b2'); var DateAdapter = /** @class */ (function () { function DateAdapter(_date, _options) { /** * An array of OccurrenceGenerator objects which produced this DateAdapter. * * #### Details * * When a Rule object creates a DateAdapter, that Rule object adds itself to * the DateAdapter's generators property before yielding the DateAdapter. If you are using a Rule * object directly, the process ends there and the DateAdapter is yielded to you (in this case, * generators will have the type `[Rule]`) * * If you are using another object, like a Schedule however, then each DateAdapter is generated * by either a Dates (rdates) or Rule (rrule) within the Schedule. After being originally * generated by a Dates/Rule, the DateAdapter is then filtered by any exdate/exrules and, * assuming it passes, then the DateAdapter "bubbles up" to the Schedule object itself. At this * point the Schedule adds itself to the generators array of the DateAdapter and yields the date * to you. So each DateAdapter produced by a Schedule has a generators property of type * `[Schedule, Rule | Dates]`. * * The generators property pairs well with the `data` property on many OccurrenceGenerators. You * can access the OccurrenceGenerators which produced a DateAdapter via `generators`, and then * access any arbitrary data via the `data` property. * * _Note: occurrence operators are never included in the generators array._ * */ // using `unknown[]` instead of `never[]` to support convenient generator typing in `Calendar`. // If `never[]` is used, then `Calendar#schedules` *must* be typed as a tuple in order to // access any values in `generators` beyond the first (Calendar) value (the rest of the values // get typed as `never`). This would prevent passing a variable to `Calendar#schedules`. this.generators = []; this[_a] = true; } /** * Similar to `Array.isArray()`, `isInstance()` provides a surefire method * of determining if an object is a `DateAdapter` by checking against the * global symbol registry. */ DateAdapter.isInstance = function (object) { return !!(object && typeof object === 'object' && object[DATE_ADAPTER_ID]); }; DateAdapter.isDate = function (_object) { throw unimplementedError('isDate()'); }; DateAdapter.fromJSON = function (_json) { throw unimplementedError('fromJSON()'); }; DateAdapter.fromDateTime = function (_datetime) { throw unimplementedError('fromDateTime()'); }; Object.defineProperty(DateAdapter.prototype, "end", { /** * Returns `undefined` if `this.duration` is falsey. Else returns * the `end` date. */ get: function () { throw unimplementedError('end'); }, enumerable: true, configurable: true }); DateAdapter.prototype.set = function (_prop, _value) { throw unimplementedError('set()'); }; DateAdapter.prototype.valueOf = function () { throw unimplementedError('valueOf()'); }; DateAdapter.prototype.toISOString = function () { throw unimplementedError('toISOString()'); }; DateAdapter.prototype.toDateTime = function () { var _a; var date = DateTime.fromJSON(this.toJSON()); (_a = date.generators).push.apply(_a, __spread(this.generators)); return date; }; DateAdapter.prototype.toJSON = function () { throw unimplementedError('toJSON()'); }; DateAdapter.prototype.assertIsValid = function () { throw unimplementedError('assertIsValid()'); }; var _a; _a = DATE_ADAPTER_ID; DateAdapter.hasTimezoneSupport = false; return DateAdapter; }()); function unimplementedError(name) { return new Error("You must implement the \"" + name + "\" method for this DateAdapter class"); } var OccurrenceIterator = /** @class */ (function () { function OccurrenceIterator(iterable, args) { var _this = this; this.iterable = iterable; this.args = args; this[Symbol.iterator] = function () { return _this._run(); }; this.iterator = iterable._run(args); this.isInfinite = iterable.isInfinite; } OccurrenceIterator.prototype.next = function (args) { return this._run(args).next(); }; OccurrenceIterator.prototype.toArray = function () { if (this.args.end || this.args.take || !this.isInfinite) { return Array.from(this._run()); } throw new InfiniteLoopError('OccurrenceIterator#toArray() can only be called if the iterator ' + 'is not infinite, or you provide and `end` argument, or you provide ' + 'a `take` argument.'); }; OccurrenceIterator.prototype._run = function (rawArgs) { var args, date, yieldArgs; return __generator(this, function (_a) { switch (_a.label) { case 0: args = this.normalizeRunArgs(rawArgs); date = this.iterator.next(args).value; _a.label = 1; case 1: if (!date) return [3 /*break*/, 3]; return [4 /*yield*/, this.normalizeDateOutput(date)]; case 2: yieldArgs = _a.sent(); args = this.normalizeRunArgs(yieldArgs); date = this.iterator.next(args).value; return [3 /*break*/, 1]; case 3: return [2 /*return*/]; } }); }; OccurrenceIterator.prototype.normalizeRunArgs = function (args) { return { skipToDate: this.normalizeDateInput(args && args.skipToDate), }; }; OccurrenceIterator.prototype.normalizeDateInput = function (date) { if (!date) { return; } return DateAdapter.isInstance(date) ? date.set('timezone', this.iterable.timezone).toDateTime() : new this.iterable.dateAdapter(date).set('timezone', this.iterable.timezone).toDateTime(); }; OccurrenceIterator.prototype.normalizeDateOutput = function (date) { if (!date) { return; } return this.iterable.dateAdapter.fromDateTime(date); }; return OccurrenceIterator; }()); var CollectionIterator = /** @class */ (function () { function CollectionIterator(iterable, args) { var _this = this; this.iterable = iterable; this.args = args; this.granularity = 'INSTANTANIOUSLY'; this[Symbol.iterator] = function () { return _this.iterator; }; if (args.granularity) { this.granularity = args.granularity; } if (args.weekStart) { this.weekStart = args.weekStart; } if (args.reverse) { throw new Error('`Calendar#collections()` does not support iterating in reverse. ' + 'Though `Calendar#occurrences()` does support iterating in reverse.'); } // Set the end arg, if present, to the end of the period. this.args = __assign({}, args, { start: args.start || iterable._run().next().value, end: args.end && this.getPeriod(args.end).end }); this.startDate = (this.args.start && this.normalizeDateOutput(this.getPeriod(this.args.start).start)) || null; this.iterator = this._run(); } CollectionIterator.prototype.next = function () { return this.iterator.next(); }; /** * While `next()` and `[Symbol.iterator]` both share state, * `toArray()` does not share state and always returns the whole * collections array. */ CollectionIterator.prototype.toArray = function () { var e_1, _a; if (this.args.end || this.args.take || !this.iterable.isInfinite) { var collections = []; try { for (var _b = __values(this._run()), _c = _b.next(); !_c.done; _c = _b.next()) { var collection = _c.value; collections.push(collection); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return collections; } throw new InfiniteLoopError('CollectionIterator#toArray() can only be called if the iterator ' + 'is not infinite, or you provide and `end` argument, or you provide ' + 'a `take` argument.'); }; CollectionIterator.prototype.normalizeDateOutput = function (date) { if (!date) return; return this.iterable.dateAdapter.fromDateTime(date); }; CollectionIterator.prototype._run = function () { var iterator, date, period, dates, index; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.startDate) return [2 /*return*/]; iterator = this.occurrenceIterator(this.iterable, this.args); date = iterator.next().value; if (!date) return [2 /*return*/]; period = this.getPeriod(this.args.start); dates = []; index = 0; _a.label = 1; case 1: if (!(date && (this.args.take === undefined || this.args.take > index))) return [3 /*break*/, 3]; while (date && date.isBeforeOrEqual(period.end)) { dates.push(date); date = iterator.next().value; } return [4 /*yield*/, new Collection(dates.map(function (date) { return _this.normalizeDateOutput(date); }), this.granularity, this.normalizeDateOutput(period.start), this.normalizeDateOutput(period.end))]; case 2: _a.sent(); if (!date) return [2 /*return*/]; dates = []; period = this.args.incrementLinearly ? this.getPeriod(this.incrementPeriod(period.period)) : this.getPeriod(date); // With these args, periods may overlap and the same date may show up // in two periods. Because of this, we need to reset the iterator // (otherwise it won't spit out a date it has already spit out). if (this.granularity === 'MONTHLY' && this.weekStart) { iterator = this.iterable._run({ start: period.start, end: this.args.end, }); date = iterator.next().value; } index++; return [3 /*break*/, 1]; case 3: return [2 /*return*/]; } }); }; CollectionIterator.prototype.getPeriod = function (date) { var granularity = freqToGranularity(this.granularity); var start; var end; var period; if (this.granularity === 'MONTHLY' && this.weekStart) { start = date.granularity('month').granularity('week', { weekStart: this.weekStart }); end = date.endGranularity('month').endGranularity('week', { weekStart: this.weekStart }); period = start; } else if (this.granularity === 'WEEKLY') { if (!this.weekStart) { throw new ArgumentError('"WEEKLY" granularity requires `weekStart` arg'); } start = date.granularity('week', { weekStart: this.weekStart }); end = date.endGranularity('week', { weekStart: this.weekStart }); period = start; } else { start = date.granularity(granularity); end = date.endGranularity(granularity); period = start; } return { start: start, end: end, period: period }; }; CollectionIterator.prototype.incrementPeriod = function (date) { switch (this.granularity) { case 'YEARLY': return date.add(1, 'year'); case 'MONTHLY': return date.add(1, 'month'); case 'WEEKLY': return date.add(1, 'week'); case 'DAILY': return date.add(1, 'day'); case 'HOURLY': return date.add(1, 'hour'); case 'MINUTELY': return date.add(1, 'minute'); case 'SECONDLY': return date.add(1, 'second'); case 'INSTANTANIOUSLY': default: return date.add(1, 'millisecond'); } }; CollectionIterator.prototype.occurrenceIterator = function (iterable, args) { var start = args.start || iterable._run().next().value; if (!start) return iterable._run(args); start = this.getPeriod(start).start; return iterable._run({ start: start, end: args.end, }); }; return CollectionIterator; }()); var Collection = /** @class */ (function () { function Collection(dates, granularity, periodStart, periodEnd) { if (dates === void 0) { dates = []; } this.dates = dates; this.granularity = granularity; this.periodStart = periodStart; this.periodEnd = periodEnd; } return Collection; }()); var PipeError = /** @class */ (function (_super) { __extends(PipeError, _super); function PipeError() { return _super !== null && _super.apply(this, arguments) || this; } return PipeError; }(Error)); var PipeRuleBase = /** @class */ (function () { function PipeRuleBase(args) { this.start = args.start; this.end = args.end; this.options = args.options; } return PipeRuleBase; }()); var PipeRule = /** @class */ (function (_super) { __extends(PipeRule, _super); function PipeRule() { return _super !== null && _super.apply(this, arguments) || this; } PipeRule.prototype.nextValidDate = function (args, skipToDate) { return this.nextPipe.run({ date: args.date, invalidDate: true, skipToDate: skipToDate, }); }; return PipeRule; }(PipeRuleBase)); /** * The `FrequencyPipe` is the first pipe in the chain of rule pipes. It is * responsible for incrementing the date, as appropriate, while taking into * account the `RRULE` frequency and interval. */ var FrequencyPipe = /** @class */ (function (_super) { __extends(FrequencyPipe, _super); function FrequencyPipe() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.intervalUnit = freqToGranularity(_this.options.frequency); _this.intervalStartDate = _this.normalizedStartDate(_this.start); _this.intervalEndDate = _this.normalizedEndDate(_this.intervalStartDate); return _this; } FrequencyPipe.prototype.run = function (args) { var date = args.date; // if a date is invalid, skipToDate will always be present // skipToDate may also be passed by a user on an otherwise valid date if (args.skipToDate) { date = args.skipToDate; this.skipToIntervalOnOrAfter(date); if (!this.dateIsWithinInterval(date)) { // this only applies when the interval is not 1 date = this.intervalStartDate; } } else if (this.dateIsWithinInterval(date) && this.dateIsWithinInterval(date.add(1, 'millisecond'))) { date = date.add(1, 'millisecond'); } else { this.incrementInterval(this.options.interval); date = this.intervalStartDate; } return this.nextPipe.run({ date: date }); }; FrequencyPipe.prototype.normalizedStartDate = function (date) { if (this.options.frequency === 'WEEKLY') { return date.granularity('week', { weekStart: this.options.weekStart }); } return date.granularity(this.intervalUnit); }; FrequencyPipe.prototype.normalizedEndDate = function (start) { switch (this.options.frequency) { case 'YEARLY': return start.add(1, 'year'); case 'MONTHLY': return start.add(1, 'month'); case 'WEEKLY': return start.add(1, 'week'); case 'DAILY': return start.add(1, 'day'); case 'HOURLY': return start.add(1, 'hour'); case 'MINUTELY': return start.add(1, 'minute'); case 'SECONDLY': return start.add(1, 'second'); case 'MILLISECONDLY': return start.add(1, 'millisecond'); } }; FrequencyPipe.prototype.incrementInterval = function (amount) { this.intervalStartDate = this.normalizedStartDate(this.intervalStartDate.add(amount, this.intervalUnit)); this.intervalEndDate = this.normalizedEndDate(this.intervalStartDate); }; FrequencyPipe.prototype.skipToIntervalOnOrAfter = function (date) { this.incrementInterval(intervalDifferenceBetweenDates({ first: this.intervalStartDate, second: date, unit: this.intervalUnit, interval: this.options.interval, weekStart: this.options.weekStart, })); }; FrequencyPipe.prototype.dateIsWithinInterval = function (date) { return this.intervalStartDate.isBeforeOrEqual(date) && this.intervalEndDate.isAfter(date); }; return FrequencyPipe; }(PipeRule)); /** * Given the frequency (unit) and interval, this function finds * how many jumps forward the first date needs in order to equal * or exceed the second date. * * For example: * * 1. Unit is daily and interval is 1. The second date is 3 days * after the first. This will return 3. * 2. Unit is yearly and interval is 1. The second date is 3 days * after the first. This will return 0. * 3. Unit is yearly and interval is 3. The second date is 4 years * after the first. This will return 6. */ function intervalDifferenceBetweenDates(_a) { var first = _a.first, second = _a.second, unit = _a.unit, interval = _a.interval, weekStart = _a.weekStart; var difference = (function () { var intervalDuration; switch (unit) { case 'year': var years = (second.get('year') - first.get('year')) * 12; years = years + second.get('month') - first.get('month'); return Math.floor(years / 12); case 'month': var months = (second.get('year') - first.get('year')) * 12; months = months + second.get('month') - first.get('month'); return months; case 'week': first = first.granularity('week', { weekStart: weekStart }); intervalDuration = MILLISECONDS_IN_WEEK; break; case 'day': intervalDuration = MILLISECONDS_IN_DAY; break; case 'hour': intervalDuration = MILLISECONDS_IN_HOUR; break; case 'minute': intervalDuration = MILLISECONDS_IN_MINUTE; break; case 'second': intervalDuration = MILLISECONDS_IN_SECOND; break; case 'millisecond': intervalDuration = 1; break; default: throw new Error('Unexpected `unit` value'); } var diff = second.valueOf() - first.valueOf(); var sign = Math.sign(diff); return Math.floor(Math.abs(diff) / intervalDuration) * sign; })(); if (difference > 0 && difference < interval) { difference = interval; } else if (difference > interval) { difference = Math.ceil(difference / interval) * interval; } return difference; } var ByMonthOfYearPipe = /** @class */ (function (_super) { __extends(ByMonthOfYearPipe, _super); function ByMonthOfYearPipe() { return _super !== null && _super.apply(this, arguments) || this; } ByMonthOfYearPipe.prototype.run = function (args) { var e_1, _a; if (args.invalidDate) { return this.nextPipe.