diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..067b6d5c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "0.10" + - "0.12" + - "4" + - "5" +sudo: false diff --git a/lib/nlp.js b/lib/nlp.js index 04b3743b..9b379102 100644 --- a/lib/nlp.js +++ b/lib/nlp.js @@ -17,644 +17,571 @@ * when those functions are used. * */ -(function (root){ - - -var serverSide = typeof module !== 'undefined' && module.exports; -var RRule; - - -if (serverSide) { - RRule = require('./rrule').RRule; -} else if (root.RRule) { - RRule = root.RRule; -} else if (typeof require !== 'undefined') { - if (!RRule) {RRule = require('rrule');} -} else { +;(function (root) { + var serverSide = typeof module !== 'undefined' && module.exports + var RRule + + if (serverSide) { + RRule = require('./rrule').RRule + } else if (root.RRule) { + RRule = root.RRule + } else if (typeof require !== 'undefined') { + if (!RRule) RRule = require('rrule') + } else { throw new Error('rrule.js is required for rrule/nlp.js to work') -} - - -//============================================================================= -// Helper functions -//============================================================================= - -/** - * Return true if a value is in an array - */ -var contains = function(arr, val) { - return arr.indexOf(val) != -1; -}; - - -//============================================================================= -// ToText -//============================================================================= - - -/** - * - * @param {RRule} rrule - * Optional: - * @param {Function} gettext function - * @param {Object} language definition - * @constructor - */ -var ToText = function(rrule, gettext, language) { - - this.gettext = gettext || function(id) {return id}; - this.language = language || ENGLISH; - this.text = ''; + } + + // ============================================================================= + // Helper functions + // ============================================================================= + + /** + * Return true if a value is in an array + */ + var contains = function (arr, val) { + return arr.indexOf(val) !== -1 + } + + // ============================================================================= + // ToText + // ============================================================================= + + /** + * + * @param {RRule} rrule + * Optional: + * @param {Function} gettext function + * @param {Object} language definition + * @constructor + */ + var ToText = function (rrule, gettext, language) { + this.text = '' + this.language = language || ENGLISH + this.gettext = gettext || function (id) { + return id + } - this.rrule = rrule; - this.freq = rrule.options.freq; - this.options = rrule.options; - this.origOptions = rrule.origOptions; + this.rrule = rrule + this.freq = rrule.options.freq + this.options = rrule.options + this.origOptions = rrule.origOptions if (this.origOptions.bymonthday) { - var bymonthday = [].concat(this.options.bymonthday); - var bynmonthday = [].concat(this.options.bynmonthday); - bymonthday.sort(); - bynmonthday.sort(); - bynmonthday.reverse(); - // 1, 2, 3, .., -5, -4, -3, .. - this.bymonthday = bymonthday.concat(bynmonthday); - if (!this.bymonthday.length) { - this.bymonthday = null; - } + var bymonthday = [].concat(this.options.bymonthday) + var bynmonthday = [].concat(this.options.bynmonthday) + + bymonthday.sort() + bynmonthday.sort() + bynmonthday.reverse() + // 1, 2, 3, .., -5, -4, -3, .. + this.bymonthday = bymonthday.concat(bynmonthday) + if (!this.bymonthday.length) this.bymonthday = null } if (this.origOptions.byweekday) { - var byweekday = !(this.origOptions.byweekday instanceof Array) - ? [this.origOptions.byweekday] - : this.origOptions.byweekday; - var days = String(byweekday); - this.byweekday = { - allWeeks:byweekday.filter(function (weekday) { - return !Boolean(weekday.n); - }), - someWeeks:byweekday.filter(function (weekday) { - return Boolean(weekday.n); - }), - isWeekdays:( - days.indexOf('MO') != -1 && - days.indexOf('TU') != -1 && - days.indexOf('WE') != -1 && - days.indexOf('TH') != -1 && - days.indexOf('FR') != -1 && - days.indexOf('SA') == -1 && - days.indexOf('SU') == -1 - ) - }; - - - var sortWeekDays = function(a, b) { - return a.weekday - b.weekday; - }; - - this.byweekday.allWeeks.sort(sortWeekDays); - this.byweekday.someWeeks.sort(sortWeekDays); - - if (!this.byweekday.allWeeks.length) { - this.byweekday.allWeeks = null; - } - if (!this.byweekday.someWeeks.length) { - this.byweekday.someWeeks = null; - } + var byweekday = !(this.origOptions.byweekday instanceof Array) + ? [this.origOptions.byweekday] : this.origOptions.byweekday + var days = String(byweekday) + + this.byweekday = { + allWeeks: byweekday.filter(function (weekday) { + return !Boolean(weekday.n) + }), + someWeeks: byweekday.filter(function (weekday) { + return Boolean(weekday.n) + }), + isWeekdays: ( + days.indexOf('MO') !== -1 && + days.indexOf('TU') !== -1 && + days.indexOf('WE') !== -1 && + days.indexOf('TH') !== -1 && + days.indexOf('FR') !== -1 && + days.indexOf('SA') === -1 && + days.indexOf('SU') === -1 + ) + } + + var sortWeekDays = function (a, b) { + return a.weekday - b.weekday + } + + this.byweekday.allWeeks.sort(sortWeekDays) + this.byweekday.someWeeks.sort(sortWeekDays) + + if (!this.byweekday.allWeeks.length) this.byweekday.allWeeks = null + if (!this.byweekday.someWeeks.length) this.byweekday.someWeeks = null + } else { + this.byweekday = null } - else { - this.byweekday = null; - } - -}; + } - -ToText.IMPLEMENTED = []; -var common = [ + var common = [ 'count', 'until', 'interval', 'byweekday', 'bymonthday', 'bymonth' -]; -ToText.IMPLEMENTED[RRule.HOURLY] = common; -ToText.IMPLEMENTED[RRule.DAILY] = ['byhour'].concat(common); -ToText.IMPLEMENTED[RRule.WEEKLY] = common; -ToText.IMPLEMENTED[RRule.MONTHLY] = common; -ToText.IMPLEMENTED[RRule.YEARLY] = ['byweekno', 'byyearday'].concat(common); - -/** - * Test whether the rrule can be fully converted to text. - * @param {RRule} rrule - * @return {Boolean} - */ -ToText.isFullyConvertible = function(rrule) { - var canConvert = true; + ] + ToText.IMPLEMENTED = [] + ToText.IMPLEMENTED[RRule.HOURLY] = common + ToText.IMPLEMENTED[RRule.DAILY] = ['byhour'].concat(common) + ToText.IMPLEMENTED[RRule.WEEKLY] = common + ToText.IMPLEMENTED[RRule.MONTHLY] = common + ToText.IMPLEMENTED[RRule.YEARLY] = ['byweekno', 'byyearday'].concat(common) + + /** + * Test whether the rrule can be fully converted to text. + * @param {RRule} rrule + * @return {Boolean} + */ + ToText.isFullyConvertible = function (rrule) { + var canConvert = true + + if (!(rrule.options.freq in ToText.IMPLEMENTED)) return false + if (rrule.origOptions.until && rrule.origOptions.count) return false - if (!(rrule.options.freq in ToText.IMPLEMENTED)) { - return false; - } - if (rrule.origOptions.until && rrule.origOptions.count) { - return false; - } for (var key in rrule.origOptions) { - if (contains(['dtstart', 'wkst', 'freq'], key)) { - return true; - } - if (!contains(ToText.IMPLEMENTED[rrule.options.freq], key)) { - canConvert = false; - return false; - } + if (contains(['dtstart', 'wkst', 'freq'], key)) return true + if (!contains(ToText.IMPLEMENTED[rrule.options.freq], key)) return false } - return canConvert; -}; - + return canConvert + } -ToText.prototype = { - - - isFullyConvertible: function() { - return ToText.isFullyConvertible(this.rrule); + ToText.prototype = { + isFullyConvertible: function () { + return ToText.isFullyConvertible(this.rrule) }, - /** * Perform the conversion. Only some of the frequencies are supported. * If some of the rrule's options aren't supported, they'll * be omitted from the output an "(~ approximate)" will be appended. * @return {*} */ - toString: function() { - - var gettext = this.gettext; + toString: function () { + var gettext = this.gettext + + if (!(this.options.freq in ToText.IMPLEMENTED)) { + return gettext('RRule error: Unable to fully convert this rrule to text') + } + + this.text = [gettext('every')] + this[RRule.FREQUENCIES[this.options.freq]]() + + if (this.options.until) { + this.add(gettext('until')) + var until = this.options.until + this.add(this.language.monthNames[until.getMonth()]) + .add(until.getDate() + ',') + .add(until.getFullYear()) + } else if (this.options.count) { + this.add(gettext('for')) + .add(this.options.count) + .add(this.plural(this.options.count) + ? gettext('times') : gettext('time')) + } + + if (!this.isFullyConvertible()) this.add(gettext('(~ approximate)')) + + return this.text.join('') + }, - if (!(this.options.freq in ToText.IMPLEMENTED)) { - return gettext( - 'RRule error: Unable to fully convert this rrule to text'); - } + HOURLY: function () { + var gettext = this.gettext - this.text = [gettext('every')]; - - this[RRule.FREQUENCIES[this.options.freq]](); - - if (this.options.until) { - this.add(gettext('until')); - var until = this.options.until; - this.add(this.language.monthNames[until.getMonth()]) - .add(until.getDate() + ',') - .add(until.getFullYear()); - } else if (this.options.count) { - this.add(gettext('for')) - .add(this.options.count) - .add(this.plural(this.options.count) - ? gettext('times') - : gettext('time')); - } + if (this.options.interval !== 1) this.add(this.options.interval) - if (!this.isFullyConvertible()) { - this.add(gettext('(~ approximate)')); - } - return this.text.join(''); + this.add(this.plural(this.options.interval) + ? gettext('hours') : gettext('hour')) }, - HOURLY: function() { - var gettext = this.gettext; - if (this.options.interval != 1) { - this.add(this.options.interval); - } + DAILY: function () { + var gettext = this.gettext - this.add(this.plural(this.options.interval) - ? gettext('hours') : gettext('hour')); + if (this.options.interval !== 1) this.add(this.options.interval) + if (this.byweekday && this.byweekday.isWeekdays) { + this.add(this.plural(this.options.interval) + ? gettext('weekdays') : gettext('weekday')) + } else { + this.add(this.plural(this.options.interval) + ? gettext('days') : gettext('day')) + } + + if (this.origOptions.bymonth) { + this.add(gettext('in')) + this._bymonth() + } + + if (this.bymonthday) { + this._bymonthday() + } else if (this.byweekday) { + this._byweekday() + } else if (this.origOptions.byhour) { + this._byhour() + } }, - DAILY: function() { - var gettext = this.gettext; - if (this.options.interval != 1) { - this.add(this.options.interval); - } + WEEKLY: function () { + var gettext = this.gettext + + if (this.options.interval !== 1) { + this.add(this.options.interval) + .add(this.plural(this.options.interval) + ? gettext('weeks') : gettext('week')) + } - if (this.byweekday && this.byweekday.isWeekdays) { - this.add(this.plural(this.options.interval) - ? gettext('weekdays') - : gettext('weekday')); + if (this.byweekday && this.byweekday.isWeekdays) { + if (this.options.interval === 1) { + this.add(this.plural(this.options.interval) + ? gettext('weekdays') : gettext('weekday')) } else { - this.add(this.plural(this.options.interval) - ? gettext('days') : gettext('day')); + this.add(gettext('on')).add(gettext('weekdays')) } + } else { + if (this.options.interval === 1) this.add(gettext('week')) if (this.origOptions.bymonth) { - this.add(gettext('in')); - this._bymonth(); + this.add(gettext('in')) + this._bymonth() } if (this.bymonthday) { - this._bymonthday(); + this._bymonthday() } else if (this.byweekday) { - this._byweekday(); - } else if (this.origOptions.byhour) { - this._byhour(); + this._byweekday() } - + } }, - WEEKLY: function() { - var gettext = this.gettext; - if (this.options.interval != 1) { - this.add(this.options.interval).add( - this.plural(this.options.interval) - ? gettext('weeks') - : gettext('week')); - } - - if (this.byweekday && this.byweekday.isWeekdays) { - - if (this.options.interval == 1) { - this.add(this.plural(this.options.interval) - ? gettext('weekdays') - : gettext('weekday')); - } else { - this.add(gettext('on')).add(gettext('weekdays')); - } + MONTHLY: function () { + var gettext = this.gettext + if (this.origOptions.bymonth) { + if (this.options.interval !== 1) { + this.add(this.options.interval).add(gettext('months')) + if (this.plural(this.options.interval)) this.add(gettext('in')) } else { - - if (this.options.interval == 1) { - this.add(gettext('week')) - } - - if (this.origOptions.bymonth) { - this.add(gettext('in')); - this._bymonth(); - } - - if (this.bymonthday) { - this._bymonthday(); - } else if (this.byweekday) { - this._byweekday(); - } + // this.add(gettext('MONTH')) } - + this._bymonth() + } else { + if (this.options.interval !== 1) this.add(this.options.interval) + this.add(this.plural(this.options.interval) + ? gettext('months') : gettext('month')) + } + if (this.bymonthday) { + this._bymonthday() + } else if (this.byweekday && this.byweekday.isWeekdays) { + this.add(gettext('on')).add(gettext('weekdays')) + } else if (this.byweekday) { + this._byweekday() + } }, - MONTHLY: function() { - var gettext = this.gettext; - if (this.origOptions.bymonth) { - if (this.options.interval != 1) { - this.add(this.options.interval).add(gettext('months')); - if (this.plural(this.options.interval)) { - this.add(gettext('in')); - } - } else { - //this.add(gettext('MONTH')); - } - this._bymonth(); - } else { - if (this.options.interval != 1) { - this.add(this.options.interval); - } - this.add(this.plural(this.options.interval) - ? gettext('months') - : gettext('month')); - } - if (this.bymonthday) { - this._bymonthday(); - } else if (this.byweekday && this.byweekday.isWeekdays) { - this.add(gettext('on')).add(gettext('weekdays')); - } else if (this.byweekday) { - this._byweekday(); - } - }, + YEARLY: function () { + var gettext = this.gettext - YEARLY: function() { - var gettext = this.gettext; - if (this.origOptions.bymonth) { - if (this.options.interval != 1) { - this.add(this.options.interval); - this.add(gettext('years')); - } else { - // this.add(gettext('YEAR')); - } - this._bymonth(); + if (this.origOptions.bymonth) { + if (this.options.interval !== 1) { + this.add(this.options.interval) + this.add(gettext('years')) } else { - if (this.options.interval != 1) { - this.add(this.options.interval); - } - this.add(this.plural(this.options.interval) - ? gettext('years') - : gettext('year')); - } - - - if (this.bymonthday) { - this._bymonthday(); - } else if (this.byweekday) { - this._byweekday(); - } - - - if (this.options.byyearday) { - this.add(gettext('on the')) - .add(this.list(this.options.byyearday, - this.nth, gettext('and'))) - .add(gettext('day')); - } - - if (this.options.byweekno) { - this.add(gettext('in')) - .add(this.plural(this.options.byweekno.length) - ? gettext('weeks') : gettext('week')) - .add(this.list(this.options.byweekno, null, gettext('and'))); + // this.add(gettext('YEAR')) } + this._bymonth() + } else { + if (this.options.interval !== 1) this.add(this.options.interval) + this.add(this.plural(this.options.interval) + ? gettext('years') : gettext('year')) + } + + if (this.bymonthday) { + this._bymonthday() + } else if (this.byweekday) { + this._byweekday() + } + + if (this.options.byyearday) { + this.add(gettext('on the')) + .add(this.list(this.options.byyearday, this.nth, gettext('and'))) + .add(gettext('day')) + } + + if (this.options.byweekno) { + this.add(gettext('in')) + .add(this.plural(this.options.byweekno.length) ? gettext('weeks') : gettext('week')) + .add(this.list(this.options.byweekno, null, gettext('and'))) + } }, - _bymonthday: function() { - var gettext = this.gettext; - if (this.byweekday && this.byweekday.allWeeks) { - this.add(gettext('on')) - .add(this.list(this.byweekday.allWeeks, - this.weekdaytext, gettext('or'))) - .add(gettext('the')) - .add(this.list(this.bymonthday, this.nth, gettext('or'))); - } else { - this.add(gettext('on the')) - .add(this.list(this.bymonthday, this.nth, gettext('and'))); - } - //this.add(gettext('DAY')); + _bymonthday: function () { + var gettext = this.gettext + if (this.byweekday && this.byweekday.allWeeks) { + this.add(gettext('on')) + .add(this.list(this.byweekday.allWeeks, this.weekdaytext, gettext('or'))) + .add(gettext('the')) + .add(this.list(this.bymonthday, this.nth, gettext('or'))) + } else { + this.add(gettext('on the')) + .add(this.list(this.bymonthday, this.nth, gettext('and'))) + } + // this.add(gettext('DAY')) }, - _byweekday: function() { - var gettext = this.gettext; - if (this.byweekday.allWeeks && !this.byweekday.isWeekdays) { - this.add(gettext('on')) - .add(this.list(this.byweekday.allWeeks, this.weekdaytext)); - } + _byweekday: function () { + var gettext = this.gettext + if (this.byweekday.allWeeks && !this.byweekday.isWeekdays) { + this.add(gettext('on')) + .add(this.list(this.byweekday.allWeeks, this.weekdaytext)) + } - if (this.byweekday.someWeeks) { + if (this.byweekday.someWeeks) { + if (this.byweekday.allWeeks) this.add(gettext('and')) - if (this.byweekday.allWeeks) { - this.add(gettext('and')); - } - - this.add(gettext('on the')) - .add(this.list(this.byweekday.someWeeks, - this.weekdaytext, - gettext('and'))); - } + this.add(gettext('on the')) + .add(this.list(this.byweekday.someWeeks, this.weekdaytext, gettext('and'))) + } }, - _byhour: function() { - var gettext = this.gettext; - this.add(gettext('at')) - .add(this.list(this.origOptions.byhour, null, gettext('and'))); - }, + _byhour: function () { + var gettext = this.gettext - _bymonth: function() { - this.add(this.list(this.options.bymonth, - this.monthtext, - this.gettext('and'))); + this.add(gettext('at')) + .add(this.list(this.origOptions.byhour, null, gettext('and'))) }, - nth: function(n) { - var nth, npos, gettext = this.gettext; - - if (n == -1) { - return gettext('last'); - } - - npos = Math.abs(n); - - switch(npos) { - case 1: - case 21: - case 31: - nth = npos + gettext('st'); - break; - case 2: - case 22: - nth = npos + gettext('nd'); - break; - case 3: - case 23: - nth = npos + gettext('rd'); - break; - default: - nth = npos + gettext('th'); - } + _bymonth: function () { + this.add(this.list(this.options.bymonth, this.monthtext, this.gettext('and'))) + }, - return n < 0 ? nth + ' ' + gettext('last') : nth; + nth: function (n) { + var nth, npos + var gettext = this.gettext + + if (n === -1) return gettext('last') + + npos = Math.abs(n) + switch (npos) { + case 1: + case 21: + case 31: + nth = npos + gettext('st') + break + case 2: + case 22: + nth = npos + gettext('nd') + break + case 3: + case 23: + nth = npos + gettext('rd') + break + default: + nth = npos + gettext('th') + } + return n < 0 ? nth + ' ' + gettext('last') : nth }, - monthtext: function(m) { - return this.language.monthNames[m - 1]; + monthtext: function (m) { + return this.language.monthNames[m - 1] }, - weekdaytext: function(wday) { - var weekday = typeof wday === 'number' ? wday : wday.getJsWeekday(); - return (wday.n ? this.nth(wday.n) + ' ' : '') - + this.language.dayNames[weekday]; + weekdaytext: function (wday) { + var weekday = typeof wday === 'number' ? wday : wday.getJsWeekday() + return (wday.n ? this.nth(wday.n) + ' ' : '') + + this.language.dayNames[weekday] }, - plural: function(n) { - return n % 100 != 1; + plural: function (n) { + return n % 100 !== 1 }, - add: function(s) { - this.text.push(' '); - this.text.push(s); - return this; + add: function (s) { + this.text.push(' ') + this.text.push(s) + return this }, - list: function(arr, callback, finalDelim, delim) { - - var delimJoin = function (array, delimiter, finalDelimiter) { - var list = ''; - for(var i = 0; i < array.length; i++) { - if (i != 0) { - if (i == array.length - 1) { - list += ' ' + finalDelimiter + ' '; - } else { - list += delimiter + ' '; - } - } - list += array[i]; - } - return list; - }; - - delim = delim || ','; - callback = callback || (function(o){return o;}); - var self = this; - var realCallback = function(arg) { - return callback.call(self, arg); - }; - - if (finalDelim) { - return delimJoin(arr.map(realCallback), delim, finalDelim); - } else { - return arr.map(realCallback).join(delim + ' '); - } - + list: function (arr, callback, finalDelim, delim) { + var delimJoin = function (array, delimiter, finalDelimiter) { + var list = '' + for (var i = 0; i < array.length; i++) { + if (i !== 0) { + if (i === array.length - 1) { + list += ' ' + finalDelimiter + ' ' + } else { + list += delimiter + ' ' + } + } + list += array[i] + } + return list + } + + delim = delim || ',' + callback = callback || function (o) { + return o + } + var self = this + var realCallback = function (arg) { + return callback.call(self, arg) + } + + if (finalDelim) { + return delimJoin(arr.map(realCallback), delim, finalDelim) + } else { + return arr.map(realCallback).join(delim + ' ') + } } - - -}; - - -//============================================================================= -// fromText -//============================================================================= -/** - * Will be able to convert some of the below described rules from - * text format to a rule object. - * - * - * RULES - * - * Every ([n]) - * day(s) - * | [weekday], ..., (and) [weekday] - * | weekday(s) - * | week(s) - * | month(s) - * | [month], ..., (and) [month] - * | year(s) - * - * - * Plus 0, 1, or multiple of these: - * - * on [weekday], ..., (or) [weekday] the [monthday], [monthday], ... (or) [monthday] - * - * on [weekday], ..., (and) [weekday] - * - * on the [monthday], [monthday], ... (and) [monthday] (day of the month) - * - * on the [nth-weekday], ..., (and) [nth-weekday] (of the month/year) - * - * - * Plus 0 or 1 of these: - * - * for [n] time(s) - * - * until [date] - * - * Plus (.) - * - * - * Definitely no supported for parsing: - * - * (for year): - * in week(s) [n], ..., (and) [n] - * - * on the [yearday], ..., (and) [n] day of the year - * on day [yearday], ..., (and) [n] - * - * - * NON-TERMINALS - * - * [n]: 1, 2 ..., one, two, three .. - * [month]: January, February, March, April, May, ... December - * [weekday]: Monday, ... Sunday - * [nth-weekday]: first [weekday], 2nd [weekday], ... last [weekday], ... - * [monthday]: first, 1., 2., 1st, 2nd, second, ... 31st, last day, 2nd last day, .. - * [date]: - * [month] (0-31(,) ([year])), - * (the) 0-31.(1-12.([year])), - * (the) 0-31/(1-12/([year])), - * [weekday] - * - * [year]: 0000, 0001, ... 01, 02, .. - * - * Definitely not supported for parsing: - * - * [yearday]: first, 1., 2., 1st, 2nd, second, ... 366th, last day, 2nd last day, .. - * - * @param {String} text - * @return {Object, Boolean} the rule, or null. - */ -var fromText = function(text, language) { + } + + // ============================================================================= + // fromText + // ============================================================================= + /** + * Will be able to convert some of the below described rules from + * text format to a rule object. + * + * + * RULES + * + * Every ([n]) + * day(s) + * | [weekday], ..., (and) [weekday] + * | weekday(s) + * | week(s) + * | month(s) + * | [month], ..., (and) [month] + * | year(s) + * + * + * Plus 0, 1, or multiple of these: + * + * on [weekday], ..., (or) [weekday] the [monthday], [monthday], ... (or) [monthday] + * + * on [weekday], ..., (and) [weekday] + * + * on the [monthday], [monthday], ... (and) [monthday] (day of the month) + * + * on the [nth-weekday], ..., (and) [nth-weekday] (of the month/year) + * + * + * Plus 0 or 1 of these: + * + * for [n] time(s) + * + * until [date] + * + * Plus (.) + * + * + * Definitely no supported for parsing: + * + * (for year): + * in week(s) [n], ..., (and) [n] + * + * on the [yearday], ..., (and) [n] day of the year + * on day [yearday], ..., (and) [n] + * + * + * NON-TERMINALS + * + * [n]: 1, 2 ..., one, two, three .. + * [month]: January, February, March, April, May, ... December + * [weekday]: Monday, ... Sunday + * [nth-weekday]: first [weekday], 2nd [weekday], ... last [weekday], ... + * [monthday]: first, 1., 2., 1st, 2nd, second, ... 31st, last day, 2nd last day, .. + * [date]: + * [month] (0-31(,) ([year])), + * (the) 0-31.(1-12.([year])), + * (the) 0-31/(1-12/([year])), + * [weekday] + * + * [year]: 0000, 0001, ... 01, 02, .. + * + * Definitely not supported for parsing: + * + * [yearday]: first, 1., 2., 1st, 2nd, second, ... 366th, last day, 2nd last day, .. + * + * @param {String} text + * @return {Object, Boolean} the rule, or null. + */ + var fromText = function (text, language) { return new RRule(parseText(text, language)) -}; - -var parseText = function(text, language) { + } - var ttr = new Parser((language || ENGLISH).tokens); + var parseText = function (text, language) { + var options = {} + var ttr = new Parser((language || ENGLISH).tokens) - if(!ttr.start(text)) { - return null; - } - - var options = {}; - - S(); - return options; + if (!ttr.start(text)) return null - function S() { - ttr.expect('every'); + S() + return options - // every [n] - var n; - if(n = ttr.accept('number')) - options.interval = parseInt(n[0]); + function S () { + // every [n] + var n - if(ttr.isDone()) - throw new Error('Unexpected end'); + ttr.expect('every') + if ((n = ttr.accept('number'))) options.interval = parseInt(n[0], 10) + if (ttr.isDone()) throw new Error('Unexpected end') - switch(ttr.symbol) { + switch (ttr.symbol) { case 'day(s)': - options.freq = RRule.DAILY; - if (ttr.nextSymbol()) { - AT(); - F(); - } - break; - - // FIXME Note: every 2 weekdays != every two weeks on weekdays. - // DAILY on weekdays is not a valid rule + options.freq = RRule.DAILY + if (ttr.nextSymbol()) { + AT() + F() + } + break + + // FIXME Note: every 2 weekdays != every two weeks on weekdays. + // DAILY on weekdays is not a valid rule case 'weekday(s)': - options.freq = RRule.WEEKLY; - options.byweekday = [ - RRule.MO, - RRule.TU, - RRule.WE, - RRule.TH, - RRule.FR - ]; - ttr.nextSymbol(); - F(); - break; + options.freq = RRule.WEEKLY + options.byweekday = [ + RRule.MO, + RRule.TU, + RRule.WE, + RRule.TH, + RRule.FR + ] + ttr.nextSymbol() + F() + break case 'week(s)': - options.freq = RRule.WEEKLY; - if (ttr.nextSymbol()) { - ON(); - F(); - } - break; + options.freq = RRule.WEEKLY + if (ttr.nextSymbol()) { + ON() + F() + } + break case 'hour(s)': - options.freq = RRule.HOURLY; - if (ttr.nextSymbol()) { - ON(); - F(); - } - break; + options.freq = RRule.HOURLY + if (ttr.nextSymbol()) { + ON() + F() + } + break case 'month(s)': - options.freq = RRule.MONTHLY; - if (ttr.nextSymbol()) { - ON(); - F(); - } - break; + options.freq = RRule.MONTHLY + if (ttr.nextSymbol()) { + ON() + F() + } + break case 'year(s)': - options.freq = RRule.YEARLY; - if (ttr.nextSymbol()) { - ON(); - F(); - } - break; + options.freq = RRule.YEARLY + if (ttr.nextSymbol()) { + ON() + F() + } + break case 'monday': case 'tuesday': @@ -663,29 +590,26 @@ var parseText = function(text, language) { case 'friday': case 'saturday': case 'sunday': - options.freq = RRule.WEEKLY; - options.byweekday = [RRule[ttr.symbol.substr(0, 2).toUpperCase()]]; + options.freq = RRule.WEEKLY + options.byweekday = [RRule[ttr.symbol.substr(0, 2).toUpperCase()]] - if(!ttr.nextSymbol()) - return; + if (!ttr.nextSymbol()) return - // TODO check for duplicates - while (ttr.accept('comma')) { - if(ttr.isDone()) - throw new Error('Unexpected end'); + // TODO check for duplicates + while (ttr.accept('comma')) { + if (ttr.isDone()) throw new Error('Unexpected end') - var wkd; - if(!(wkd = decodeWKD())) { - throw new Error('Unexpected symbol ' + ttr.symbol - + ', expected weekday'); - } - - options.byweekday.push(RRule[wkd]); - ttr.nextSymbol(); + var wkd + if (!(wkd = decodeWKD())) { + throw new Error('Unexpected symbol ' + ttr.symbol + ', expected weekday') } - MDAYs(); - F(); - break; + + options.byweekday.push(RRule[wkd]) + ttr.nextSymbol() + } + MDAYs() + F() + break case 'january': case 'february': @@ -699,169 +623,143 @@ var parseText = function(text, language) { case 'october': case 'november': case 'december': - options.freq = RRule.YEARLY; - options.bymonth = [decodeM()]; - - if(!ttr.nextSymbol()) - return; + options.freq = RRule.YEARLY + options.bymonth = [decodeM()] - // TODO check for duplicates - while (ttr.accept('comma')) { - if(ttr.isDone()) - throw new Error('Unexpected end'); + if (!ttr.nextSymbol()) return - var m; - if(!(m = decodeM())) { - throw new Error('Unexpected symbol ' + ttr.symbol - + ', expected month'); - } + // TODO check for duplicates + while (ttr.accept('comma')) { + if (ttr.isDone()) throw new Error('Unexpected end') - options.bymonth.push(m); - ttr.nextSymbol(); + var m + if (!(m = decodeM())) { + throw new Error('Unexpected symbol ' + ttr.symbol + ', expected month') } - ON(); - F(); - break; + options.bymonth.push(m) + ttr.nextSymbol() + } + + ON() + F() + break default: - throw new Error('Unknown symbol'); + throw new Error('Unknown symbol') - } + } } - function ON() { - - var on = ttr.accept('on'); - var the = ttr.accept('the'); - if(!(on || the)) { - return; - } - - do { - - var nth, wkd, m; - - // nth | - if(nth = decodeNTH()) { - //ttr.nextSymbol(); - - if (wkd = decodeWKD()) { - ttr.nextSymbol(); - if (!options.byweekday) { - options.byweekday = []; - } - options.byweekday.push(RRule[wkd].nth(nth)); - } else { - if(!options.bymonthday) { - options.bymonthday = []; - } - options.bymonthday.push(nth); - ttr.accept('day(s)'); - } - - // - } else if(wkd = decodeWKD()) { - ttr.nextSymbol(); - if(!options.byweekday) - options.byweekday = []; - options.byweekday.push(RRule[wkd]); - } else if(ttr.symbol == 'weekday(s)') { - ttr.nextSymbol(); - if(!options.byweekday) - options.byweekday = []; - options.byweekday.push(RRule.MO); - options.byweekday.push(RRule.TU); - options.byweekday.push(RRule.WE); - options.byweekday.push(RRule.TH); - options.byweekday.push(RRule.FR); - } else if(ttr.symbol == 'week(s)') { - ttr.nextSymbol(); - var n; - if(!(n = ttr.accept('number'))) { - throw new Error('Unexpected symbol ' + ttr.symbol - + ', expected week number'); - } - options.byweekno = [n[0]]; - while(ttr.accept('comma')) { - if(!(n = ttr.accept('number'))) { - throw new Error('Unexpected symbol ' + ttr.symbol - + '; expected monthday'); - } - options.byweekno.push(n[0]); - } - - } else if(m = decodeM()) { - ttr.nextSymbol(); - if(!options.bymonth) - options.bymonth = []; - options.bymonth.push(m); - } else { - return; + function ON () { + var on = ttr.accept('on') + var the = ttr.accept('the') + if (!(on || the)) return + + do { + var nth, wkd, m + + // nth | + if ((nth = decodeNTH())) { + // ttr.nextSymbol() + + if ((wkd = decodeWKD())) { + ttr.nextSymbol() + if (!options.byweekday) options.byweekday = [] + options.byweekday.push(RRule[wkd].nth(nth)) + } else { + if (!options.bymonthday) options.bymonthday = [] + options.bymonthday.push(nth) + ttr.accept('day(s)') + } + // + } else if ((wkd = decodeWKD())) { + ttr.nextSymbol() + if (!options.byweekday) options.byweekday = [] + options.byweekday.push(RRule[wkd]) + } else if (ttr.symbol === 'weekday(s)') { + ttr.nextSymbol() + if (!options.byweekday) options.byweekday = [] + options.byweekday.push(RRule.MO) + options.byweekday.push(RRule.TU) + options.byweekday.push(RRule.WE) + options.byweekday.push(RRule.TH) + options.byweekday.push(RRule.FR) + } else if (ttr.symbol === 'week(s)') { + ttr.nextSymbol() + var n + if (!(n = ttr.accept('number'))) { + throw new Error('Unexpected symbol ' + ttr.symbol + ', expected week number') + } + options.byweekno = [n[0]] + while (ttr.accept('comma')) { + if (!(n = ttr.accept('number'))) { + throw new Error('Unexpected symbol ' + ttr.symbol + '; expected monthday') } - - } while (ttr.accept('comma') || ttr.accept('the') || ttr.accept('on')); + options.byweekno.push(n[0]) + } + } else if ((m = decodeM())) { + ttr.nextSymbol() + if (!options.bymonth) options.bymonth = [] + options.bymonth.push(m) + } else { + return + } + } while (ttr.accept('comma') || ttr.accept('the') || ttr.accept('on')) } - function AT() { + function AT () { + var at = ttr.accept('at') + if (!at) return - var at = ttr.accept('at'); - if(!at) { - return; + do { + var n + if (!(n = ttr.accept('number'))) { + throw new Error('Unexpected symbol ' + ttr.symbol + ', expected hour') } - - do { - var n; - if(!(n = ttr.accept('number'))) { - throw new Error('Unexpected symbol ' + ttr.symbol - + ', expected hour'); - } - options.byhour = [n[0]]; - while(ttr.accept('comma')) { - if(!(n = ttr.accept('number'))) { - throw new Error('Unexpected symbol ' + ttr.symbol - + '; expected hour'); - } - options.byhour.push(n[0]); - } - - } while (ttr.accept('comma') || ttr.accept('at')); + options.byhour = [n[0]] + while (ttr.accept('comma')) { + if (!(n = ttr.accept('number'))) { + throw new Error('Unexpected symbol ' + ttr.symbol + '; expected hour') + } + options.byhour.push(n[0]) + } + } while (ttr.accept('comma') || ttr.accept('at')) } - - function decodeM() { - switch(ttr.symbol) { + function decodeM () { + switch (ttr.symbol) { case 'january': - return 1; + return 1 case 'february': - return 2; + return 2 case 'march': - return 3; + return 3 case 'april': - return 4; + return 4 case 'may': - return 5; + return 5 case 'june': - return 6; + return 6 case 'july': - return 7; + return 7 case 'august': - return 8; + return 8 case 'september': - return 9; + return 9 case 'october': - return 10; + return 10 case 'november': - return 11; + return 11 case 'december': - return 12; + return 12 default: - return false; - } + return false + } } - function decodeWKD() { - switch(ttr.symbol) { + function decodeWKD () { + switch (ttr.symbol) { case 'monday': case 'tuesday': case 'wednesday': @@ -869,259 +767,233 @@ var parseText = function(text, language) { case 'friday': case 'saturday': case 'sunday': - return ttr.symbol.substr(0, 2).toUpperCase(); - break; - + return ttr.symbol.substr(0, 2).toUpperCase() default: - return false; - } + return false + } } - function decodeNTH() { - - switch(ttr.symbol) { + function decodeNTH () { + switch (ttr.symbol) { case 'last': - ttr.nextSymbol(); - return -1; + ttr.nextSymbol() + return -1 case 'first': - ttr.nextSymbol(); - return 1; + ttr.nextSymbol() + return 1 case 'second': - ttr.nextSymbol(); - return ttr.accept('last') ? -2 : 2; + ttr.nextSymbol() + return ttr.accept('last') ? -2 : 2 case 'third': - ttr.nextSymbol(); - return ttr.accept('last') ? -3 : 3; + ttr.nextSymbol() + return ttr.accept('last') ? -3 : 3 case 'nth': - var v = parseInt(ttr.value[1]); - if(v < -366 || v > 366) - throw new Error('Nth out of range: ' + v); + var v = parseInt(ttr.value[1], 10) + if (v < -366 || v > 366) throw new Error('Nth out of range: ' + v) - ttr.nextSymbol(); - return ttr.accept('last') ? -v : v; + ttr.nextSymbol() + return ttr.accept('last') ? -v : v default: - return false; - } + return false + } } - function MDAYs() { - - ttr.accept('on'); - ttr.accept('the'); - - var nth; - if(!(nth = decodeNTH())) { - return; - } + function MDAYs () { + ttr.accept('on') + ttr.accept('the') - options.bymonthday = [nth]; - ttr.nextSymbol(); + var nth + if (!(nth = decodeNTH())) return - while(ttr.accept('comma')) { + options.bymonthday = [nth] + ttr.nextSymbol() - if (!(nth = decodeNTH())) { - throw new Error('Unexpected symbol ' + ttr.symbol - + '; expected monthday'); - } + while (ttr.accept('comma')) { + if (!(nth = decodeNTH())) { + throw new Error('Unexpected symbol ' + ttr.symbol + '; expected monthday') + } - options.bymonthday.push(nth); + options.bymonthday.push(nth) + ttr.nextSymbol() + } + } - ttr.nextSymbol(); - } + function F () { + if (ttr.symbol === 'until') { + var date = Date.parse(ttr.text) + + if (!date) throw new Error('Cannot parse until date:' + ttr.text) + options.until = new Date(date) + } else if (ttr.accept('for')) { + options.count = ttr.value[0] + ttr.expect('number') + // ttr.expect('times') + } + } + } + + // ============================================================================= + // Parser + // ============================================================================= + + var Parser = function (rules) { + this.rules = rules + } + + Parser.prototype.start = function (text) { + this.text = text + this.done = false + return this.nextSymbol() + } + + Parser.prototype.isDone = function () { + return this.done && this.symbol == null + } + + Parser.prototype.nextSymbol = function () { + var best, bestSymbol + var p = this + + this.symbol = null + this.value = null + do { + if (this.done) return false + + var match, rule + best = null + for (var name in this.rules) { + rule = this.rules[name] + if ((match = rule.exec(p.text))) { + if (best == null || match[0].length > best[0].length) { + best = match + bestSymbol = name + } + } + } + + if (best != null) { + this.text = this.text.substr(best[0].length) + + if (this.text === '') this.done = true + } + + if (best == null) { + this.done = true + this.symbol = null + this.value = null + return + } + } while (bestSymbol === 'SKIP') + + this.symbol = bestSymbol + this.value = best + return true + } + + Parser.prototype.accept = function (name) { + if (this.symbol === name) { + if (this.value) { + var v = this.value + this.nextSymbol() + return v + } + + this.nextSymbol() + return true } - function F() { + return false + } - if(ttr.symbol == 'until') { + Parser.prototype.expect = function (name) { + if (this.accept(name)) return true - var date = Date.parse(ttr.text); + throw new Error('expected ' + name + ' but found ' + this.symbol) + } - if (!date) { - throw new Error('Cannot parse until date:' + ttr.text); - } - options.until = new Date(date); - } else if(ttr.accept('for')){ + // ============================================================================= + // i18n + // ============================================================================= - options.count = ttr.value[0]; - ttr.expect('number'); - /* ttr.expect('times') */ - } - } -}; - - -//============================================================================= -// Parser -//============================================================================= - -var Parser = function(rules) { - this.rules = rules; -}; - -Parser.prototype.start = function(text) { - this.text = text; - this.done = false; - return this.nextSymbol(); -}; - -Parser.prototype.isDone = function() { - return this.done && this.symbol == null; -}; - -Parser.prototype.nextSymbol = function() { - var p = this, best, bestSymbol; - - this.symbol = null; - this.value = null; - do { - if(this.done) { - return false; - } - - best = null; - - var match, rule; - for (var name in this.rules) { - rule = this.rules[name]; - if(match = rule.exec(p.text)) { - if(best == null || match[0].length > best[0].length) { - best = match; - bestSymbol = name; - } - } - - } - - if(best != null) { - this.text = this.text.substr(best[0].length); - - if(this.text == '') { - this.done = true; - } - } - - if(best == null) { - this.done = true; - this.symbol = null; - this.value = null; - return; - } - } while(bestSymbol == 'SKIP'); - - this.symbol = bestSymbol; - this.value = best; - return true; -}; - -Parser.prototype.accept = function(name) { - if(this.symbol == name) { - if(this.value) { - var v = this.value; - this.nextSymbol(); - return v; - } - - this.nextSymbol(); - return true; - } - - return false; -}; - -Parser.prototype.expect = function(name) { - if(this.accept(name)) { - return true; - } - - throw new Error('expected ' + name + ' but found ' + this.symbol); -}; - - -//============================================================================= -// i18n -//============================================================================= - -var ENGLISH = { + var ENGLISH = { dayNames: [ - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday" + 'Sunday', 'Monday', 'Tuesday', 'Wednesday', + 'Thursday', 'Friday', 'Saturday' ], monthNames: [ - "January", "February", "March", "April", "May", - "June", "July", "August", "September", "October", - "November", "December" + 'January', 'February', 'March', 'April', 'May', + 'June', 'July', 'August', 'September', 'October', + 'November', 'December' ], tokens: { - 'SKIP': /^[ \r\n\t]+|^\.$/, - 'number': /^[1-9][0-9]*/, - 'numberAsText': /^(one|two|three)/i, - 'every': /^every/i, - 'day(s)': /^days?/i, - 'weekday(s)': /^weekdays?/i, - 'week(s)': /^weeks?/i, - 'hour(s)': /^hours?/i, - 'month(s)': /^months?/i, - 'year(s)': /^years?/i, - 'on': /^(on|in)/i, - 'at': /^(at)/i, - 'the': /^the/i, - 'first': /^first/i, - 'second': /^second/i, - 'third': /^third/i, - 'nth': /^([1-9][0-9]*)(\.|th|nd|rd|st)/i, - 'last': /^last/i, - 'for': /^for/i, - 'time(s)': /^times?/i, - 'until': /^(un)?til/i, - 'monday': /^mo(n(day)?)?/i, - 'tuesday': /^tu(e(s(day)?)?)?/i, - 'wednesday': /^we(d(n(esday)?)?)?/i, - 'thursday': /^th(u(r(sday)?)?)?/i, - 'friday': /^fr(i(day)?)?/i, - 'saturday': /^sa(t(urday)?)?/i, - 'sunday': /^su(n(day)?)?/i, - 'january': /^jan(uary)?/i, - 'february': /^feb(ruary)?/i, - 'march': /^mar(ch)?/i, - 'april': /^apr(il)?/i, - 'may': /^may/i, - 'june': /^june?/i, - 'july': /^july?/i, - 'august': /^aug(ust)?/i, - 'september': /^sep(t(ember)?)?/i, - 'october': /^oct(ober)?/i, - 'november': /^nov(ember)?/i, - 'december': /^dec(ember)?/i, - 'comma': /^(,\s*|(and|or)\s*)+/i + 'SKIP': /^[ \r\n\t]+|^\.$/, + 'number': /^[1-9][0-9]*/, + 'numberAsText': /^(one|two|three)/i, + 'every': /^every/i, + 'day(s)': /^days?/i, + 'weekday(s)': /^weekdays?/i, + 'week(s)': /^weeks?/i, + 'hour(s)': /^hours?/i, + 'month(s)': /^months?/i, + 'year(s)': /^years?/i, + 'on': /^(on|in)/i, + 'at': /^(at)/i, + 'the': /^the/i, + 'first': /^first/i, + 'second': /^second/i, + 'third': /^third/i, + 'nth': /^([1-9][0-9]*)(\.|th|nd|rd|st)/i, + 'last': /^last/i, + 'for': /^for/i, + 'time(s)': /^times?/i, + 'until': /^(un)?til/i, + 'monday': /^mo(n(day)?)?/i, + 'tuesday': /^tu(e(s(day)?)?)?/i, + 'wednesday': /^we(d(n(esday)?)?)?/i, + 'thursday': /^th(u(r(sday)?)?)?/i, + 'friday': /^fr(i(day)?)?/i, + 'saturday': /^sa(t(urday)?)?/i, + 'sunday': /^su(n(day)?)?/i, + 'january': /^jan(uary)?/i, + 'february': /^feb(ruary)?/i, + 'march': /^mar(ch)?/i, + 'april': /^apr(il)?/i, + 'may': /^may/i, + 'june': /^june?/i, + 'july': /^july?/i, + 'august': /^aug(ust)?/i, + 'september': /^sep(t(ember)?)?/i, + 'october': /^oct(ober)?/i, + 'november': /^nov(ember)?/i, + 'december': /^dec(ember)?/i, + 'comma': /^(,\s*|(and|or)\s*)+/i } -}; + } + // ============================================================================= + // Export + // ============================================================================= -//============================================================================= -// Export -//============================================================================= - -var nlp = { + var nlp = { fromText: fromText, parseText: parseText, isFullyConvertible: ToText.isFullyConvertible, - toText: function(rrule, gettext, language) { - return new ToText(rrule, gettext, language).toString(); + toText: function (rrule, gettext, language) { + return new ToText(rrule, gettext, language).toString() } -}; + } -if (serverSide) { + if (serverSide) { module.exports = nlp -} else { - root['_RRuleNLP'] = nlp; -} + } else { + root['_RRuleNLP'] = nlp + } -if (typeof define === "function" && define.amd) { + if (typeof define === 'function' && define.amd) { /*global define:false */ - define("rrule", [], function () { - return nlp; - }); -} - -})(this); + define('rrule', [], function () { + return nlp + }) + } +})(this) diff --git a/lib/rrule.js b/lib/rrule.js index e50b5241..0070e2cf 100644 --- a/lib/rrule.js +++ b/lib/rrule.js @@ -13,37 +13,32 @@ * https://github.com/jkbrzt/rrule/blob/master/LICENCE * */ -(function(root){ +;(function (root) { + var serverSide = typeof module !== 'undefined' && module.exports -var serverSide = typeof module !== 'undefined' && module.exports; - - -var getnlp = function() { + var getnlp = function () { if (!getnlp._nlp) { - if (serverSide) { - // Lazy, runtime import to avoid circular refs. - getnlp._nlp = require('./nlp') - } else if (!(getnlp._nlp = root._RRuleNLP)) { - throw new Error( - 'You need to include rrule/nlp.js for fromText/toText to work.' - ) - } + if (serverSide) { + // Lazy, runtime import to avoid circular refs. + getnlp._nlp = require('./nlp') + } else if (!(getnlp._nlp = root._RRuleNLP)) { + throw new Error( + 'You need to include rrule/nlp.js for fromText/toText to work.') + } } - return getnlp._nlp; -}; - - -//============================================================================= -// Date utilities -//============================================================================= - -/** - * General date-related utilities. - * Also handles several incompatibilities between JavaScript and Python - * - */ -var dateutil = { - + return getnlp._nlp + } + + // ============================================================================= + // Date utilities + // ============================================================================= + + /** + * General date-related utilities. + * Also handles several incompatibilities between JavaScript and Python + * + */ + var dateutil = { MONTH_DAYS: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], /** @@ -72,762 +67,693 @@ var dateutil = { /** * py_date.timetuple()[7] */ - getYearDay: function(date) { - var dateNoTime = new Date( - date.getFullYear(), date.getMonth(), date.getDate()); - return Math.ceil( - (dateNoTime - new Date(date.getFullYear(), 0, 1)) / dateutil.ONE_DAY) + 1; + getYearDay: function (date) { + var dateNoTime = new Date( + date.getFullYear(), date.getMonth(), date.getDate()) + return Math.ceil( + (dateNoTime - new Date(date.getFullYear(), 0, 1)) / dateutil.ONE_DAY) + 1 }, - isLeapYear: function(year) { - if (year instanceof Date) { - year = year.getFullYear(); - } - return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0); + isLeapYear: function (year) { + if (year instanceof Date) year = year.getFullYear() + return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0) }, /** * @return {Number} the date's timezone offset in ms */ - tzOffset: function(date) { - return date.getTimezoneOffset() * 60 * 1000 + tzOffset: function (date) { + return date.getTimezoneOffset() * 60 * 1000 }, /** * @see: */ - daysBetween: function(date1, date2) { - // The number of milliseconds in one day - // Convert both dates to milliseconds - var date1_ms = date1.getTime() - dateutil.tzOffset(date1); - var date2_ms = date2.getTime() - dateutil.tzOffset(date2); - // Calculate the difference in milliseconds - var difference_ms = Math.abs(date1_ms - date2_ms); - // Convert back to days and return - return Math.round(difference_ms / dateutil.ONE_DAY); + daysBetween: function (date1, date2) { + // The number of milliseconds in one day + // Convert both dates to milliseconds + var date1_ms = date1.getTime() - dateutil.tzOffset(date1) + var date2_ms = date2.getTime() - dateutil.tzOffset(date2) + // Calculate the difference in milliseconds + var difference_ms = Math.abs(date1_ms - date2_ms) + // Convert back to days and return + return Math.round(difference_ms / dateutil.ONE_DAY) }, /** * @see: */ - toOrdinal: function(date) { - return dateutil.daysBetween(date, dateutil.ORDINAL_BASE); + toOrdinal: function (date) { + return dateutil.daysBetween(date, dateutil.ORDINAL_BASE) }, /** * @see - */ - fromOrdinal: function(ordinal) { - var millisecsFromBase = ordinal * dateutil.ONE_DAY; - return new Date(dateutil.ORDINAL_BASE.getTime() - - dateutil.tzOffset(dateutil.ORDINAL_BASE) - + millisecsFromBase - + dateutil.tzOffset(new Date(millisecsFromBase))); + fromOrdinal: function (ordinal) { + var millisecsFromBase = ordinal * dateutil.ONE_DAY + return new Date(dateutil.ORDINAL_BASE.getTime() - + dateutil.tzOffset(dateutil.ORDINAL_BASE) + + millisecsFromBase + + dateutil.tzOffset(new Date(millisecsFromBase))) }, /** * @see: */ - monthRange: function(year, month) { - var date = new Date(year, month, 1); - return [dateutil.getWeekday(date), dateutil.getMonthDays(date)]; + monthRange: function (year, month) { + var date = new Date(year, month, 1) + return [dateutil.getWeekday(date), dateutil.getMonthDays(date)] }, - getMonthDays: function(date) { - var month = date.getMonth(); - return month == 1 && dateutil.isLeapYear(date) - ? 29 - : dateutil.MONTH_DAYS[month]; + getMonthDays: function (date) { + var month = date.getMonth() + return month === 1 && dateutil.isLeapYear(date) + ? 29 : dateutil.MONTH_DAYS[month] }, /** * @return {Number} python-like weekday */ - getWeekday: function(date) { - return dateutil.PY_WEEKDAYS[date.getDay()]; + getWeekday: function (date) { + return dateutil.PY_WEEKDAYS[date.getDay()] }, /** * @see: */ - combine: function(date, time) { - time = time || date; - return new Date( - date.getFullYear(), date.getMonth(), date.getDate(), - time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds() - ); + combine: function (date, time) { + time = time || date + return new Date( + date.getFullYear(), date.getMonth(), date.getDate(), + time.getHours(), time.getMinutes(), time.getSeconds(), + time.getMilliseconds()) }, - clone: function(date) { - var dolly = new Date(date.getTime()); - return dolly; + clone: function (date) { + var dolly = new Date(date.getTime()) + return dolly }, - cloneDates: function(dates) { - var clones = []; - for (var i = 0; i < dates.length; i++) { - clones.push(dateutil.clone(dates[i])); - } - return clones; + cloneDates: function (dates) { + var clones = [] + for (var i = 0; i < dates.length; i++) { + clones.push(dateutil.clone(dates[i])) + } + return clones }, /** * Sorts an array of Date or dateutil.Time objects */ - sort: function(dates) { - dates.sort(function(a, b){ - return a.getTime() - b.getTime(); - }); + sort: function (dates) { + dates.sort(function (a, b) { + return a.getTime() - b.getTime() + }) }, - timeToUntilString: function(time) { - var date = new Date(time); - var comp, comps = [ - date.getUTCFullYear(), - date.getUTCMonth() + 1, - date.getUTCDate(), - 'T', - date.getUTCHours(), - date.getUTCMinutes(), - date.getUTCSeconds(), - 'Z' - ]; - for (var i = 0; i < comps.length; i++) { - comp = comps[i]; - if (!/[TZ]/.test(comp) && comp < 10) { - comps[i] = '0' + String(comp); - } - } - return comps.join(''); + timeToUntilString: function (time) { + var comp + var date = new Date(time) + var comps = [ + date.getUTCFullYear(), + date.getUTCMonth() + 1, + date.getUTCDate(), + 'T', + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + 'Z' + ] + + for (var i = 0; i < comps.length; i++) { + comp = comps[i] + if (!/[TZ]/.test(comp) && comp < 10) comps[i] = '0' + String(comp) + } + return comps.join('') }, - untilStringToDate: function(until) { - var re = /^(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2})Z)?$/; - var bits = re.exec(until); - if (!bits) { - throw new Error('Invalid UNTIL value: ' + until) - } - return new Date( - Date.UTC(bits[1], - bits[2] - 1, - bits[3], - bits[5] || 0, - bits[6] || 0, - bits[7] || 0 - )); + untilStringToDate: function (until) { + var re = /^(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2})Z)?$/ + var bits = re.exec(until) + if (!bits) throw new Error('Invalid UNTIL value: ' + until) + return new Date(Date.UTC( + bits[1], + bits[2] - 1, + bits[3], + bits[5] || 0, + bits[6] || 0, + bits[7] || 0)) } -}; + } -dateutil.Time = function(hour, minute, second, millisecond) { - this.hour = hour; - this.minute = minute; - this.second = second; - this.millisecond = millisecond || 0; -}; + dateutil.Time = function (hour, minute, second, millisecond) { + this.hour = hour + this.minute = minute + this.second = second + this.millisecond = millisecond || 0 + } -dateutil.Time.prototype = { - getHours: function() { - return this.hour; + dateutil.Time.prototype = { + getHours: function () { + return this.hour }, - getMinutes: function() { - return this.minute; + getMinutes: function () { + return this.minute }, - getSeconds: function() { - return this.second; + getSeconds: function () { + return this.second }, - getMilliseconds: function(){ - return this.millisecond + getMilliseconds: function () { + return this.millisecond }, - getTime: function() { - return ((this.hour * 60 * 60) - + (this.minute * 60) - + this.second) - * 1000 + this.millisecond; + getTime: function () { + return ((this.hour * 60 * 60) + (this.minute * 60) + this.second) * 1000 + + this.millisecond } -}; - + } -//============================================================================= -// Helper functions -//============================================================================= + // ============================================================================= + // Helper functions + // ============================================================================= - -/** - * Simplified version of python's range() - */ -var range = function(start, end) { + /** + * Simplified version of python's range() + */ + var range = function (start, end) { if (arguments.length === 1) { - end = start; - start = 0; - } - var rang = []; - for (var i = start; i < end; i++) { - rang.push(i); + end = start + start = 0 } - return rang; -}; -var repeat = function(value, times) { - var i = 0, array = []; + var rang = [] + for (var i = start; i < end; i++) rang.push(i) + return rang + } + + var repeat = function (value, times) { + var i = 0 + var array = [] + if (value instanceof Array) { - for (; i < times; i++) { - array[i] = [].concat(value); - } + for (; i < times; i++) array[i] = [].concat(value) } else { - for (; i < times; i++) { - array[i] = value; - } + for (; i < times; i++) array[i] = value } - return array; -}; - - -/** - * Python like split - */ -var split = function (str, sep, num) { - var splits = str.split(sep); + return array + } + + /** + * Python like split + */ + var split = function (str, sep, num) { + var splits = str.split(sep) return num - ? splits.slice(0, num).concat([splits.slice(num).join(sep)]) - : splits; -} - -/** - * closure/goog/math/math.js:modulo - * Copyright 2006 The Closure Library Authors. - * The % operator in JavaScript returns the remainder of a / b, but differs from - * some other languages in that the result will have the same sign as the - * dividend. For example, -1 % 8 == -1, whereas in some other languages - * (such as Python) the result would be 7. This function emulates the more - * correct modulo behavior, which is useful for certain applications such as - * calculating an offset index in a circular list. - * - * @param {number} a The dividend. - * @param {number} b The divisor. - * @return {number} a % b where the result is between 0 and b (either 0 <= x < b - * or b < x <= 0, depending on the sign of b). - */ -var pymod = function(a, b) { - var r = a % b; - // If r and b differ in sign, add b to wrap the result to the correct sign. - return (r * b < 0) ? r + b : r; -}; - - -/** - * @see: - */ -var divmod = function(a, b) { - return {div: Math.floor(a / b), mod: pymod(a, b)}; -}; - - -/** - * Python-like boolean - * @return {Boolean} value of an object/primitive, taking into account - * the fact that in Python an empty list's/tuple's - * boolean value is False, whereas in JS it's true - */ -var plb = function(obj) { - return (obj instanceof Array && obj.length == 0) - ? false - : Boolean(obj); -}; - - -/** - * Return true if a value is in an array - */ -var contains = function(arr, val) { - return arr.indexOf(val) != -1; -}; - + ? splits.slice(0, num).concat([splits.slice(num).join(sep)]) : splits + } + + /** + * closure/goog/math/math.js:modulo + * Copyright 2006 The Closure Library Authors. + * The % operator in JavaScript returns the remainder of a / b, but differs from + * some other languages in that the result will have the same sign as the + * dividend. For example, -1 % 8 == -1, whereas in some other languages + * (such as Python) the result would be 7. This function emulates the more + * correct modulo behavior, which is useful for certain applications such as + * calculating an offset index in a circular list. + * + * @param {number} a The dividend. + * @param {number} b The divisor. + * @return {number} a % b where the result is between 0 and b (either 0 <= x < b + * or b < x <= 0, depending on the sign of b). + */ + var pymod = function (a, b) { + var r = a % b + // If r and b differ in sign, add b to wrap the result to the correct sign. + return (r * b < 0) ? r + b : r + } + + /** + * @see: + */ + var divmod = function (a, b) { + return {div: Math.floor(a / b), mod: pymod(a, b)} + } + + /** + * Python-like boolean + * @return {Boolean} value of an object/primitive, taking into account + * the fact that in Python an empty list's/tuple's + * boolean value is False, whereas in JS it's true + */ + var plb = function (obj) { + return (obj instanceof Array && obj.length === 0) + ? false : Boolean(obj) + } + + /** + * Return true if a value is in an array + */ + var contains = function (arr, val) { + return arr.indexOf(val) !== -1 + } + + // ============================================================================= + // Date masks + // ============================================================================= + + // Every mask is 7 days longer to handle cross-year weekly periods. + + var M365MASK = [].concat( + repeat(1, 31), repeat(2, 28), repeat(3, 31), + repeat(4, 30), repeat(5, 31), repeat(6, 30), + repeat(7, 31), repeat(8, 31), repeat(9, 30), + repeat(10, 31), repeat(11, 30), repeat(12, 31), + repeat(1, 7)) -//============================================================================= -// Date masks -//============================================================================= + var M366MASK = [].concat( + repeat(1, 31), repeat(2, 29), repeat(3, 31), + repeat(4, 30), repeat(5, 31), repeat(6, 30), + repeat(7, 31), repeat(8, 31), repeat(9, 30), + repeat(10, 31), repeat(11, 30), repeat(12, 31), + repeat(1, 7)) -// Every mask is 7 days longer to handle cross-year weekly periods. + var M28 = range(1, 29) + var M29 = range(1, 30) + var M30 = range(1, 31) + var M31 = range(1, 32) -var M365MASK = [].concat( - repeat(1, 31), repeat(2, 28), repeat(3, 31), - repeat(4, 30), repeat(5, 31), repeat(6, 30), - repeat(7, 31), repeat(8, 31), repeat(9, 30), - repeat(10, 31), repeat(11, 30), repeat(12, 31), - repeat(1, 7) -); -var M366MASK = [].concat( - repeat(1, 31), repeat(2, 29), repeat(3, 31), - repeat(4, 30), repeat(5, 31), repeat(6, 30), - repeat(7, 31), repeat(8, 31), repeat(9, 30), - repeat(10, 31), repeat(11, 30), repeat(12, 31), - repeat(1, 7) -); - -var M28 = range(1, 29), - M29 = range(1, 30), - M30 = range(1, 31), - M31 = range(1, 32); -var MDAY366MASK = [].concat( + var MDAY366MASK = [].concat( M31, M29, M31, M30, M31, M30, M31, M31, M30, M31, M30, M31, - M31.slice(0, 7) -); -var MDAY365MASK = [].concat( + M31.slice(0, 7)) + + var MDAY365MASK = [].concat( M31, M28, M31, M30, M31, M30, M31, M31, M30, M31, M30, M31, - M31.slice(0, 7) -); - -M28 = range(-28, 0); -M29 = range(-29, 0); -M30 = range(-30, 0); -M31 = range(-31, 0); -var NMDAY366MASK = [].concat( + M31.slice(0, 7)) + + M28 = range(-28, 0) + M29 = range(-29, 0) + M30 = range(-30, 0) + M31 = range(-31, 0) + + var NMDAY366MASK = [].concat( M31, M29, M31, M30, M31, M30, M31, M31, M30, M31, M30, M31, - M31.slice(0, 7) -); -var NMDAY365MASK = [].concat( + M31.slice(0, 7)) + + var NMDAY365MASK = [].concat( M31, M28, M31, M30, M31, M30, M31, M31, M30, M31, M30, M31, - M31.slice(0, 7) -); + M31.slice(0, 7)) -var M366RANGE = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; -var M365RANGE = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + var M366RANGE = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366] + var M365RANGE = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365] -var WDAYMASK = (function() { - for (var wdaymask = [], i = 0; i < 55; i++) { - wdaymask = wdaymask.concat(range(7)); - } - return wdaymask; -}()); - - -//============================================================================= -// Weekday -//============================================================================= + var WDAYMASK = (function () { + for (var wdaymask = [], i = 0; i < 55; i++) wdaymask = wdaymask.concat(range(7)) + return wdaymask + }()) -var Weekday = function(weekday, n) { - if (n === 0) { - throw new Error('Can\'t create weekday with n == 0'); - } - this.weekday = weekday; - this.n = n; -}; + // ============================================================================= + // Weekday + // ============================================================================= -Weekday.prototype = { + var Weekday = function (weekday, n) { + if (n === 0) throw new Error("Can't create weekday with n == 0") + this.weekday = weekday + this.n = n + } + Weekday.prototype = { // __call__ - Cannot call the object directly, do it through // e.g. RRule.TH.nth(-1) instead, - nth: function(n) { - return this.n == n ? this : new Weekday(this.weekday, n); + nth: function (n) { + return this.n === n ? this : new Weekday(this.weekday, n) }, // __eq__ - equals: function(other) { - return this.weekday == other.weekday && this.n == other.n; + equals: function (other) { + return this.weekday === other.weekday && this.n === other.n }, // __repr__ - toString: function() { - var s = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'][this.weekday]; - if (this.n) { - s = (this.n > 0 ? '+' : '') + String(this.n) + s; - } - return s; + toString: function () { + var s = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'][this.weekday] + if (this.n) s = (this.n > 0 ? '+' : '') + String(this.n) + s + return s }, - getJsWeekday: function() { - return this.weekday == 6 ? 0 : this.weekday + 1; + getJsWeekday: function () { + return this.weekday === 6 ? 0 : this.weekday + 1 } -}; + } + // ============================================================================= + // RRule + // ============================================================================= -//============================================================================= -// RRule -//============================================================================= - -/** - * - * @param {Object?} options - see - * The only required option is `freq`, one of RRule.YEARLY, RRule.MONTHLY, ... - * @constructor - */ -var RRule = function(options, noCache) { - + /** + * + * @param {Object?} options - see + * The only required option is `freq`, one of RRule.YEARLY, RRule.MONTHLY, ... + * @constructor + */ + var RRule = function (options, noCache) { + options = options || {} // RFC string - this._string = null; - - options = options || {}; - + this._string = null this._cache = noCache ? null : { - all: false, - before: [], - after: [], - between: [] - }; + all: false, + before: [], + after: [], + between: [] + } // used by toString() - this.origOptions = {}; + this.origOptions = {} - var invalid = [], - keys = Object.keys(options), - defaultKeys = Object.keys(RRule.DEFAULT_OPTIONS); + var invalid = [] + var keys = Object.keys(options) + var defaultKeys = Object.keys(RRule.DEFAULT_OPTIONS) // Shallow copy for origOptions and check for invalid - keys.forEach(function(key) { - this.origOptions[key] = options[key]; - if (!contains(defaultKeys, key)) invalid.push(key); - }, this); + keys.forEach(function (key) { + this.origOptions[key] = options[key] + if (!contains(defaultKeys, key)) invalid.push(key) + }, this) - if (invalid.length) { - throw new Error('Invalid options: ' + invalid.join(', ')) - } + if (invalid.length) throw new Error('Invalid options: ' + invalid.join(', ')) if (!RRule.FREQUENCIES[options.freq] && options.byeaster === null) { - throw new Error('Invalid frequency: ' + String(options.freq)) + throw new Error('Invalid frequency: ' + String(options.freq)) } // Merge in default options - defaultKeys.forEach(function(key) { - if (!contains(keys, key)) options[key] = RRule.DEFAULT_OPTIONS[key]; - }); + defaultKeys.forEach(function (key) { + if (!contains(keys, key)) options[key] = RRule.DEFAULT_OPTIONS[key] + }) - var opts = this.options = options; + var opts = this.options = options - if (opts.byeaster !== null) { - opts.freq = RRule.YEARLY; - } + if (opts.byeaster !== null) opts.freq = RRule.YEARLY + if (!opts.dtstart) opts.dtstart = new Date() - if (!opts.dtstart) { - opts.dtstart = new Date(); - } - var millisecondModulo = opts.dtstart.getTime() % 1000; + var millisecondModulo = opts.dtstart.getTime() % 1000 if (opts.wkst === null) { - opts.wkst = RRule.MO.weekday; - } else if (typeof opts.wkst == 'number') { - // cool, just keep it like that + opts.wkst = RRule.MO.weekday + } else if (typeof opts.wkst === 'number') { + // cool, just keep it like that } else { - opts.wkst = opts.wkst.weekday; + opts.wkst = opts.wkst.weekday } if (opts.bysetpos !== null) { - if (typeof opts.bysetpos == 'number') { - opts.bysetpos = [opts.bysetpos]; - } - for (var i = 0; i < opts.bysetpos.length; i++) { - var v = opts.bysetpos[i]; - if (v == 0 || !(-366 <= v && v <= 366)) { - throw new Error( - 'bysetpos must be between 1 and 366,' + - ' or between -366 and -1' - ); - } + if (typeof opts.bysetpos === 'number') opts.bysetpos = [opts.bysetpos] + + for (var i = 0; i < opts.bysetpos.length; i++) { + var v = opts.bysetpos[i] + if (v === 0 || !(v >= -366 && v <= 366)) { + throw new Error('bysetpos must be between 1 and 366,' + + ' or between -366 and -1') } + } } - if (!(plb(opts.byweekno) || plb(opts.byyearday) - || plb(opts.bymonthday) || opts.byweekday !== null - || opts.byeaster !== null)) - { - switch (opts.freq) { - case RRule.YEARLY: - if (!opts.bymonth) { - opts.bymonth = opts.dtstart.getMonth() + 1; - } - opts.bymonthday = opts.dtstart.getDate(); - break; - case RRule.MONTHLY: - opts.bymonthday = opts.dtstart.getDate(); - break; - case RRule.WEEKLY: - opts.byweekday = dateutil.getWeekday( - opts.dtstart); - break; - } + if (!(plb(opts.byweekno) || plb(opts.byyearday) || plb(opts.bymonthday) || + opts.byweekday !== null || opts.byeaster !== null)) { + switch (opts.freq) { + case RRule.YEARLY: + if (!opts.bymonth) opts.bymonth = opts.dtstart.getMonth() + 1 + opts.bymonthday = opts.dtstart.getDate() + break + case RRule.MONTHLY: + opts.bymonthday = opts.dtstart.getDate() + break + case RRule.WEEKLY: + opts.byweekday = dateutil.getWeekday(opts.dtstart) + break + } } // bymonth - if (opts.bymonth !== null - && !(opts.bymonth instanceof Array)) { - opts.bymonth = [opts.bymonth]; + if (opts.bymonth !== null && !(opts.bymonth instanceof Array)) { + opts.bymonth = [opts.bymonth] } - // byyearday - if (opts.byyearday !== null - && !(opts.byyearday instanceof Array)) { - opts.byyearday = [opts.byyearday]; + if (opts.byyearday !== null && !(opts.byyearday instanceof Array)) { + opts.byyearday = [opts.byyearday] } // bymonthday if (opts.bymonthday === null) { - opts.bymonthday = []; - opts.bynmonthday = []; + opts.bymonthday = [] + opts.bynmonthday = [] } else if (opts.bymonthday instanceof Array) { - var bymonthday = [], bynmonthday = []; - - for (i = 0; i < opts.bymonthday.length; i++) { - var v = opts.bymonthday[i]; - if (v > 0) { - bymonthday.push(v); - } else if (v < 0) { - bynmonthday.push(v); - } - } - opts.bymonthday = bymonthday; - opts.bynmonthday = bynmonthday; + var bymonthday = [] + var bynmonthday = [] + + for (i = 0; i < opts.bymonthday.length; i++) { + v = opts.bymonthday[i] + if (v > 0) { + bymonthday.push(v) + } else if (v < 0) { + bynmonthday.push(v) + } + } + opts.bymonthday = bymonthday + opts.bynmonthday = bynmonthday } else { - if (opts.bymonthday < 0) { - opts.bynmonthday = [opts.bymonthday]; - opts.bymonthday = []; - } else { - opts.bynmonthday = []; - opts.bymonthday = [opts.bymonthday]; - } + if (opts.bymonthday < 0) { + opts.bynmonthday = [opts.bymonthday] + opts.bymonthday = [] + } else { + opts.bynmonthday = [] + opts.bymonthday = [opts.bymonthday] + } } // byweekno - if (opts.byweekno !== null - && !(opts.byweekno instanceof Array)) { - opts.byweekno = [opts.byweekno]; + if (opts.byweekno !== null && !(opts.byweekno instanceof Array)) { + opts.byweekno = [opts.byweekno] } // byweekday / bynweekday if (opts.byweekday === null) { - opts.bynweekday = null; - } else if (typeof opts.byweekday == 'number') { - opts.byweekday = [opts.byweekday]; - opts.bynweekday = null; - + opts.bynweekday = null + } else if (typeof opts.byweekday === 'number') { + opts.byweekday = [opts.byweekday] + opts.bynweekday = null } else if (opts.byweekday instanceof Weekday) { - - if (!opts.byweekday.n || opts.freq > RRule.MONTHLY) { - opts.byweekday = [opts.byweekday.weekday]; - opts.bynweekday = null; - } else { - opts.bynweekday = [ - [opts.byweekday.weekday, - opts.byweekday.n] - ]; - opts.byweekday = null; - } - + if (!opts.byweekday.n || opts.freq > RRule.MONTHLY) { + opts.byweekday = [opts.byweekday.weekday] + opts.bynweekday = null + } else { + opts.bynweekday = [ + [opts.byweekday.weekday, opts.byweekday.n] + ] + opts.byweekday = null + } } else { - var byweekday = [], bynweekday = []; + var byweekday = [] + var bynweekday = [] - for (i = 0; i < opts.byweekday.length; i++) { - var wday = opts.byweekday[i]; + for (i = 0; i < opts.byweekday.length; i++) { + var wday = opts.byweekday[i] - if (typeof wday == 'number') { - byweekday.push(wday); - } else if (!wday.n || opts.freq > RRule.MONTHLY) { - byweekday.push(wday.weekday); - } else { - bynweekday.push([wday.weekday, wday.n]); - } + if (typeof wday === 'number') { + byweekday.push(wday) + } else if (!wday.n || opts.freq > RRule.MONTHLY) { + byweekday.push(wday.weekday) + } else { + bynweekday.push([wday.weekday, wday.n]) } - opts.byweekday = plb(byweekday) ? byweekday : null; - opts.bynweekday = plb(bynweekday) ? bynweekday : null; + } + opts.byweekday = plb(byweekday) ? byweekday : null + opts.bynweekday = plb(bynweekday) ? bynweekday : null } // byhour if (opts.byhour === null) { - opts.byhour = (opts.freq < RRule.HOURLY) - ? [opts.dtstart.getHours()] - : null; - } else if (typeof opts.byhour == 'number') { - opts.byhour = [opts.byhour]; + opts.byhour = (opts.freq < RRule.HOURLY) ? [opts.dtstart.getHours()] : null + } else if (typeof opts.byhour === 'number') { + opts.byhour = [opts.byhour] } // byminute if (opts.byminute === null) { - opts.byminute = (opts.freq < RRule.MINUTELY) - ? [opts.dtstart.getMinutes()] - : null; - } else if (typeof opts.byminute == 'number') { - opts.byminute = [opts.byminute]; + opts.byminute = (opts.freq < RRule.MINUTELY) + ? [opts.dtstart.getMinutes()] : null + } else if (typeof opts.byminute === 'number') { + opts.byminute = [opts.byminute] } // bysecond if (opts.bysecond === null) { - opts.bysecond = (opts.freq < RRule.SECONDLY) - ? [opts.dtstart.getSeconds()] - : null; - } else if (typeof opts.bysecond == 'number') { - opts.bysecond = [opts.bysecond]; + opts.bysecond = (opts.freq < RRule.SECONDLY) + ? [opts.dtstart.getSeconds()] : null + } else if (typeof opts.bysecond === 'number') { + opts.bysecond = [opts.bysecond] } if (opts.freq >= RRule.HOURLY) { - this.timeset = null; + this.timeset = null } else { - this.timeset = []; - for (i = 0; i < opts.byhour.length; i++) { - var hour = opts.byhour[i]; - for (var j = 0; j < opts.byminute.length; j++) { - var minute = opts.byminute[j]; - for (var k = 0; k < opts.bysecond.length; k++) { - var second = opts.bysecond[k]; - // python: - // datetime.time(hour, minute, second, - // tzinfo=self._tzinfo)) - this.timeset.push(new dateutil.Time(hour, minute, second, millisecondModulo)); - } - } - } - dateutil.sort(this.timeset); + this.timeset = [] + for (i = 0; i < opts.byhour.length; i++) { + var hour = opts.byhour[i] + for (var j = 0; j < opts.byminute.length; j++) { + var minute = opts.byminute[j] + for (var k = 0; k < opts.bysecond.length; k++) { + var second = opts.bysecond[k] + // python: + // datetime.time(hour, minute, second, + // tzinfo=self._tzinfo)) + this.timeset.push(new dateutil.Time(hour, minute, second, millisecondModulo)) + } + } + } + dateutil.sort(this.timeset) } + } -}; -//}}} - -// RRule class 'constants' + // RRule class 'constants' -RRule.FREQUENCIES = [ + RRule.FREQUENCIES = [ 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY' -]; - -RRule.YEARLY = 0; -RRule.MONTHLY = 1; -RRule.WEEKLY = 2; -RRule.DAILY = 3; -RRule.HOURLY = 4; -RRule.MINUTELY = 5; -RRule.SECONDLY = 6; - -RRule.MO = new Weekday(0); -RRule.TU = new Weekday(1); -RRule.WE = new Weekday(2); -RRule.TH = new Weekday(3); -RRule.FR = new Weekday(4); -RRule.SA = new Weekday(5); -RRule.SU = new Weekday(6); - -RRule.DEFAULT_OPTIONS = { - freq: null, - dtstart: null, - interval: 1, - wkst: RRule.MO, - count: null, - until: null, - bysetpos: null, - bymonth: null, - bymonthday: null, + ] + + RRule.YEARLY = 0 + RRule.MONTHLY = 1 + RRule.WEEKLY = 2 + RRule.DAILY = 3 + RRule.HOURLY = 4 + RRule.MINUTELY = 5 + RRule.SECONDLY = 6 + + RRule.MO = new Weekday(0) + RRule.TU = new Weekday(1) + RRule.WE = new Weekday(2) + RRule.TH = new Weekday(3) + RRule.FR = new Weekday(4) + RRule.SA = new Weekday(5) + RRule.SU = new Weekday(6) + + RRule.DEFAULT_OPTIONS = { + freq: null, + dtstart: null, + interval: 1, + wkst: RRule.MO, + count: null, + until: null, + bysetpos: null, + bymonth: null, + bymonthday: null, bynmonthday: null, - byyearday: null, - byweekno: null, - byweekday: null, - bynweekday: null, - byhour: null, - byminute: null, - bysecond: null, - byeaster: null -}; - - - -RRule.parseText = function(text, language) { + byyearday: null, + byweekno: null, + byweekday: null, + bynweekday: null, + byhour: null, + byminute: null, + bysecond: null, + byeaster: null + } + + RRule.parseText = function (text, language) { return getnlp().parseText(text, language) -}; + } -RRule.fromText = function(text, language) { + RRule.fromText = function (text, language) { return getnlp().fromText(text, language) -}; + } -RRule.optionsToString = function(options) { - var key, keys, defaultKeys, value, strValues, pairs = []; - - keys = Object.keys(options); - defaultKeys = Object.keys(RRule.DEFAULT_OPTIONS); + RRule.optionsToString = function (options) { + var key, value, strValues + var pairs = [] + var keys = Object.keys(options) + var defaultKeys = Object.keys(RRule.DEFAULT_OPTIONS) for (var i = 0; i < keys.length; i++) { - - if (!contains(defaultKeys, keys[i])) continue; - - key = keys[i].toUpperCase(); - value = options[keys[i]]; - strValues = []; - - if (value === null || value instanceof Array && !value.length) { - continue; - } - - switch (key) { - case 'FREQ': - value = RRule.FREQUENCIES[options.freq]; - break; - case 'WKST': - value = value.toString(); - break; - case 'BYWEEKDAY': - /* - NOTE: BYWEEKDAY is a special case. - RRule() deconstructs the rule.options.byweekday array - into an array of Weekday arguments. - On the other hand, rule.origOptions is an array of Weekdays. - We need to handle both cases here. - It might be worth change RRule to keep the Weekdays. - - Also, BYWEEKDAY (used by RRule) vs. BYDAY (RFC) - - */ - key = 'BYDAY'; - if (!(value instanceof Array)) { - value = [value]; - } - for (var wday, j = 0; j < value.length; j++) { - wday = value[j]; - if (wday instanceof Weekday) { - // good - } else if (wday instanceof Array) { - wday = new Weekday(wday[0], wday[1]); - } else { - wday = new Weekday(wday); - } - strValues[j] = wday.toString(); - } - value = strValues; - break; - case'DTSTART': - case'UNTIL': - value = dateutil.timeToUntilString(value); - break; - default: - if (value instanceof Array) { - for (var j = 0; j < value.length; j++) { - strValues[j] = String(value[j]); - } - value = strValues; - } else { - value = String(value); - } - - } - pairs.push([key, value]); + if (!contains(defaultKeys, keys[i])) continue + + key = keys[i].toUpperCase() + value = options[keys[i]] + strValues = [] + + if (value === null || value instanceof Array && !value.length) continue + + switch (key) { + case 'FREQ': + value = RRule.FREQUENCIES[options.freq] + break + case 'WKST': + value = value.toString() + break + case 'BYWEEKDAY': + /* + NOTE: BYWEEKDAY is a special case. + RRule() deconstructs the rule.options.byweekday array + into an array of Weekday arguments. + On the other hand, rule.origOptions is an array of Weekdays. + We need to handle both cases here. + It might be worth change RRule to keep the Weekdays. + + Also, BYWEEKDAY (used by RRule) vs. BYDAY (RFC) + + */ + key = 'BYDAY' + if (!(value instanceof Array)) value = [value] + + for (var wday, j = 0; j < value.length; j++) { + wday = value[j] + if (wday instanceof Weekday) { + // good + } else if (wday instanceof Array) { + wday = new Weekday(wday[0], wday[1]) + } else { + wday = new Weekday(wday) + } + strValues[j] = wday.toString() + } + value = strValues + break + case 'DTSTART': + case 'UNTIL': + value = dateutil.timeToUntilString(value) + break + default: + if (value instanceof Array) { + for (j = 0; j < value.length; j++) strValues[j] = String(value[j]) + value = strValues + } else { + value = String(value) + } + + } + pairs.push([key, value]) } - var strings = []; - for (var i = 0; i < pairs.length; i++) { - var attr = pairs[i]; - strings.push(attr[0] + '=' + attr[1].toString()); + var strings = [] + for (i = 0; i < pairs.length; i++) { + var attr = pairs[i] + strings.push(attr[0] + '=' + attr[1].toString()) } - return strings.join(';'); - -}; - -RRule.prototype = { + return strings.join(';') + } + RRule.prototype = { constructor: RRule, /** @@ -836,17 +762,17 @@ RRule.prototype = { * to stop the iteration. * @return Array containing all recurrences. */ - all: function(iterator) { - if (iterator) { - return this._iter(new CallbackIterResult('all', {}, iterator)); - } else { - var result = this._cacheGet('all'); - if (result === false) { - result = this._iter(new IterResult('all', {})); - this._cacheAdd('all', result); - } - return result; + all: function (iterator) { + if (iterator) { + return this._iter(new CallbackIterResult('all', {}, iterator)) + } else { + var result = this._cacheGet('all') + if (result === false) { + result = this._iter(new IterResult('all', {})) + this._cacheAdd('all', result) } + return result + } }, /** @@ -856,24 +782,22 @@ RRule.prototype = { * list, if they are found in the recurrence set. * @return Array */ - between: function(after, before, inc, iterator) { - var args = { - before: before, - after: after, - inc: inc - } - - if (iterator) { - return this._iter( - new CallbackIterResult('between', args, iterator)); - } else { - var result = this._cacheGet('between', args); - if (result === false) { - result = this._iter(new IterResult('between', args)); - this._cacheAdd('between', result, args); - } - return result; - } + between: function (after, before, inc, iterator) { + var args = { + before: before, + after: after, + inc: inc + } + + if (iterator) { + return this._iter(new CallbackIterResult('between', args, iterator)) + } + var result = this._cacheGet('between', args) + if (result === false) { + result = this._iter(new IterResult('between', args)) + this._cacheAdd('between', result, args) + } + return result }, /** @@ -882,17 +806,14 @@ RRule.prototype = { * With inc == True, if dt itself is an occurrence, it will be returned. * @return Date or null */ - before: function(dt, inc) { - var args = { - dt: dt, - inc: inc - }, - result = this._cacheGet('before', args); - if (result === false) { - result = this._iter(new IterResult('before', args)); - this._cacheAdd('before', result, args); - } - return result; + before: function (dt, inc) { + var args = {dt: dt, inc: inc} + var result = this._cacheGet('before', args) + if (result === false) { + result = this._iter(new IterResult('before', args)) + this._cacheAdd('before', result, args) + } + return result }, /** @@ -901,25 +822,22 @@ RRule.prototype = { * With inc == True, if dt itself is an occurrence, it will be returned. * @return Date or null */ - after: function(dt, inc) { - var args = { - dt: dt, - inc: inc - }, - result = this._cacheGet('after', args); - if (result === false) { - result = this._iter(new IterResult('after', args)); - this._cacheAdd('after', result, args); - } - return result; + after: function (dt, inc) { + var args = {dt: dt, inc: inc} + var result = this._cacheGet('after', args) + if (result === false) { + result = this._iter(new IterResult('after', args)) + this._cacheAdd('after', result, args) + } + return result }, /** * Returns the number of recurrences in this set. It will have go trough * the whole recurrence, if this hasn't been done before. */ - count: function() { - return this.all().length; + count: function () { + return this.all().length }, /** @@ -927,20 +845,20 @@ RRule.prototype = { * @see * @return String */ - toString: function() { - return RRule.optionsToString(this.origOptions); + toString: function () { + return RRule.optionsToString(this.origOptions) }, - /** - * Will convert all rules described in nlp:ToText - * to text. - */ - toText: function(gettext, language) { - return getnlp().toText(this, gettext, language); - }, + /** + * Will convert all rules described in nlp:ToText + * to text. + */ + toText: function (gettext, language) { + return getnlp().toText(this, gettext, language) + }, - isFullyConvertibleToText: function() { - return getnlp().isFullyConvertible(this) + isFullyConvertibleToText: function () { + return getnlp().isFullyConvertible(this) }, /** @@ -948,23 +866,20 @@ RRule.prototype = { * @param {Array,Date} value - an array of dates, one date, or null * @param {Object?} args - _iter arguments */ - _cacheAdd: function(what, value, args) { - - if (!this._cache) return; - - if (value) { - value = (value instanceof Date) - ? dateutil.clone(value) - : dateutil.cloneDates(value); - } - - if (what == 'all') { - this._cache.all = value; - } else { - args._value = value; - this._cache[what].push(args); - } - + _cacheAdd: function (what, value, args) { + if (!this._cache) return + + if (value) { + value = (value instanceof Date) + ? dateutil.clone(value) : dateutil.cloneDates(value) + } + + if (what === 'all') { + this._cache.all = value + } else { + args._value = value + this._cache[what].push(args) + } }, /** @@ -974,839 +889,764 @@ RRule.prototype = { * [] - cached, but zero occurrences (all/between) * [Date1, DateN] - cached (all/between) */ - _cacheGet: function(what, args) { - - if (!this._cache) { - return false; - } - - var cached = false; - - if (what == 'all') { - cached = this._cache.all; - } else { - // Let's see whether we've already called the - // 'what' method with the same 'args' - loopItems: - for (var item, i = 0; i < this._cache[what].length; i++) { - item = this._cache[what][i]; - for (var k in args) { - if (args.hasOwnProperty(k) - && String(args[k]) != String(item[k])) { - continue loopItems; - } - } - cached = item._value; - break; - } - } - - if (!cached && this._cache.all) { - // Not in the cache, but we already know all the occurrences, - // so we can find the correct dates from the cached ones. - var iterResult = new IterResult(what, args); - for (var i = 0; i < this._cache.all.length; i++) { - if (!iterResult.accept(this._cache.all[i])) { - break; - } - } - cached = iterResult.getValue(); - this._cacheAdd(what, cached, args); - } - - return cached instanceof Array - ? dateutil.cloneDates(cached) - : (cached instanceof Date - ? dateutil.clone(cached) - : cached); + _cacheGet: function (what, args) { + if (!this._cache) return false + + var cached = false + var argsKeys = args ? Object.keys(args) : [] + var findCacheDiff = function (item) { + for (var key, i = 0; i < argsKeys.length; i++) { + key = argsKeys[i] + if (String(args[key]) !== String(item[key])) return true + } + return false + } + + if (what === 'all') { + cached = this._cache.all + } else { + // Let's see whether we've already called the + // 'what' method with the same 'args' + for (var item, i = 0; i < this._cache[what].length; i++) { + item = this._cache[what][i] + if (argsKeys.length && findCacheDiff(item)) continue + cached = item._value + break + } + } + + if (!cached && this._cache.all) { + // Not in the cache, but we already know all the occurrences, + // so we can find the correct dates from the cached ones. + var iterResult = new IterResult(what, args) + for (i = 0; i < this._cache.all.length; i++) { + if (!iterResult.accept(this._cache.all[i])) break + } + cached = iterResult.getValue() + this._cacheAdd(what, cached, args) + } + + return cached instanceof Array + ? dateutil.cloneDates(cached) + : (cached instanceof Date ? dateutil.clone(cached) : cached) }, /** * @return a RRule instance with the same freq and options * as this one (cache is not cloned) */ - clone: function() { - return new RRule(this.origOptions); + clone: function () { + return new RRule(this.origOptions) }, - _iter: function(iterResult) { - - /* Since JavaScript doesn't have the python's yield operator (<1.7), - we use the IterResult object that tells us when to stop iterating. - - */ - - var dtstart = this.options.dtstart, - dtstartMillisecondModulo= this.options.dtstart%1000; - - var year = dtstart.getFullYear(), - month = dtstart.getMonth() + 1, - day = dtstart.getDate(), - hour = dtstart.getHours(), - minute = dtstart.getMinutes(), - second = dtstart.getSeconds(), - weekday = dateutil.getWeekday(dtstart), - yearday = dateutil.getYearDay(dtstart); - - // Some local variables to speed things up a bit - var freq = this.options.freq, - interval = this.options.interval, - wkst = this.options.wkst, - until = this.options.until, - bymonth = this.options.bymonth, - byweekno = this.options.byweekno, - byyearday = this.options.byyearday, - byweekday = this.options.byweekday, - byeaster = this.options.byeaster, - bymonthday = this.options.bymonthday, - bynmonthday = this.options.bynmonthday, - bysetpos = this.options.bysetpos, - byhour = this.options.byhour, - byminute = this.options.byminute, - bysecond = this.options.bysecond; - - var ii = new Iterinfo(this); - ii.rebuild(year, month); - - var getdayset = {}; - getdayset[RRule.YEARLY] = ii.ydayset; - getdayset[RRule.MONTHLY] = ii.mdayset; - getdayset[RRule.WEEKLY] = ii.wdayset; - getdayset[RRule.DAILY] = ii.ddayset; - getdayset[RRule.HOURLY] = ii.ddayset; - getdayset[RRule.MINUTELY] = ii.ddayset; - getdayset[RRule.SECONDLY] = ii.ddayset; - - getdayset = getdayset[freq]; - - var timeset; - if (freq < RRule.HOURLY) { - timeset = this.timeset; + _iter: function (iterResult) { + /* Since JavaScript doesn't have the python's yield operator (<1.7), + we use the IterResult object that tells us when to stop iterating. + + */ + + var dtstart = this.options.dtstart + var dtstartMillisecondModulo = this.options.dtstart % 1000 + + var year = dtstart.getFullYear() + var month = dtstart.getMonth() + 1 + var day = dtstart.getDate() + var hour = dtstart.getHours() + var minute = dtstart.getMinutes() + var second = dtstart.getSeconds() + var weekday = dateutil.getWeekday(dtstart) + + // Some local variables to speed things up a bit + var freq = this.options.freq + var interval = this.options.interval + var wkst = this.options.wkst + var until = this.options.until + var bymonth = this.options.bymonth + var byweekno = this.options.byweekno + var byyearday = this.options.byyearday + var byweekday = this.options.byweekday + var byeaster = this.options.byeaster + var bymonthday = this.options.bymonthday + var bynmonthday = this.options.bynmonthday + var bysetpos = this.options.bysetpos + var byhour = this.options.byhour + var byminute = this.options.byminute + var bysecond = this.options.bysecond + + var ii = new Iterinfo(this) + ii.rebuild(year, month) + + var getdayset = {} + getdayset[RRule.YEARLY] = ii.ydayset + getdayset[RRule.MONTHLY] = ii.mdayset + getdayset[RRule.WEEKLY] = ii.wdayset + getdayset[RRule.DAILY] = ii.ddayset + getdayset[RRule.HOURLY] = ii.ddayset + getdayset[RRule.MINUTELY] = ii.ddayset + getdayset[RRule.SECONDLY] = ii.ddayset + + getdayset = getdayset[freq] + + var timeset + if (freq < RRule.HOURLY) { + timeset = this.timeset + } else { + var gettimeset = {} + gettimeset[RRule.HOURLY] = ii.htimeset + gettimeset[RRule.MINUTELY] = ii.mtimeset + gettimeset[RRule.SECONDLY] = ii.stimeset + gettimeset = gettimeset[freq] + if ((freq >= RRule.HOURLY && plb(byhour) && !contains(byhour, hour)) || + (freq >= RRule.MINUTELY && plb(byminute) && !contains(byminute, minute)) || + (freq >= RRule.SECONDLY && plb(bysecond) && !contains(bysecond, minute))) { + timeset = [] } else { - var gettimeset = {}; - gettimeset[RRule.HOURLY] = ii.htimeset; - gettimeset[RRule.MINUTELY] = ii.mtimeset; - gettimeset[RRule.SECONDLY] = ii.stimeset; - gettimeset = gettimeset[freq]; - if ((freq >= RRule.HOURLY && plb(byhour) && !contains(byhour, hour)) || - (freq >= RRule.MINUTELY && plb(byminute) && !contains(byminute, minute)) || - (freq >= RRule.SECONDLY && plb(bysecond) && !contains(bysecond, minute))) - { - timeset = []; + timeset = gettimeset.call(ii, hour, minute, second, dtstartMillisecondModulo) + } + } + + var total = 0 + var count = this.options.count + var i, j, k, dm, div, mod, tmp, pos, dayset, start, end, fixday, filtered + + while (true) { + // Get dayset with the right frequency + tmp = getdayset.call(ii, year, month, day) + dayset = tmp[0] + start = tmp[1] + end = tmp[2] + + // Do the "hard" work ;-) + filtered = false + for (j = start; j < end; j++) { + i = dayset[j] + + filtered = (plb(bymonth) && !contains(bymonth, ii.mmask[i])) || + (plb(byweekno) && !ii.wnomask[i]) || + (plb(byweekday) && !contains(byweekday, ii.wdaymask[i])) || + (plb(ii.nwdaymask) && !ii.nwdaymask[i]) || + (byeaster !== null && !contains(ii.eastermask, i)) || + ((plb(bymonthday) || plb(bynmonthday)) && + !contains(bymonthday, ii.mdaymask[i]) && + !contains(bynmonthday, ii.nmdaymask[i])) || + (plb(byyearday) && + ((i < ii.yearlen && + !contains(byyearday, i + 1) && + !contains(byyearday, -ii.yearlen + i)) || + (i >= ii.yearlen && + !contains(byyearday, i + 1 - ii.yearlen) && + !contains(byyearday, -ii.nextyearlen + i - ii.yearlen)))) + + if (filtered) dayset[i] = null + } + + // Output results + if (plb(bysetpos) && plb(timeset)) { + var daypos, timepos + var poslist = [] + + for (i, j = 0; j < bysetpos.length; j++) { + pos = bysetpos[j] + + if (pos < 0) { + daypos = Math.floor(pos / timeset.length) + timepos = pymod(pos, timeset.length) } else { - timeset = gettimeset.call(ii, hour, minute, second, dtstartMillisecondModulo); + daypos = Math.floor((pos - 1) / timeset.length) + timepos = pymod((pos - 1), timeset.length) } - } - var filtered, total = 0, count = this.options.count; - - var iterNo = 0; - - var i, j, k, dm, div, mod, tmp, pos, dayset, start, end, fixday; - - while (true) { - - // Get dayset with the right frequency - tmp = getdayset.call(ii, year, month, day); - dayset = tmp[0]; start = tmp[1]; end = tmp[2]; - - // Do the "hard" work ;-) - filtered = false; - for (j = start; j < end; j++) { - - i = dayset[j]; - - if ((plb(bymonth) && !contains(bymonth, ii.mmask[i])) || - (plb(byweekno) && !ii.wnomask[i]) || - (plb(byweekday) && !contains(byweekday, ii.wdaymask[i])) || - (plb(ii.nwdaymask) && !ii.nwdaymask[i]) || - (byeaster !== null && !contains(ii.eastermask, i)) || - ( - (plb(bymonthday) || plb(bynmonthday)) && - !contains(bymonthday, ii.mdaymask[i]) && - !contains(bynmonthday, ii.nmdaymask[i]) - ) - || - ( - plb(byyearday) - && - ( - ( - i < ii.yearlen && - !contains(byyearday, i + 1) && - !contains(byyearday, -ii.yearlen + i) - ) - || - ( - i >= ii.yearlen && - !contains(byyearday, i + 1 - ii.yearlen) && - !contains(byyearday, -ii.nextyearlen + i - ii.yearlen) - ) - ) - ) - ) - { - dayset[i] = null; - filtered = true; + try { + tmp = [] + for (k = start; k < end; k++) { + var val = dayset[k] + if (val === null) continue + tmp.push(val) + } + if (daypos < 0) { + // we're trying to emulate python's aList[-n] + i = tmp.slice(daypos)[0] + } else { + i = tmp[daypos] + } + + var time = timeset[timepos] + var date = dateutil.fromOrdinal(ii.yearordinal + i) + var res = dateutil.combine(date, time) + // XXX: can this ever be in the array? + // - compare the actual date instead? + if (!contains(poslist, res)) poslist.push(res) + } catch (e) {} + } + + dateutil.sort(poslist) + for (j = 0; j < poslist.length; j++) { + res = poslist[j] + if (until && res > until) { + this._len = total + return iterResult.getValue() + } else if (res >= dtstart) { + ++total + if (!iterResult.accept(res)) return iterResult.getValue() + if (count) { + --count + if (!count) { + this._len = total + return iterResult.getValue() } + } } - - // Output results - if (plb(bysetpos) && plb(timeset)) { - - var daypos, timepos, poslist = []; - - for (i, j = 0; j < bysetpos.length; j++) { - var pos = bysetpos[j]; - if (pos < 0) { - daypos = Math.floor(pos / timeset.length); - timepos = pymod(pos, timeset.length); - } else { - daypos = Math.floor((pos - 1) / timeset.length); - timepos = pymod((pos - 1), timeset.length); - } - - try { - tmp = []; - for (k = start; k < end; k++) { - var val = dayset[k]; - if (val === null) { - continue; - } - tmp.push(val); - } - if (daypos < 0) { - // we're trying to emulate python's aList[-n] - i = tmp.slice(daypos)[0]; - } else { - i = tmp[daypos]; - } - - var time = timeset[timepos]; - - var date = dateutil.fromOrdinal(ii.yearordinal + i); - var res = dateutil.combine(date, time); - // XXX: can this ever be in the array? - // - compare the actual date instead? - if (!contains(poslist, res)) { - poslist.push(res); - } - } catch (e) {} - } - - dateutil.sort(poslist); - - for (j = 0; j < poslist.length; j++) { - var res = poslist[j]; - if (until && res > until) { - this._len = total; - return iterResult.getValue(); - } else if (res >= dtstart) { - ++total; - if (!iterResult.accept(res)) { - return iterResult.getValue(); - } - if (count) { - --count; - if (!count) { - this._len = total; - return iterResult.getValue(); - } - } - } - } - - } else { - for (j = start; j < end; j++) { - i = dayset[j]; - if (i !== null) { - var date = dateutil.fromOrdinal(ii.yearordinal + i); - for (k = 0; k < timeset.length; k++) { - var time = timeset[k]; - var res = dateutil.combine(date, time); - if (until && res > until) { - this._len = total; - return iterResult.getValue(); - } else if (res >= dtstart) { - ++total; - if (!iterResult.accept(res)) { - return iterResult.getValue(); - } - if (count) { - --count; - if (!count) { - this._len = total; - return iterResult.getValue(); - } - } - } - } + } + } else { + for (j = start; j < end; j++) { + i = dayset[j] + if (i !== null) { + date = dateutil.fromOrdinal(ii.yearordinal + i) + for (k = 0; k < timeset.length; k++) { + time = timeset[k] + res = dateutil.combine(date, time) + if (until && res > until) { + this._len = total + return iterResult.getValue() + } else if (res >= dtstart) { + ++total + if (!iterResult.accept(res)) return iterResult.getValue() + if (count) { + --count + if (!count) { + this._len = total + return iterResult.getValue() } + } } + } } - - // Handle frequency and interval - fixday = false; - if (freq == RRule.YEARLY) { - year += interval; - if (year > dateutil.MAXYEAR) { - this._len = total; - return iterResult.getValue(); - } - ii.rebuild(year, month); - } else if (freq == RRule.MONTHLY) { - month += interval; - if (month > 12) { - div = Math.floor(month / 12); - mod = pymod(month, 12); - month = mod; - year += div; - if (month == 0) { - month = 12; - --year; - } - if (year > dateutil.MAXYEAR) { - this._len = total; - return iterResult.getValue(); - } - } - ii.rebuild(year, month); - } else if (freq == RRule.WEEKLY) { - if (wkst > weekday) { - day += -(weekday + 1 + (6 - wkst)) + interval * 7; - } else { - day += -(weekday - wkst) + interval * 7; - } - weekday = wkst; - fixday = true; - } else if (freq == RRule.DAILY) { - day += interval; - fixday = true; - } else if (freq == RRule.HOURLY) { - if (filtered) { - // Jump to one iteration before next day - hour += Math.floor((23 - hour) / interval) * interval; - } - while (true) { - hour += interval; - dm = divmod(hour, 24); - div = dm.div; - mod = dm.mod; - if (div) { - hour = mod; - day += div; - fixday = true; - } - if (!plb(byhour) || contains(byhour, hour)) { - break; - } - } - timeset = gettimeset.call(ii, hour, minute, second); - } else if (freq == RRule.MINUTELY) { - if (filtered) { - // Jump to one iteration before next day - minute += Math.floor( - (1439 - (hour * 60 + minute)) / interval) * interval; - } - while(true) { - minute += interval; - dm = divmod(minute, 60); - div = dm.div; - mod = dm.mod; - if (div) { - minute = mod; - hour += div; - dm = divmod(hour, 24); - div = dm.div; - mod = dm.mod; - if (div) { - hour = mod; - day += div; - fixday = true; - filtered = false; - } - } - if ((!plb(byhour) || contains(byhour, hour)) && - (!plb(byminute) || contains(byminute, minute))) { - break; - } - } - timeset = gettimeset.call(ii, hour, minute, second); - } else if (freq == RRule.SECONDLY) { - if (filtered) { - // Jump to one iteration before next day - second += Math.floor( - (86399 - (hour * 3600 + minute * 60 + second)) / interval) * interval; - } - while (true) { - second += interval; - dm = divmod(second, 60); - div = dm.div; - mod = dm.mod; - if (div) { - second = mod; - minute += div; - dm = divmod(minute, 60); - div = dm.div; - mod = dm.mod; - if (div) { - minute = mod; - hour += div; - dm = divmod(hour, 24); - div = dm.div; - mod = dm.mod; - if (div) { - hour = mod; - day += div; - fixday = true; - } - } - } - if ((!plb(byhour) || contains(byhour, hour)) && - (!plb(byminute) || contains(byminute, minute)) && - (!plb(bysecond) || contains(bysecond, second))) - { - break; - } + } + } + + // Handle frequency and interval + fixday = false + if (freq === RRule.YEARLY) { + year += interval + if (year > dateutil.MAXYEAR) { + this._len = total + return iterResult.getValue() + } + ii.rebuild(year, month) + } else if (freq === RRule.MONTHLY) { + month += interval + if (month > 12) { + div = Math.floor(month / 12) + mod = pymod(month, 12) + month = mod + year += div + if (month === 0) { + month = 12 + --year + } + if (year > dateutil.MAXYEAR) { + this._len = total + return iterResult.getValue() + } + } + ii.rebuild(year, month) + } else if (freq === RRule.WEEKLY) { + if (wkst > weekday) { + day += -(weekday + 1 + (6 - wkst)) + interval * 7 + } else { + day += -(weekday - wkst) + interval * 7 + } + weekday = wkst + fixday = true + } else if (freq === RRule.DAILY) { + day += interval + fixday = true + } else if (freq === RRule.HOURLY) { + if (filtered) { + // Jump to one iteration before next day + hour += Math.floor((23 - hour) / interval) * interval + } + while (true) { + hour += interval + dm = divmod(hour, 24) + div = dm.div + mod = dm.mod + if (div) { + hour = mod + day += div + fixday = true + } + if (!plb(byhour) || contains(byhour, hour)) break + } + timeset = gettimeset.call(ii, hour, minute, second) + } else if (freq === RRule.MINUTELY) { + if (filtered) { + // Jump to one iteration before next day + minute += Math.floor( + (1439 - (hour * 60 + minute)) / interval) * interval + } + + while (true) { + minute += interval + dm = divmod(minute, 60) + div = dm.div + mod = dm.mod + if (div) { + minute = mod + hour += div + dm = divmod(hour, 24) + div = dm.div + mod = dm.mod + if (div) { + hour = mod + day += div + fixday = true + filtered = false + } + } + if ((!plb(byhour) || contains(byhour, hour)) && + (!plb(byminute) || contains(byminute, minute))) { + break + } + } + timeset = gettimeset.call(ii, hour, minute, second) + } else if (freq === RRule.SECONDLY) { + if (filtered) { + // Jump to one iteration before next day + second += Math.floor( + (86399 - (hour * 3600 + minute * 60 + second)) / interval) * interval + } + while (true) { + second += interval + dm = divmod(second, 60) + div = dm.div + mod = dm.mod + if (div) { + second = mod + minute += div + dm = divmod(minute, 60) + div = dm.div + mod = dm.mod + if (div) { + minute = mod + hour += div + dm = divmod(hour, 24) + div = dm.div + mod = dm.mod + if (div) { + hour = mod + day += div + fixday = true } - timeset = gettimeset.call(ii, hour, minute, second); + } } - - if (fixday && day > 28) { - var daysinmonth = dateutil.monthRange(year, month - 1)[1]; - if (day > daysinmonth) { - while (day > daysinmonth) { - day -= daysinmonth; - ++month; - if (month == 13) { - month = 1; - ++year; - if (year > dateutil.MAXYEAR) { - this._len = total; - return iterResult.getValue(); - } - } - daysinmonth = dateutil.monthRange(year, month - 1)[1]; - } - ii.rebuild(year, month); + if ((!plb(byhour) || contains(byhour, hour)) && + (!plb(byminute) || contains(byminute, minute)) && + (!plb(bysecond) || contains(bysecond, second))) { + break + } + } + timeset = gettimeset.call(ii, hour, minute, second) + } + + if (fixday && day > 28) { + var daysinmonth = dateutil.monthRange(year, month - 1)[1] + if (day > daysinmonth) { + while (day > daysinmonth) { + day -= daysinmonth + ++month + if (month === 13) { + month = 1 + ++year + if (year > dateutil.MAXYEAR) { + this._len = total + return iterResult.getValue() } + } + daysinmonth = dateutil.monthRange(year, month - 1)[1] } + ii.rebuild(year, month) + } } + } } -}; - + } -RRule.parseString = function(rfcString) { - rfcString = rfcString.replace(/^\s+|\s+$/, ''); - if (!rfcString.length) { - return null; - } + RRule.parseString = function (rfcString) { + rfcString = rfcString.replace(/^\s+|\s+$/, '') + if (!rfcString.length) return null - var i, j, key, value, attr, - attrs = rfcString.split(';'), - options = {}; + var i, j, key, value, attr + var attrs = rfcString.split(';') + var options = {} for (i = 0; i < attrs.length; i++) { - attr = attrs[i].split('='); - key = attr[0]; - value = attr[1]; - switch (key) { - case 'FREQ': - options.freq = RRule[value]; - break; - case 'WKST': - options.wkst = RRule[value]; - break; - case 'COUNT': - case 'INTERVAL': - case 'BYSETPOS': - case 'BYMONTH': - case 'BYMONTHDAY': - case 'BYYEARDAY': - case 'BYWEEKNO': - case 'BYHOUR': - case 'BYMINUTE': - case 'BYSECOND': - if (value.indexOf(',') != -1) { - value = value.split(','); - for (j = 0; j < value.length; j++) { - if (/^[+-]?\d+$/.test(value[j])) { - value[j] = Number(value[j]); - } - } - } else if (/^[+-]?\d+$/.test(value)) { - value = Number(value); - } - key = key.toLowerCase(); - options[key] = value; - break; - case 'BYDAY': // => byweekday - var n, wday, day, days = value.split(','); - options.byweekday = []; - for (j = 0; j < days.length; j++) { - day = days[j]; - if (day.length == 2) { // MO, TU, ... - wday = RRule[day]; // wday instanceof Weekday - options.byweekday.push(wday); - } else { // -1MO, +3FR, 1SO, ... - day = day.match(/^([+-]?\d)([A-Z]{2})$/); - n = Number(day[1]); - wday = day[2]; - wday = RRule[wday].weekday; - options.byweekday.push(new Weekday(wday, n)); - } - } - break; - case 'DTSTART': - options.dtstart = dateutil.untilStringToDate(value); - break; - case 'UNTIL': - options.until = dateutil.untilStringToDate(value); - break; - case 'BYEASTER': - options.byeaster = Number(value); - break; - default: - throw new Error("Unknown RRULE property '" + key + "'"); - } + attr = attrs[i].split('=') + key = attr[0] + value = attr[1] + switch (key) { + case 'FREQ': + options.freq = RRule[value] + break + case 'WKST': + options.wkst = RRule[value] + break + case 'COUNT': + case 'INTERVAL': + case 'BYSETPOS': + case 'BYMONTH': + case 'BYMONTHDAY': + case 'BYYEARDAY': + case 'BYWEEKNO': + case 'BYHOUR': + case 'BYMINUTE': + case 'BYSECOND': + if (value.indexOf(',') !== -1) { + value = value.split(',') + for (j = 0; j < value.length; j++) { + if (/^[+-]?\d+$/.test(value[j])) value[j] = Number(value[j]) + } + } else if (/^[+-]?\d+$/.test(value)) { + value = Number(value) + } + key = key.toLowerCase() + options[key] = value + break + case 'BYDAY': // => byweekday + var n, wday, day + var days = value.split(',') + + options.byweekday = [] + for (j = 0; j < days.length; j++) { + day = days[j] + if (day.length === 2) { // MO, TU, ... + wday = RRule[day] // wday instanceof Weekday + options.byweekday.push(wday) + } else { // -1MO, +3FR, 1SO, ... + day = day.match(/^([+-]?\d)([A-Z]{2})$/) + n = Number(day[1]) + wday = day[2] + wday = RRule[wday].weekday + options.byweekday.push(new Weekday(wday, n)) + } + } + break + case 'DTSTART': + options.dtstart = dateutil.untilStringToDate(value) + break + case 'UNTIL': + options.until = dateutil.untilStringToDate(value) + break + case 'BYEASTER': + options.byeaster = Number(value) + break + default: + throw new Error("Unknown RRULE property '" + key + "'") + } } - return options; -}; - - -RRule.fromString = function(string) { - return new RRule(RRule.parseString(string)); -}; - - -//============================================================================= -// Iterinfo -//============================================================================= - -var Iterinfo = function(rrule) { - this.rrule = rrule; - this.lastyear = null; - this.lastmonth = null; - this.yearlen = null; - this.nextyearlen = null; - this.yearordinal = null; - this.yearweekday = null; - this.mmask = null; - this.mrange = null; - this.mdaymask = null; - this.nmdaymask = null; - this.wdaymask = null; - this.wnomask = null; - this.nwdaymask = null; - this.eastermask = null; -}; - -Iterinfo.prototype.easter = function(y, offset) { - offset = offset || 0; - - var a = y % 19, - b = Math.floor(y / 100), - c = y % 100, - d = Math.floor(b / 4), - e = b % 4, - f = Math.floor((b + 8) / 25), - g = Math.floor((b - f + 1) / 3), - h = Math.floor(19 * a + b - d - g + 15) % 30, - i = Math.floor(c / 4), - k = c % 4, - l = Math.floor(32 + 2 * e + 2 * i - h - k) % 7, - m = Math.floor((a + 11 * h + 22 * l) / 451), - month = Math.floor((h + l - 7 * m + 114) / 31), - day = (h + l - 7 * m + 114) % 31 + 1, - date = Date.UTC(y, month - 1, day + offset), - yearStart = Date.UTC(y, 0, 1); - - return [ Math.ceil((date - yearStart) / (1000 * 60 * 60 * 24)) ]; -} - -Iterinfo.prototype.rebuild = function(year, month) { - - var rr = this.rrule; - - if (year != this.lastyear) { - - this.yearlen = dateutil.isLeapYear(year) ? 366 : 365; - this.nextyearlen = dateutil.isLeapYear(year + 1) ? 366 : 365; - var firstyday = new Date(year, 0, 1); - - this.yearordinal = dateutil.toOrdinal(firstyday); - this.yearweekday = dateutil.getWeekday(firstyday); - - var wday = dateutil.getWeekday(new Date(year, 0, 1)); - - if (this.yearlen == 365) { - this.mmask = [].concat(M365MASK); - this.mdaymask = [].concat(MDAY365MASK); - this.nmdaymask = [].concat(NMDAY365MASK); - this.wdaymask = WDAYMASK.slice(wday); - this.mrange = [].concat(M365RANGE); + return options + } + + RRule.fromString = function (string) { + return new RRule(RRule.parseString(string)) + } + + // ============================================================================= + // Iterinfo + // ============================================================================= + + var Iterinfo = function (rrule) { + this.rrule = rrule + this.lastyear = null + this.lastmonth = null + this.yearlen = null + this.nextyearlen = null + this.yearordinal = null + this.yearweekday = null + this.mmask = null + this.mrange = null + this.mdaymask = null + this.nmdaymask = null + this.wdaymask = null + this.wnomask = null + this.nwdaymask = null + this.eastermask = null + } + + Iterinfo.prototype.easter = function (y, offset) { + offset = offset || 0 + + var a = y % 19 + var b = Math.floor(y / 100) + var c = y % 100 + var d = Math.floor(b / 4) + var e = b % 4 + var f = Math.floor((b + 8) / 25) + var g = Math.floor((b - f + 1) / 3) + var h = Math.floor(19 * a + b - d - g + 15) % 30 + var i = Math.floor(c / 4) + var k = c % 4 + var l = Math.floor(32 + 2 * e + 2 * i - h - k) % 7 + var m = Math.floor((a + 11 * h + 22 * l) / 451) + var month = Math.floor((h + l - 7 * m + 114) / 31) + var day = (h + l - 7 * m + 114) % 31 + 1 + var date = Date.UTC(y, month - 1, day + offset) + var yearStart = Date.UTC(y, 0, 1) + + return [Math.ceil((date - yearStart) / (1000 * 60 * 60 * 24))] + } + + Iterinfo.prototype.rebuild = function (year, month) { + var rr = this.rrule + + if (year !== this.lastyear) { + this.yearlen = dateutil.isLeapYear(year) ? 366 : 365 + this.nextyearlen = dateutil.isLeapYear(year + 1) ? 366 : 365 + var firstyday = new Date(year, 0, 1) + + this.yearordinal = dateutil.toOrdinal(firstyday) + this.yearweekday = dateutil.getWeekday(firstyday) + + var wday = dateutil.getWeekday(new Date(year, 0, 1)) + + if (this.yearlen === 365) { + this.mmask = [].concat(M365MASK) + this.mdaymask = [].concat(MDAY365MASK) + this.nmdaymask = [].concat(NMDAY365MASK) + this.wdaymask = WDAYMASK.slice(wday) + this.mrange = [].concat(M365RANGE) + } else { + this.mmask = [].concat(M366MASK) + this.mdaymask = [].concat(MDAY366MASK) + this.nmdaymask = [].concat(NMDAY366MASK) + this.wdaymask = WDAYMASK.slice(wday) + this.mrange = [].concat(M366RANGE) + } + + if (!plb(rr.options.byweekno)) { + this.wnomask = null + } else { + this.wnomask = repeat(0, this.yearlen + 7) + var no1wkst, firstwkst, wyearlen + no1wkst = firstwkst = pymod(7 - this.yearweekday + rr.options.wkst, 7) + if (no1wkst >= 4) { + no1wkst = 0 + // Number of days in the year, plus the days we got + // from last year. + wyearlen = this.yearlen + pymod(this.yearweekday - rr.options.wkst, 7) } else { - this.mmask = [].concat(M366MASK); - this.mdaymask = [].concat(MDAY366MASK); - this.nmdaymask = [].concat(NMDAY366MASK); - this.wdaymask = WDAYMASK.slice(wday); - this.mrange = [].concat(M366RANGE); - } - - if (!plb(rr.options.byweekno)) { - this.wnomask = null; - } else { - this.wnomask = repeat(0, this.yearlen + 7); - var no1wkst, firstwkst, wyearlen; - no1wkst = firstwkst = pymod( - 7 - this.yearweekday + rr.options.wkst, 7); - if (no1wkst >= 4) { - no1wkst = 0; - // Number of days in the year, plus the days we got - // from last year. - wyearlen = this.yearlen + pymod( - this.yearweekday - rr.options.wkst, 7); - } else { - // Number of days in the year, minus the days we - // left in last year. - wyearlen = this.yearlen - no1wkst; - } - var div = Math.floor(wyearlen / 7); - var mod = pymod(wyearlen, 7); - var numweeks = Math.floor(div + (mod / 4)); - for (var n, i, j = 0; j < rr.options.byweekno.length; j++) { - n = rr.options.byweekno[j]; - if (n < 0) { - n += numweeks + 1; - } if (!(0 < n && n <= numweeks)) { - continue; - } if (n > 1) { - i = no1wkst + (n - 1) * 7; - if (no1wkst != firstwkst) { - i -= 7-firstwkst; - } - } else { - i = no1wkst; - } - for (var k = 0; k < 7; k++) { - this.wnomask[i] = 1; - i++; - if (this.wdaymask[i] == rr.options.wkst) { - break; - } - } + // Number of days in the year, minus the days we + // left in last year. + wyearlen = this.yearlen - no1wkst + } + var div = Math.floor(wyearlen / 7) + var mod = pymod(wyearlen, 7) + var numweeks = Math.floor(div + (mod / 4)) + for (var n, i, j = 0; j < rr.options.byweekno.length; j++) { + n = rr.options.byweekno[j] + if (n < 0) { + n += numweeks + 1 + } if (!(n > 0 && n <= numweeks)) { + continue + } if (n > 1) { + i = no1wkst + (n - 1) * 7 + if (no1wkst !== firstwkst) { + i -= 7 - firstwkst } - - if (contains(rr.options.byweekno, 1)) { - // Check week number 1 of next year as well - // orig-TODO : Check -numweeks for next year. - var i = no1wkst + numweeks * 7; - if (no1wkst != firstwkst) { - i -= 7 - firstwkst; - } - if (i < this.yearlen) { - // If week starts in next year, we - // don't care about it. - for (var j = 0; j < 7; j++) { - this.wnomask[i] = 1; - i += 1; - if (this.wdaymask[i] == rr.options.wkst) { - break; - } - } - } + } else { + i = no1wkst + } + for (var k = 0; k < 7; k++) { + this.wnomask[i] = 1 + i++ + if (this.wdaymask[i] === rr.options.wkst) break + } + } + + if (contains(rr.options.byweekno, 1)) { + // Check week number 1 of next year as well + // orig-TODO : Check -numweeks for next year. + i = no1wkst + numweeks * 7 + if (no1wkst !== firstwkst) i -= 7 - firstwkst + if (i < this.yearlen) { + // If week starts in next year, we + // don't care about it. + for (j = 0; j < 7; j++) { + this.wnomask[i] = 1 + i += 1 + if (this.wdaymask[i] === rr.options.wkst) break } - - if (no1wkst) { - // Check last week number of last year as - // well. If no1wkst is 0, either the year - // started on week start, or week number 1 - // got days from last year, so there are no - // days from last year's last week number in - // this year. - var lnumweeks; - if (!contains(rr.options.byweekno, -1)) { - var lyearweekday = dateutil.getWeekday( - new Date(year - 1, 0, 1)); - var lno1wkst = pymod( - 7 - lyearweekday + rr.options.wkst, 7); - var lyearlen = dateutil.isLeapYear(year - 1) ? 366 : 365; - if (lno1wkst >= 4) { - lno1wkst = 0; - lnumweeks = Math.floor( - 52 - + pymod( - lyearlen + pymod( - lyearweekday - rr.options.wkst, 7), 7) / 4); - } else { - lnumweeks = Math.floor( - 52 + pymod(this.yearlen - no1wkst, 7) / 4); - } - } else { - lnumweeks = -1; - } - if (contains(rr.options.byweekno, lnumweeks)) { - for (var i = 0; i < no1wkst; i++) { - this.wnomask[i] = 1; - } - } + } + } + + if (no1wkst) { + // Check last week number of last year as + // well. If no1wkst is 0, either the year + // started on week start, or week number 1 + // got days from last year, so there are no + // days from last year's last week number in + // this year. + var lnumweeks + if (!contains(rr.options.byweekno, -1)) { + var lyearweekday = dateutil.getWeekday(new Date(year - 1, 0, 1)) + var lno1wkst = pymod(7 - lyearweekday + rr.options.wkst, 7) + var lyearlen = dateutil.isLeapYear(year - 1) ? 366 : 365 + if (lno1wkst >= 4) { + lno1wkst = 0 + lnumweeks = Math.floor(52 + + pymod(lyearlen + pymod(lyearweekday - rr.options.wkst, 7), 7) / 4) + } else { + lnumweeks = Math.floor(52 + pymod(this.yearlen - no1wkst, 7) / 4) } - } + } else { + lnumweeks = -1 + } + if (contains(rr.options.byweekno, lnumweeks)) { + for (i = 0; i < no1wkst; i++) this.wnomask[i] = 1 + } + } + } } - if (plb(rr.options.bynweekday) - && (month != this.lastmonth || year != this.lastyear)) { - var ranges = []; - if (rr.options.freq == RRule.YEARLY) { - if (plb(rr.options.bymonth)) { - for (j = 0; j < rr.options.bymonth.length; j++) { - month = rr.options.bymonth[j]; - ranges.push(this.mrange.slice(month - 1, month + 1)); - } + if (plb(rr.options.bynweekday) && (month !== this.lastmonth || year !== this.lastyear)) { + var ranges = [] + if (rr.options.freq === RRule.YEARLY) { + if (plb(rr.options.bymonth)) { + for (j = 0; j < rr.options.bymonth.length; j++) { + month = rr.options.bymonth[j] + ranges.push(this.mrange.slice(month - 1, month + 1)) + } + } else { + ranges = [[0, this.yearlen]] + } + } else if (rr.options.freq === RRule.MONTHLY) { + ranges = [this.mrange.slice(month - 1, month + 1)] + } + if (plb(ranges)) { + // Weekly frequency won't get here, so we may not + // care about cross-year weekly periods. + this.nwdaymask = repeat(0, this.yearlen) + + for (j = 0; j < ranges.length; j++) { + var rang = ranges[j] + var first = rang[0] + var last = rang[1] + last -= 1 + for (k = 0; k < rr.options.bynweekday.length; k++) { + wday = rr.options.bynweekday[k][0] + n = rr.options.bynweekday[k][1] + if (n < 0) { + i = last + (n + 1) * 7 + i -= pymod(this.wdaymask[i] - wday, 7) } else { - ranges = [[0, this.yearlen]]; + i = first + (n - 1) * 7 + i += pymod(7 - this.wdaymask[i] + wday, 7) } - } else if (rr.options.freq == RRule.MONTHLY) { - ranges = [this.mrange.slice(month - 1, month + 1)]; - } - if (plb(ranges)) { - // Weekly frequency won't get here, so we may not - // care about cross-year weekly periods. - this.nwdaymask = repeat(0, this.yearlen); - - for (var j = 0; j < ranges.length; j++) { - var rang = ranges[j]; - var first = rang[0], last = rang[1]; - last -= 1; - for (var k = 0; k < rr.options.bynweekday.length; k++) { - var wday = rr.options.bynweekday[k][0], - n = rr.options.bynweekday[k][1]; - if (n < 0) { - i = last + (n + 1) * 7; - i -= pymod(this.wdaymask[i] - wday, 7); - } else { - i = first + (n - 1) * 7; - i += pymod(7 - this.wdaymask[i] + wday, 7); - } - if (first <= i && i <= last) { - this.nwdaymask[i] = 1; - } - } - } - + if (first <= i && i <= last) this.nwdaymask[i] = 1 + } } + } - this.lastyear = year; - this.lastmonth = month; + this.lastyear = year + this.lastmonth = month } if (rr.options.byeaster !== null) { - this.eastermask = this.easter(year, rr.options.byeaster); + this.eastermask = this.easter(year, rr.options.byeaster) } -}; - -Iterinfo.prototype.ydayset = function(year, month, day) { - return [range(this.yearlen), 0, this.yearlen]; -}; - -Iterinfo.prototype.mdayset = function(year, month, day) { - var set = repeat(null, this.yearlen); - var start = this.mrange[month-1]; - var end = this.mrange[month]; - for (var i = start; i < end; i++) { - set[i] = i; - } - return [set, start, end]; -}; + } + + Iterinfo.prototype.ydayset = function (year, month, day) { + return [range(this.yearlen), 0, this.yearlen] + } -Iterinfo.prototype.wdayset = function(year, month, day) { + Iterinfo.prototype.mdayset = function (year, month, day) { + var set = repeat(null, this.yearlen) + var start = this.mrange[month - 1] + var end = this.mrange[month] + for (var i = start; i < end; i++) set[i] = i + return [set, start, end] + } + Iterinfo.prototype.wdayset = function (year, month, day) { // We need to handle cross-year weeks here. - var set = repeat(null, this.yearlen + 7); - var i = dateutil.toOrdinal( - new Date(year, month - 1, day)) - this.yearordinal; - var start = i; + var set = repeat(null, this.yearlen + 7) + var i = dateutil.toOrdinal(new Date(year, month - 1, day)) - this.yearordinal + var start = i for (var j = 0; j < 7; j++) { - set[i] = i; - ++i; - if (this.wdaymask[i] == this.rrule.options.wkst) { - break; - } + set[i] = i + ++i + if (this.wdaymask[i] === this.rrule.options.wkst) break } - return [set, start, i]; -}; - -Iterinfo.prototype.ddayset = function(year, month, day) { - var set = repeat(null, this.yearlen); - var i = dateutil.toOrdinal( - new Date(year, month - 1, day)) - this.yearordinal; - set[i] = i; - return [set, i, i + 1]; -}; - -Iterinfo.prototype.htimeset = function(hour, minute, second, millisecond) { - var set = [], rr = this.rrule; + return [set, start, i] + } + + Iterinfo.prototype.ddayset = function (year, month, day) { + var set = repeat(null, this.yearlen) + var i = dateutil.toOrdinal(new Date(year, month - 1, day)) - this.yearordinal + set[i] = i + return [set, i, i + 1] + } + + Iterinfo.prototype.htimeset = function (hour, minute, second, millisecond) { + var set = [] + var rr = this.rrule for (var i = 0; i < rr.options.byminute.length; i++) { - minute = rr.options.byminute[i]; - for (var j = 0; j < rr.options.bysecond.length; j++) { - second = rr.options.bysecond[j]; - set.push(new dateutil.Time(hour, minute, second, millisecond)); - } + minute = rr.options.byminute[i] + for (var j = 0; j < rr.options.bysecond.length; j++) { + second = rr.options.bysecond[j] + set.push(new dateutil.Time(hour, minute, second, millisecond)) + } } - dateutil.sort(set); - return set; -}; + dateutil.sort(set) + return set + } -Iterinfo.prototype.mtimeset = function(hour, minute, second,millisecond) { - var set = [], rr = this.rrule; + Iterinfo.prototype.mtimeset = function (hour, minute, second, millisecond) { + var set = [] + var rr = this.rrule for (var j = 0; j < rr.options.bysecond.length; j++) { - second = rr.options.bysecond[j]; - set.push(new dateutil.Time(hour, minute, second, millisecond)); + second = rr.options.bysecond[j] + set.push(new dateutil.Time(hour, minute, second, millisecond)) } - dateutil.sort(set); - return set; -}; - -Iterinfo.prototype.stimeset = function(hour, minute, second, millisecond) { - return [new dateutil.Time(hour, minute, second, millisecond)]; -}; - - -//============================================================================= -// Results -//============================================================================= - -/** - * This class helps us to emulate python's generators, sorta. - */ -var IterResult = function(method, args) { + dateutil.sort(set) + return set + } + + Iterinfo.prototype.stimeset = function (hour, minute, second, millisecond) { + return [new dateutil.Time(hour, minute, second, millisecond)] + } + + // ============================================================================= + // Results + // ============================================================================= + + /** + * This class helps us to emulate python's generators, sorta. + */ + var IterResult = function (method, args) { this.init(method, args) -}; - -IterResult.prototype = { - - init: function(method, args) { - this.method = method; - this.args = args; - - this._result = []; - - this.minDate = null; - this.maxDate = null; - - if (method == 'between') { - this.maxDate = args.inc - ? args.before - : new Date(args.before.getTime() - 1); - this.minDate = args.inc - ? args.after - : new Date(args.after.getTime() + 1); - } else if (method == 'before') { - this.maxDate = args.inc ? args.dt : new Date(args.dt.getTime() - 1); - } else if (method == 'after') { - this.minDate = args.inc ? args.dt : new Date(args.dt.getTime() + 1); - } + } + + IterResult.prototype = { + init: function (method, args) { + this.method = method + this.args = args + this.minDate = null + this.maxDate = null + this._result = [] + + if (method === 'between') { + this.maxDate = args.inc + ? args.before : new Date(args.before.getTime() - 1) + this.minDate = args.inc + ? args.after : new Date(args.after.getTime() + 1) + } else if (method === 'before') { + this.maxDate = args.inc ? args.dt : new Date(args.dt.getTime() - 1) + } else if (method === 'after') { + this.minDate = args.inc ? args.dt : new Date(args.dt.getTime() + 1) + } }, /** @@ -1814,30 +1654,25 @@ IterResult.prototype = { * * @param {Date} date - the date isn't necessarly added to the result * list (if it is too late/too early) - * @return {Boolean} true if it makes sense to continue the iteration; + * @return {Boolean} true if it makes sense to continue the iteration * false if we're done. */ - accept: function(date) { - var tooEarly = this.minDate && date < this.minDate, - tooLate = this.maxDate && date > this.maxDate; - - if (this.method == 'between') { - if (tooEarly) - return true; - if (tooLate) - return false; - } else if (this.method == 'before') { - if (tooLate) - return false; - } else if (this.method == 'after') { - if (tooEarly) - return true; - this.add(date); - return false; - } - - return this.add(date); - + accept: function (date) { + var tooEarly = this.minDate && date < this.minDate + var tooLate = this.maxDate && date > this.maxDate + + if (this.method === 'between') { + if (tooEarly) return true + if (tooLate) return false + } else if (this.method === 'before') { + if (tooLate) return false + } else if (this.method === 'after') { + if (tooEarly) return true + this.add(date) + return false + } + + return this.add(date) }, /** @@ -1845,9 +1680,9 @@ IterResult.prototype = { * @param {Date} date that is part of the result. * @return {Boolean} whether we are interested in more values. */ - add: function(date) { - this._result.push(date); - return true; + add: function (date) { + this._result.push(date) + return true }, /** @@ -1855,149 +1690,142 @@ IterResult.prototype = { * and 'between' an array. * @return {Date,Array?} */ - getValue: function() { - switch (this.method) { - case 'all': - case 'between': - return this._result; - case 'before': - case 'after': - return this._result.length - ? this._result[this._result.length - 1] - : null; - } + getValue: function () { + var res = this._result + switch (this.method) { + case 'all': + case 'between': + return res + case 'before': + case 'after': + return res.length ? res[res.length - 1] : null + } }, clone: function () { return new IterResult(this.method, this.args) } - -}; - - -/** - * IterResult subclass that calls a callback function on each add, - * and stops iterating when the callback returns false. - */ -var CallbackIterResult = function(method, args, iterator) { - var allowedMethods = ['all', 'between']; + } + + /** + * IterResult subclass that calls a callback function on each add, + * and stops iterating when the callback returns false. + */ + var CallbackIterResult = function (method, args, iterator) { + var allowedMethods = ['all', 'between'] if (!contains(allowedMethods, method)) { - throw new Error('Invalid method "' + method - + '". Only all and between works with iterator.'); + throw new Error('Invalid method "' + method + + '". Only all and between works with iterator.') + } + this.add = function (date) { + if (iterator(date, this._result.length)) { + this._result.push(date) + return true + } + return false } - this.add = function(date) { - if (iterator(date, this._result.length)) { - this._result.push(date); - return true; - } - return false; - - }; - - this.init(method, args); - -}; -CallbackIterResult.prototype = IterResult.prototype; -/** - * - * @param {Boolean?} noCache - * The same stratagy as RRule on cache, default to false - * @constructor - */ + this.init(method, args) + } + CallbackIterResult.prototype = IterResult.prototype -var RRuleSet = function (noCache) { + /** + * + * @param {Boolean?} noCache + * The same stratagy as RRule on cache, default to false + * @constructor + */ + var RRuleSet = function (noCache) { // Let RRuleSet cacheable this._cache = noCache ? null : { - all: false, - before: [], - after: [], - between: [] - }; - this._rrule = []; - this._rdate = []; - this._exrule = []; - this._exdate = []; -}; - -RRuleSet.prototype = { + all: false, + before: [], + after: [], + between: [] + } + this._rrule = [] + this._rdate = [] + this._exrule = [] + this._exdate = [] + } + RRuleSet.prototype = { constructor: RRuleSet, /** * @param {RRule} */ rrule: function (rrule) { - if (!(rrule instanceof RRule)) { - throw new TypeError(String(rrule) + ' is not RRule instance'); - } - if (!contains(this._rrule.map(String), String(rrule))) { - this._rrule.push(rrule); - } + if (!(rrule instanceof RRule)) { + throw new TypeError(String(rrule) + ' is not RRule instance') + } + if (!contains(this._rrule.map(String), String(rrule))) { + this._rrule.push(rrule) + } }, /** * @param {Date} */ rdate: function (date) { - if (!(date instanceof Date)) { - throw new TypeError(String(date) + ' is not Date instance'); - } - if (!contains(this._rdate.map(Number), Number(date))) { - this._rdate.push(date); - dateutil.sort(this._rdate); - } + if (!(date instanceof Date)) { + throw new TypeError(String(date) + ' is not Date instance') + } + if (!contains(this._rdate.map(Number), Number(date))) { + this._rdate.push(date) + dateutil.sort(this._rdate) + } }, /** * @param {RRule} */ exrule: function (rrule) { - if (!(rrule instanceof RRule)) { - throw new TypeError(String(rrule) + ' is not RRule instance'); - } - if (!contains(this._exrule.map(String), String(rrule))) { - this._exrule.push(rrule); - } + if (!(rrule instanceof RRule)) { + throw new TypeError(String(rrule) + ' is not RRule instance') + } + if (!contains(this._exrule.map(String), String(rrule))) { + this._exrule.push(rrule) + } }, /** * @param {Date} */ exdate: function (date) { - if (!(date instanceof Date)) { - throw new TypeError(String(date) + ' is not Date instance'); - } - if (!contains(this._exdate.map(Number), Number(date))) { - this._exdate.push(date); - dateutil.sort(this._exdate); - } + if (!(date instanceof Date)) { + throw new TypeError(String(date) + ' is not Date instance') + } + if (!contains(this._exdate.map(Number), Number(date))) { + this._exdate.push(date) + dateutil.sort(this._exdate) + } }, valueOf: function () { - var result = [] - if (this._rrule.length) { - this._rrule.forEach(function (rrule) { - result.push('RRULE:' + rrule); - }) - } - if (this._rdate.length) { - result.push('RDATE:' + this._rdate.map(function(rdate){ - return dateutil.timeToUntilString(rdate); - }).join(',')); - } - if (this._exrule.length) { - this._exrule.forEach(function (exrule) { - result.push('EXRULE:' + exrule); - }) - } - if (this._exdate.length) { - result.push('EXDATE:' + this._exdate.map(function(exdate){ - return dateutil.timeToUntilString(exdate); - }).join(',')); - } - return result; + var result = [] + if (this._rrule.length) { + this._rrule.forEach(function (rrule) { + result.push('RRULE:' + rrule) + }) + } + if (this._rdate.length) { + result.push('RDATE:' + this._rdate.map(function (rdate) { + return dateutil.timeToUntilString(rdate) + }).join(',')) + } + if (this._exrule.length) { + this._exrule.forEach(function (exrule) { + result.push('EXRULE:' + exrule) + }) + } + if (this._exdate.length) { + result.push('EXDATE:' + this._exdate.map(function (exdate) { + return dateutil.timeToUntilString(exdate) + }).join(',')) + } + return result }, /** @@ -2005,114 +1833,112 @@ RRuleSet.prototype = { * ["RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU;DTSTART=19970902T010000Z","RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH;DTSTART=19970902T010000Z"] */ toString: function () { - return JSON.stringify(this.valueOf()); + return JSON.stringify(this.valueOf()) }, _iter: function (iterResult) { - var _exdateHash = {}; - var _exrule = this._exrule; - var _accept = iterResult.accept; - - function evalExdate (after, before) { - _exrule.forEach(function (rrule) { - rrule.between(after, before, true).forEach(function (date) { - _exdateHash[Number(date)] = true; - }); - }); - } - - this._exdate.forEach(function (date) { - _exdateHash[Number(date)] = true; - }); - + var _exdateHash = {} + var _exrule = this._exrule + var _accept = iterResult.accept + + function evalExdate (after, before) { + _exrule.forEach(function (rrule) { + rrule.between(after, before, true).forEach(function (date) { + _exdateHash[Number(date)] = true + }) + }) + } + + this._exdate.forEach(function (date) { + _exdateHash[Number(date)] = true + }) + + iterResult.accept = function (date) { + var dt = Number(date) + if (!_exdateHash[dt]) { + evalExdate(new Date(dt - 1), new Date(dt + 1)) + if (!_exdateHash[dt]) { + _exdateHash[dt] = true + return _accept.call(this, date) + } + } + return true + } + + if (iterResult.method === 'between') { + evalExdate(iterResult.args.after, iterResult.args.before) iterResult.accept = function (date) { - var dt = Number(date) - if (!_exdateHash[dt]) { - evalExdate(new Date(dt - 1), new Date(dt + 1)); - if (!_exdateHash[dt]) { - _exdateHash[dt] = true; - return _accept.call(this, date); - } - } - return true; - } - - if (iterResult.method === 'between') { - evalExdate(iterResult.args.after, iterResult.args.before); - iterResult.accept = function (date) { - var dt = Number(date) - if (!_exdateHash[dt]) { - _exdateHash[dt] = true; - return _accept.call(this, date); - } - return true; - } - } - - for (var i = 0; i < this._rdate.length; i++) { - if (!iterResult.accept(new Date(this._rdate[i]))) { - break; - } - } - - this._rrule.forEach(function (rrule) { - rrule._iter(iterResult); - }); - - var result = iterResult._result; - dateutil.sort(result); - switch (iterResult.method) { - case 'all': - case 'between': - return result; - case 'before': - return (result.length && result[result.length - 1]) || null; - case 'after': - return (result.length && result[0]) || null; - default: - return null; - } + var dt = Number(date) + if (!_exdateHash[dt]) { + _exdateHash[dt] = true + return _accept.call(this, date) + } + return true + } + } + + for (var i = 0; i < this._rdate.length; i++) { + if (!iterResult.accept(new Date(this._rdate[i]))) break + } + + this._rrule.forEach(function (rrule) { + rrule._iter(iterResult) + }) + + var res = iterResult._result + dateutil.sort(res) + switch (iterResult.method) { + case 'all': + case 'between': + return res + case 'before': + return (res.length && res[res.length - 1]) || null + case 'after': + return (res.length && res[0]) || null + default: + return null + } }, /** * Create a new RRuleSet Object completely base on current instance */ clone: function () { - var rrs = new RRuleSet(!!this._cache); - var i - for (i = 0; i < this._rrule.length; i++) { - rrs.rrule(this._rrule[i].clone()); - } - for (i = 0; i < this._rdate.length; i++) { - rrs.rdate(new Date(this._rdate[i])); - } - for (i = 0; i < this._exrule.length; i++) { - rrs.exrule(this._exrule[i].clone()); - } - for (i = 0; i < this._exdate.length; i++) { - rrs.exdate(new Date(this._exdate[i])); - } - return rrs; + var rrs = new RRuleSet(!!this._cache) + var i + for (i = 0; i < this._rrule.length; i++) { + rrs.rrule(this._rrule[i].clone()) + } + for (i = 0; i < this._rdate.length; i++) { + rrs.rdate(new Date(this._rdate[i])) + } + for (i = 0; i < this._exrule.length; i++) { + rrs.exrule(this._exrule[i].clone()) + } + for (i = 0; i < this._exdate.length; i++) { + rrs.exdate(new Date(this._exdate[i])) + } + return rrs } -}; + } -/** - * Inherts method from RRule - * add Read interface and set RRuleSet cacheable - */ + /** + * Inherts method from RRule + * add Read interface and set RRuleSet cacheable + */ + var RRuleSetMethods = ['all', 'between', 'before', 'after', 'count', '_cacheAdd', '_cacheGet'] + RRuleSetMethods.forEach(function (method) { + RRuleSet.prototype[method] = RRule.prototype[method] + }) -['all', 'between', 'before', 'after', 'count', '_cacheAdd', '_cacheGet'].forEach(function (method) { - RRuleSet.prototype[method] = RRule.prototype[method]; -}); + /** + * RRuleStr + * To parse a set of rrule strings + */ -/** - * RRuleStr - * To parse a set of rrule strings - */ - -var RRuleStr = function () {}; + var RRuleStr = function () {} -RRuleStr.DEFAULT_OPTIONS = { + RRuleStr.DEFAULT_OPTIONS = { dtstart: null, cache: false, unfold: false, @@ -2120,360 +1946,334 @@ RRuleStr.DEFAULT_OPTIONS = { compatible: false, ignoretz: false, tzinfos: null -}; - -RRuleStr._freq_map = {"YEARLY": RRule.YEARLY, - "MONTHLY": RRule.MONTHLY, - "WEEKLY": RRule.WEEKLY, - "DAILY": RRule.DAILY, - "HOURLY": RRule.HOURLY, - "MINUTELY": RRule.MINUTELY, - "SECONDLY": RRule.SECONDLY}; - -RRuleStr._weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, - "FR": 4, "SA": 5, "SU": 6}; - -RRuleStr.prototype = { - + } + + RRuleStr._freq_map = { + 'YEARLY': RRule.YEARLY, + 'MONTHLY': RRule.MONTHLY, + 'WEEKLY': RRule.WEEKLY, + 'DAILY': RRule.DAILY, + 'HOURLY': RRule.HOURLY, + 'MINUTELY': RRule.MINUTELY, + 'SECONDLY': RRule.SECONDLY + } + + RRuleStr._weekday_map = { + 'MO': 0, + 'TU': 1, + 'WE': 2, + 'TH': 3, + 'FR': 4, + 'SA': 5, + 'SU': 6 + } + + RRuleStr.prototype = { constructor: RRuleStr, _handle_int: function (rrkwargs, name, value, options) { - rrkwargs[name.toLowerCase()] = parseInt(value); + rrkwargs[name.toLowerCase()] = parseInt(value, 10) }, _handle_int_list: function (rrkwargs, name, value, options) { - rrkwargs[name.toLowerCase()] = value.split(',').map(function(x) { return parseInt(x)}); + rrkwargs[name.toLowerCase()] = value.split(',').map(function (x) { + return parseInt(x, 10) + }) }, _handle_FREQ: function (rrkwargs, name, value, options) { - rrkwargs['freq'] = RRuleStr._freq_map[value]; + rrkwargs['freq'] = RRuleStr._freq_map[value] }, _handle_UNTIL: function (rrkwargs, name, value, options) { - try { - rrkwargs['until'] = dateutil.untilStringToDate(value); - } catch (error) { - throw new Error('invalid until date'); - } + try { + rrkwargs['until'] = dateutil.untilStringToDate(value) + } catch (error) { + throw new Error('invalid until date') + } }, _handle_WKST: function (rrkwargs, name, value, options) { - rrkwargs['wkst'] = RRuleStr._weekday_map[value]; + rrkwargs['wkst'] = RRuleStr._weekday_map[value] }, _handle_BYWEEKDAY: function (rrkwargs, name, value, options) { - // Two ways to specify this: +1MO or MO(+1) - - var l = []; - var i, j; - var wdays = value.split(','), wday; - var splt; - var n, w; - for (i = 0; i < wdays.length; i++) { - wday = wdays[i]; - if (wday.indexOf('(') > -1) { - // If it's of the form TH(+1), etc. - splt = wday.split('('); - w = splt[0]; - n = parseInt(splt.slice(1, -1)); - } else { - // # If it's of the form +1MO - for (j = 0; j < wday.length; j++) { - if ('+-0123456789'.indexOf(wday[j]) == -1) { - break - } - } - n = wday.slice(0, j) || null; - w = wday.slice(j); - - if (n) { - n = parseInt(n); - } - } + // Two ways to specify this: +1MO or MO(+1) + var splt, i, j, n, w, wday + var l = [] + var wdays = value.split(',') + + for (i = 0; i < wdays.length; i++) { + wday = wdays[i] + if (wday.indexOf('(') > -1) { + // If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = parseInt(splt.slice(1, -1), 10) + } else { + // # If it's of the form +1MO + for (j = 0; j < wday.length; j++) { + if ('+-0123456789'.indexOf(wday[j]) === -1) break + } + n = wday.slice(0, j) || null + w = wday.slice(j) - var weekday = new Weekday(RRuleStr._weekday_map[w], n); - l.push(weekday); + if (n) n = parseInt(n, 10) } - rrkwargs['byweekday'] = l; + + var weekday = new Weekday(RRuleStr._weekday_map[w], n) + l.push(weekday) + } + rrkwargs['byweekday'] = l }, _parseRfcRRule: function (line, options) { - options = options || {}; - options.dtstart = options.dtstart || null; - options.cache = options.cache || false; - options.ignoretz = options.ignoretz || false; - options.tzinfos = options.tzinfos || null; - - var parts; - var name, value; - if (line.indexOf(':') != -1) { - parts = line.split(':'); - name = parts[0]; - value = parts[1]; - - if (name != 'RRULE') { - throw new Error('unknown parameter name') - } - } else { - value = line; - } - - var pairs = value.split(';'); - var i; - var rrkwargs = {}; - for (i = 0; i < pairs.length; i++) { - parts = pairs[i].split('='); - name = parts[0].toUpperCase(); - value = parts[1].toUpperCase(); + options = options || {} + options.dtstart = options.dtstart || null + options.cache = options.cache || false + options.ignoretz = options.ignoretz || false + options.tzinfos = options.tzinfos || null + + var name, value, parts + if (line.indexOf(':') !== -1) { + parts = line.split(':') + name = parts[0] + value = parts[1] + + if (name !== 'RRULE') throw new Error('unknown parameter name') + } else { + value = line + } + + var i + var rrkwargs = {} + var pairs = value.split(';') + + for (i = 0; i < pairs.length; i++) { + parts = pairs[i].split('=') + name = parts[0].toUpperCase() + value = parts[1].toUpperCase() - try { - this['_handle_' + name](rrkwargs, name, value , { - ignoretz: options.ignoretz, - tzinfos: options.tzinfos - }); - } catch (error) { - throw new Error("unknown parameter '" + name + "':" + value ) - } + try { + this['_handle_' + name](rrkwargs, name, value, { + ignoretz: options.ignoretz, + tzinfos: options.tzinfos + }) + } catch (error) { + throw new Error("unknown parameter '" + name + "':" + value) } - rrkwargs.dtstart = rrkwargs.dtstart || options.dtstart; - return new RRule(rrkwargs, !options.cache); + } + rrkwargs.dtstart = rrkwargs.dtstart || options.dtstart + return new RRule(rrkwargs, !options.cache) }, _parseRfc: function (s, options) { - - if (options.compatible) { - options.forceset = true; - options.unfold = true; - } - - s = s.toUpperCase().trim(); - - if (!s) { - throw new Error('Invalid empty string') - } - - var i = 0; - var line, lines; - - // More info about 'unfold' option - // Go head to http://www.ietf.org/rfc/rfc2445.txt - if (options.unfold) { - lines = s.split('\n'); - while (i < lines.length) { - // TODO - line = lines[i] = lines[i].replace(/\s+$/g, ''); - if (!line) { - lines.splice(i, 1); - } else if (i > 0 && line[0] == " ") { - lines[i - 1] += line.slice(1); - lines.splice(i, 1); - } else { - i += 1; - } + if (options.compatible) { + options.forceset = true + options.unfold = true + } + + s = s && s.toUpperCase().trim() + if (!s) throw new Error('Invalid empty string') + + var i = 0 + var line, lines + + // More info about 'unfold' option + // Go head to http://www.ietf.org/rfc/rfc2445.txt + if (options.unfold) { + lines = s.split('\n') + while (i < lines.length) { + // TODO + line = lines[i] = lines[i].replace(/\s+$/g, '') + if (!line) { + lines.splice(i, 1) + } else if (i > 0 && line[0] === ' ') { + lines[i - 1] += line.slice(1) + lines.splice(i, 1) + } else { + i += 1 + } + } + } else { + lines = s.split(/\s/) + } + + var rrulevals = [] + var rdatevals = [] + var exrulevals = [] + var exdatevals = [] + var name, value, parts, parms, parm, dtstart, rset, j, k, datestrs, datestr + + if (!options.forceset && lines.length === 1 && (s.indexOf(':') === -1 || + s.indexOf('RRULE:') === 0)) { + return this._parseRfcRRule(lines[0], { + cache: options.cache, + dtstart: options.dtstart, + ignoretz: options.ignoretz, + tzinfos: options.tzinfos + }) + } else { + for (i = 0; i < lines.length; i++) { + line = lines[i] + if (!line) continue + if (line.indexOf(':') === -1) { + name = 'RRULE' + value = line + } else { + parts = split(line, ':', 1) + name = parts[0] + value = parts[1] + } + parms = name.split(';') + if (!parms) throw new Error('empty property name') + name = parms[0] + parms = parms.slice(1) + + if (name === 'RRULE') { + for (j = 0; j < parms.length; j++) { + parm = parms[j] + throw new Error('unsupported RRULE parm: ' + parm) } - } else { - lines = s.split(/\s/); - } - - - var rrulevals = []; - var rdatevals = []; - var exrulevals = []; - var exdatevals = []; - var name; - var value; - var parts; - var parms; - var parm; - var dtstart; - var rset; - var j, k; - var datestrs, datestr; - if (!options.forceset && lines.length === 1 && (s.indexOf(':') == -1 || s.indexOf('RRULE:') == 0)) { - return this._parseRfcRRule(lines[0], { - cache: options.cache, - dtstart: options.dtstart, - ignoretz: options.ignoretz, - tzinfos: options.tzinfos - }); - } else { - for (i = 0; i < lines.length; i++) { - line = lines[i]; - if (!line) { - continue - } - if (line.indexOf(':') == -1) { - name = 'RRULE'; - value = line; - } else { - parts = split(line, ':', 1); - name = parts[0]; - value = parts[1]; - } - parms = name.split(';'); - if (!parms) { - throw new Error('empty property name') - } - name = parms[0]; - parms = parms.slice(1); - - if (name == 'RRULE') { - for(j = 0; j < parms.length; j++) { - parm = parms[j]; - throw new Error('unsupported RRULE parm: '+ parm) - } - rrulevals.push(value); - } else if (name == 'RDATE') { - for(j = 0; j < parms.length; j++) { - parm = parms[j]; - if (parm != 'VALUE=DATE-TIME') { - throw new Error('unsupported RDATE parm: '+ parm) - } - } - rdatevals.push(value); - } else if (name == 'EXRULE') { - for(j = 0; j < parms.length; j++) { - parm = parms[j]; - throw new Error('unsupported EXRULE parm: '+ parm) - } - exrulevals.push(value); - } else if (name == 'EXDATE') { - for(j = 0; j < parms.length; j++) { - parm = parms[j]; - if (parm != 'VALUE=DATE-TIME') { - throw new Error('unsupported RDATE parm: '+ parm) - } - } - exdatevals.push(value); - } else if (name == 'DTSTART') { - dtstart = dateutil.untilStringToDate(value); - } else { - throw new Error("unsupported property: "+ name) - } + rrulevals.push(value) + } else if (name === 'RDATE') { + for (j = 0; j < parms.length; j++) { + parm = parms[j] + if (parm !== 'VALUE=DATE-TIME') { + throw new Error('unsupported RDATE parm: ' + parm) + } } - - if (options.forceset || rrulevals.length > 1 || rdatevals.length || exrulevals.length || exdatevals.length) { - rset = new RRuleSet(!options.cache); - for (j = 0; j < rrulevals.length; j++) { - rset.rrule(this._parseRfcRRule(rrulevals[j], { - dtstart: options.dtstart || dtstart, - ignoretz: options.ignoretz, - tzinfos: options.tzinfos - })); - } - for (j = 0; j < rdatevals.length; j++) { - datestrs = rdatevals[j].split(','); - for (k = 0; k < datestrs.length; k++) { - datestr = datestrs[k]; - rset.rdate(dateutil.untilStringToDate(datestr)); - } - } - for (j = 0; j < exrulevals.length; j++) { - rset.exrule(this._parseRfcRRule(exrulevals[j], { - dtstart: options.dtstart || dtstart, - ignoretz: options.ignoretz, - tzinfos: options.tzinfos - })); - } - for (j = 0; j < exdatevals.length; j++) { - datestrs = exdatevals[j].split(',') - for (k = 0; k < datestrs.length; k++) { - datestr = datestrs[k]; - rset.exdate(dateutil.untilStringToDate(datestr)); - } - } - - if (options.campatiable && options.dtstart) { - rset.rdate(dtstart); - } - return rset; - } else { - return this._parseRfcRRule(rrulevals[0], { - dtstart: options.dtstart || dtstart, - cache: options.cache, - ignoretz: options.ignoretz, - tzinfos: options.tzinfos - }); + rdatevals.push(value) + } else if (name === 'EXRULE') { + for (j = 0; j < parms.length; j++) { + parm = parms[j] + throw new Error('unsupported EXRULE parm: ' + parm) } - } + exrulevals.push(value) + } else if (name === 'EXDATE') { + for (j = 0; j < parms.length; j++) { + parm = parms[j] + if (parm !== 'VALUE=DATE-TIME') { + throw new Error('unsupported RDATE parm: ' + parm) + } + } + exdatevals.push(value) + } else if (name === 'DTSTART') { + dtstart = dateutil.untilStringToDate(value) + } else { + throw new Error('unsupported property: ' + name) + } + } + + if (options.forceset || rrulevals.length > 1 || rdatevals.length || + exrulevals.length || exdatevals.length) { + rset = new RRuleSet(!options.cache) + for (j = 0; j < rrulevals.length; j++) { + rset.rrule(this._parseRfcRRule(rrulevals[j], { + dtstart: options.dtstart || dtstart, + ignoretz: options.ignoretz, + tzinfos: options.tzinfos + })) + } + for (j = 0; j < rdatevals.length; j++) { + datestrs = rdatevals[j].split(',') + for (k = 0; k < datestrs.length; k++) { + datestr = datestrs[k] + rset.rdate(dateutil.untilStringToDate(datestr)) + } + } + for (j = 0; j < exrulevals.length; j++) { + rset.exrule(this._parseRfcRRule(exrulevals[j], { + dtstart: options.dtstart || dtstart, + ignoretz: options.ignoretz, + tzinfos: options.tzinfos + })) + } + for (j = 0; j < exdatevals.length; j++) { + datestrs = exdatevals[j].split(',') + for (k = 0; k < datestrs.length; k++) { + datestr = datestrs[k] + rset.exdate(dateutil.untilStringToDate(datestr)) + } + } + + if (options.campatiable && options.dtstart) rset.rdate(dtstart) + return rset + } else { + return this._parseRfcRRule(rrulevals[0], { + dtstart: options.dtstart || dtstart, + cache: options.cache, + ignoretz: options.ignoretz, + tzinfos: options.tzinfos + }) + } + } }, parse: function (s, options) { - options = options || {}; + options = options || {} - var invalid = [], - keys = Object.keys(options), - defaultKeys = Object.keys(RRuleStr.DEFAULT_OPTIONS); + var invalid = [] + var keys = Object.keys(options) + var defaultKeys = Object.keys(RRuleStr.DEFAULT_OPTIONS) - keys.forEach(function (key) { - if (!contains(defaultKeys, key)) invalid.push(key); - }, this); + keys.forEach(function (key) { + if (!contains(defaultKeys, key)) invalid.push(key) + }, this) - if (invalid.length) { - throw new Error('Invalid options: ' + invalid.join(', ')) - } + if (invalid.length) throw new Error('Invalid options: ' + invalid.join(', ')) - // Merge in default options - defaultKeys.forEach(function(key) { - if (!contains(keys, key)) options[key] = RRuleStr.DEFAULT_OPTIONS[key]; - }); + // Merge in default options + defaultKeys.forEach(function (key) { + if (!contains(keys, key)) options[key] = RRuleStr.DEFAULT_OPTIONS[key] + }) - return this._parseRfc(s, options); + return this._parseRfc(s, options) } -} + } -RRuleStr.prototype._handle_DTSTART = function (rrkwargs, name, value, options) { - rrkwargs[name.toLowerCase()] = dateutil.untilStringToDate(value); -} + RRuleStr.prototype._handle_DTSTART = function (rrkwargs, name, value, options) { + rrkwargs[name.toLowerCase()] = dateutil.untilStringToDate(value) + } -RRuleStr.prototype._handle_BYDAY = RRuleStr.prototype._handle_BYWEEKDAY; + RRuleStr.prototype._handle_BYDAY = RRuleStr.prototype._handle_BYWEEKDAY + RRuleStr.prototype._handle_INTERVAL = RRuleStr.prototype._handle_int + RRuleStr.prototype._handle_COUNT = RRuleStr.prototype._handle_int -RRuleStr.prototype._handle_INTERVAL = RRuleStr.prototype._handle_int; -RRuleStr.prototype._handle_COUNT = RRuleStr.prototype._handle_int; - - -[ - '_handle_BYSETPOS','_handle_BYMONTH','_handle_BYMONTHDAY', - '_handle_BYYEARDAY','_handle_BYEASTER','_handle_BYWEEKNO', - '_handle_BYHOUR','_handle_BYMINUTE','_handle_BYSECOND' -].forEach(function (method) { + ;[ + '_handle_BYSETPOS', '_handle_BYMONTH', '_handle_BYMONTHDAY', + '_handle_BYYEARDAY', '_handle_BYEASTER', '_handle_BYWEEKNO', + '_handle_BYHOUR', '_handle_BYMINUTE', '_handle_BYSECOND' + ].forEach(function (method) { RRuleStr.prototype[method] = RRuleStr.prototype._handle_int_list -}) - - -//============================================================================= -// Export -//============================================================================= - - -// Only one RRuleStr instance for all rrule string parsing work. -var rruleStr = new RRuleStr(); -var rrulestr = function () { - return rruleStr.parse.apply(rruleStr, arguments); -} - -if (serverSide) { - module.exports = { - RRule: RRule, - RRuleSet: RRuleSet, - rrulestr: rrulestr - } -} -if (typeof ender === 'undefined') { - root['RRule'] = RRule; - root['RRuleSet'] = RRuleSet; - root['rrulestr'] = rrulestr; -} - -if (typeof define === "function" && define.amd) { + }) + + // ============================================================================= + // Export + // ============================================================================= + + // Only one RRuleStr instance for all rrule string parsing work. + var rruleStr = new RRuleStr() + var rrulestr = function () { + return rruleStr.parse.apply(rruleStr, arguments) + } + + RRule.RRule = RRule + RRule.RRuleSet = RRuleSet + RRule.rrulestr = rrulestr + + if (serverSide) module.exports = RRule + if (typeof ender === 'undefined') { + root['RRule'] = RRule + root['RRuleSet'] = RRuleSet + root['rrulestr'] = rrulestr + } + + if (typeof define === 'function' && define.amd) { /*global define:false */ - define("rrule", [], function () { - RRule.RRule = RRule; - RRule.RRuleSet = RRuleSet; - RRule.rrulestr = rrulestr; - // Always return RRule for Backward Compatibility - return RRule; - }); -} - -}(this)); + define('rrule', [], function () { + // Always return RRule for Backward Compatibility + return RRule + }) + } +}(this)) diff --git a/package.json b/package.json index fdb7ade9..3553ddca 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,35 @@ { - "name": "rrule", - "version": "2.2.0-dev", - "description": "JavaScript library for working with recurrence rules for calendar dates.", - "homepage": "http://jkbrzt.github.io/rrule/", - "keywords":[ - "dates", - "recurrences", - "calendar", - "icalendar", - "rfc" - ], - "author": "Jakub Roztocil and Lars Schöning", - "main": "lib/rrule", - "repository" :{ - "type": "git", - "url": "git://github.com/jkbrzt/rrule.git" - }, - "scripts": { - "test": "echo Run tests in the browser at tests/index.html" - } + "name": "rrule", + "version": "2.2.0-dev", + "description": "JavaScript library for working with recurrence rules for calendar dates.", + "homepage": "http://jkbrzt.github.io/rrule/", + "keywords": [ + "dates", + "recurrences", + "calendar", + "icalendar", + "rfc" + ], + "author": "Jakub Roztocil and Lars Schöning", + "main": "lib/rrule", + "repository": { + "type": "git", + "url": "git://github.com/jkbrzt/rrule.git" + }, + "scripts": { + "test": "standard" + }, + "devDependencies": { + "standard": "^5.4.1" + }, + "standard": { + "ignore": [ + "tests/demo", + "tests/vendor" + ] + }, + "files": [ + "lib", + "README.md" + ] } diff --git a/tests/tests.js b/tests/tests.js index cd13b765..e2c9a564 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,2500 +1,3634 @@ +/* global $, test, equal, RRule, RRuleSet, rrulestr + testRecurring, datetimeUTC, datetime, parse, assertStrType*/ + +module('RRule', { + setup: function () { + // Enable additional toString() / fromString() tests + // for each testRecurring(). + this.ALSO_TEST_STRING_FUNCTIONS = true + + // Enable additional toText() / fromText() tests + // for each testRecurring(). + // Many of the tests fail because the conversion is only approximate, + // but it gives an idea about how well or bad it converts. + this.ALSO_TEST_NLP_FUNCTIONS = false + + // Thorough after()/before()/between() tests. + // NOTE: can take a longer time. + this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true + + this.ALSO_TEST_SUBSECOND_PRECISION = true + } +}) -module("RRule", { - - setup: function() { - - // Enable additional toString() / fromString() tests - //for each testRecurring(). - this.ALSO_TEST_STRING_FUNCTIONS = true; - - // Enable additional toText() / fromText() tests - // for each testRecurring(). - // Many of the tests fail because the conversion is only approximate, - // but it gives an idea about how well or bad it converts. - this.ALSO_TEST_NLP_FUNCTIONS = false; - - // Thorough after()/before()/between() tests. - // NOTE: can take a longer time. - this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true; - - this.ALSO_TEST_SUBSECOND_PRECISION = true; - - } - -}); +var texts = [ + ['Every day', 'FREQ=DAILY'], + ['Every day at 10, 12 and 17', 'FREQ=DAILY;BYHOUR=10,12,17'], + ['Every week', 'FREQ=WEEKLY'], + ['Every hour', 'FREQ=HOURLY'], + ['Every 4 hours', 'INTERVAL=4;FREQ=HOURLY'], + ['Every week on Tuesday', 'FREQ=WEEKLY;BYDAY=TU'], + ['Every week on Monday, Wednesday', 'FREQ=WEEKLY;BYDAY=MO,WE'], + ['Every weekday', 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR'], + ['Every 2 weeks', 'INTERVAL=2;FREQ=WEEKLY'], + ['Every month', 'FREQ=MONTHLY'], + ['Every 6 months', 'INTERVAL=6;FREQ=MONTHLY'], + ['Every year', 'FREQ=YEARLY'], + ['Every month on the 4th', 'FREQ=MONTHLY;BYMONTHDAY=4'], + ['Every month on the 4th last', 'FREQ=MONTHLY;BYMONTHDAY=-4'], + ['Every month on the 3rd Tuesday', 'FREQ=MONTHLY;BYDAY=+3TU'], + ['Every month on the 3rd last Tuesday', 'FREQ=MONTHLY;BYDAY=-3TU'], + ['Every month on the last Monday', 'FREQ=MONTHLY;BYDAY=-1MO'], + ['Every month on the 2nd last Friday', 'FREQ=MONTHLY;BYDAY=-2FR'], + // This one will fail. + // The text date should be treated as a floating one, but toString + // always returns UTC dates. + // ['Every week until January 1, 2007', 'FREQ=WEEKLY;UNTIL=20070101T000000Z'], + ['Every week for 20 times', 'FREQ=WEEKLY;COUNT=20'] +] + +test('fromText()', function () { + $.each(texts, function () { + var text = this[0] + var string = this[1] + equal(RRule.fromText(text).toString(), string, text + ' => ' + string) + }) +}) +test('toText()', function () { + $.each(texts, function () { + var text = this[0] + var string = this[1] + equal(RRule.fromString(string).toText().toLowerCase(), text.toLowerCase(), + string + ' => ' + text) + }) +}) -var texts = [ - ['Every day', 'FREQ=DAILY'], - ['Every day at 10, 12 and 17', 'FREQ=DAILY;BYHOUR=10,12,17'], - ['Every week', 'FREQ=WEEKLY'], - ['Every hour', 'FREQ=HOURLY'], - ['Every 4 hours', 'INTERVAL=4;FREQ=HOURLY'], - ['Every week on Tuesday', 'FREQ=WEEKLY;BYDAY=TU'], - ['Every week on Monday, Wednesday', 'FREQ=WEEKLY;BYDAY=MO,WE'], - ['Every weekday', 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR'], - ['Every 2 weeks', 'INTERVAL=2;FREQ=WEEKLY'], - ['Every month', 'FREQ=MONTHLY'], - ['Every 6 months', 'INTERVAL=6;FREQ=MONTHLY'], - ['Every year', 'FREQ=YEARLY'], - ['Every month on the 4th', 'FREQ=MONTHLY;BYMONTHDAY=4'], - ['Every month on the 4th last', 'FREQ=MONTHLY;BYMONTHDAY=-4'], - ['Every month on the 3rd Tuesday', 'FREQ=MONTHLY;BYDAY=+3TU'], - ['Every month on the 3rd last Tuesday', 'FREQ=MONTHLY;BYDAY=-3TU'], - ['Every month on the last Monday', 'FREQ=MONTHLY;BYDAY=-1MO'], - ['Every month on the 2nd last Friday', 'FREQ=MONTHLY;BYDAY=-2FR'], - // This one will fail. - // The text date should be treated as a floating one, but toString - // always returns UTC dates. - // ['Every week until January 1, 2007', 'FREQ=WEEKLY;UNTIL=20070101T000000Z'], - ['Every week for 20 times', 'FREQ=WEEKLY;COUNT=20'] -]; -test('fromText()', function() { - $.each(texts, function(){ - var text = this[0], - string = this[1]; - - equal(RRule.fromText(text).toString(), string, - text + ' => ' + string); - }); - -}); - -test('toText()', function() { - $.each(texts, function(){ - var text = this[0], - string = this[1]; - console.log(text, string) - equal(RRule.fromString(string).toText().toLowerCase(), text.toLowerCase(), - string + ' => ' + text); - }); - -}); - - -strings = [ +test('fromString()', function () { + var strings = [ ['FREQ=WEEKLY;UNTIL=20100101T000000Z', 'FREQ=WEEKLY;UNTIL=20100101T000000Z'], // Parse also `date` but return `date-time` ['FREQ=WEEKLY;UNTIL=20100101', 'FREQ=WEEKLY;UNTIL=20100101T000000Z'] -]; -test('fromString()', function() { - $.each(strings, function(){ - var s = this[0], s2 = this[1]; - equal(RRule.fromString(s).toString(), s2, s + ' => ' + s2); - }); - -}); - - -testRecurring( - 'missing Feb 28 https://github.com/jkbrzt/rrule/issues/21', - new RRule({ - freq: RRule.MONTHLY, - dtstart: new Date(2013, 0, 1), - count: 3, - bymonthday: [28] - }), - [ - new Date(2013, 0, 28), - new Date(2013, 1, 28), - new Date(2013, 2, 28) - ]); - + ] + $.each(strings, function () { + var s = this[0] + var s2 = this[1] + equal(RRule.fromString(s).toString(), s2, s + ' => ' + s2) + }) +}) -//============================================================================= -// The original `dateutil.rrule` test suite converted from Py to JS. -//============================================================================= +testRecurring('missing Feb 28 https://github.com/jkbrzt/rrule/issues/21', + new RRule({ + freq: RRule.MONTHLY, + dtstart: new Date(2013, 0, 1), + count: 3, + bymonthday: [28] + }), + [ + new Date(2013, 0, 28), + new Date(2013, 1, 28), + new Date(2013, 2, 28) + ] +) + +// ============================================================================= + // The original `dateutil.rrule` test suite converted from Py to JS. + // ============================================================================= testRecurring('testBefore', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'before', - args: [parse("19970905T090000")] - }, - datetime(1997, 9, 4, 9, 0) -); + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'before', + args: [parse('19970905T090000')] + }, + datetime(1997, 9, 4, 9, 0) +) testRecurring('testBeforeInc', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'before', - args: [parse("19970905T090000"), true] - }, - datetime(1997, 9, 5, 9, 0) -); + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'before', + args: [parse('19970905T090000'), true] + }, + datetime(1997, 9, 5, 9, 0) +) testRecurring('testAfter', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'after', - args: [parse("19970904T090000")] - }, - datetime(1997, 9, 5, 9, 0) -); - + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'after', + args: [parse('19970904T090000')] + }, + datetime(1997, 9, 5, 9, 0) +) testRecurring('testAfterInc', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'after', - args: [parse("19970904T090000"), true] - }, - datetime(1997, 9, 4, 9, 0) -); + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'after', + args: [parse('19970904T090000'), true] + }, + datetime(1997, 9, 4, 9, 0) +) testRecurring('testBetween', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'between', - args: [parse("19970902T090000"), parse("19970906T090000")] - }, - [ - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 5, 9, 0) - ] -); + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'between', + args: [parse('19970902T090000'), parse('19970906T090000')] + }, + [ + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 5, 9, 0) + ] +) testRecurring('testBetweenInc', - { - rrule: new RRule({freq: RRule.DAILY, dtstart: parse("19970902T090000")}), - method: 'between', - args: [parse("19970902T090000"), parse("19970906T090000"), true] - }, - [ - datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 5, 9, 0), - datetime(1997, 9, 6, 9, 0) - ] -); - - - -testRecurring('testYearly', new RRule({freq: RRule.YEARLY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]); - -testRecurring('testYearlyInterval', new RRule({freq: RRule.YEARLY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0), - datetime(2001, 9, 2, 9, 0)]); - -testRecurring('testYearlyIntervalLarge', new RRule({freq: RRule.YEARLY, - count:3, - interval:100, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(2097, 9, 2, 9, 0), - datetime(2197, 9, 2, 9, 0)]); - -testRecurring('testYearlyByMonth', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 2, 9, 0), - datetime(1998, 3, 2, 9, 0), - datetime(1999, 1, 2, 9, 0)]); - -testRecurring('testYearlyByMonthDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 9, 0), - datetime(1997, 10, 1, 9, 0), - datetime(1997, 10, 3, 9, 0)]); - -testRecurring('testYearlyByMonthAndMonthDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 9, 0), - datetime(1998, 1, 7, 9, 0), - datetime(1998, 3, 5, 9, 0)]); - -testRecurring('testYearlyByWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); - -testRecurring('testYearlyByNWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 25, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 12, 31, 9, 0)]); - -testRecurring('testYearlyByNWeekDayLarge', new RRule({freq: RRule.YEARLY, - count:3, - byweekday:[RRule.TU.nth(3), RRule.TH.nth(-3)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 11, 9, 0), - datetime(1998, 1, 20, 9, 0), - datetime(1998, 12, 17, 9, 0)]); - -testRecurring('testYearlyByMonthAndWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testYearlyByMonthAndNWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 29, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testYearlyByMonthAndNWeekDayLarge', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(3), RRule.TH.nth(-3)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 15, 9, 0), - datetime(1998, 1, 20, 9, 0), - datetime(1998, 3, 12, 9, 0)]); - -testRecurring('testYearlyByMonthDayAndWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 2, 3, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testYearlyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 3, 3, 9, 0), - datetime(2001, 3, 1, 9, 0)]); - -testRecurring('testYearlyByYearDay', new RRule({freq: RRule.YEARLY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testYearlyByYearDayNeg', new RRule({freq: RRule.YEARLY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testYearlyByMonthAndYearDay', new RRule({freq: RRule.YEARLY, - count:4, - bymonth:[4, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 4, 10, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testYearlyByMonthAndYearDayNeg', new RRule({freq: RRule.YEARLY, - count:4, - bymonth:[4, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 4, 10, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testYearlyByWeekNo', new RRule({freq: RRule.YEARLY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 9, 0), - datetime(1998, 5, 12, 9, 0), - datetime(1998, 5, 13, 9, 0)]); + { + rrule: new RRule({ + freq: RRule.DAILY, + dtstart: parse('19970902T090000') + }), + method: 'between', + args: [parse('19970902T090000'), parse('19970906T090000'), true] + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 5, 9, 0), + datetime(1997, 9, 6, 9, 0) + ] +) + +testRecurring('testYearly', + new RRule({ + freq: RRule.YEARLY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1998, 9, 2, 9, 0), + datetime(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testYearlyInterval', + new RRule({freq: RRule.YEARLY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1999, 9, 2, 9, 0), + datetime(2001, 9, 2, 9, 0) + ] +) + +testRecurring('testYearlyIntervalLarge', + new RRule({freq: RRule.YEARLY, + count: 3, + interval: 100, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(2097, 9, 2, 9, 0), + datetime(2197, 9, 2, 9, 0) + ] +) + +testRecurring('testYearlyByMonth', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 2, 9, 0), + datetime(1998, 3, 2, 9, 0), + datetime(1999, 1, 2, 9, 0) + ] +) + +testRecurring('testYearlyByMonthDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 9, 0), + datetime(1997, 10, 1, 9, 0), + datetime(1997, 10, 3, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndMonthDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 9, 0), + datetime(1998, 1, 7, 9, 0), + datetime(1998, 3, 5, 9, 0) + ] +) + +testRecurring('testYearlyByWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testYearlyByNWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 25, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 12, 31, 9, 0) + ] +) + +testRecurring('testYearlyByNWeekDayLarge', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekday: [RRule.TU.nth(3), RRule.TH.nth(-3)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 11, 9, 0), + datetime(1998, 1, 20, 9, 0), + datetime(1998, 12, 17, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndNWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 29, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndNWeekDayLarge', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(3), RRule.TH.nth(-3)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 15, 9, 0), + datetime(1998, 1, 20, 9, 0), + datetime(1998, 3, 12, 9, 0) + ] +) + +testRecurring('testYearlyByMonthDayAndWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 2, 3, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 3, 3, 9, 0), + datetime(2001, 3, 1, 9, 0) + ] +) + +testRecurring('testYearlyByYearDay', + new RRule({freq: RRule.YEARLY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testYearlyByYearDayNeg', + new RRule({freq: RRule.YEARLY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndYearDay', + new RRule({freq: RRule.YEARLY, + count: 4, + bymonth: [4, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 4, 10, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testYearlyByMonthAndYearDayNeg', + new RRule({freq: RRule.YEARLY, + count: 4, + bymonth: [4, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 4, 10, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testYearlyByWeekNo', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 9, 0), + datetime(1998, 5, 12, 9, 0), + datetime(1998, 5, 13, 9, 0) + ] +) testRecurring('testYearlyByWeekNoAndWeekDay', - // That's a nice one. The first days of week number one - // may be in the last year. - new RRule({freq: RRule.YEARLY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 9, 0), - datetime(1999, 1, 4, 9, 0), - datetime(2000, 1, 3, 9, 0)]); + // That's a nice one. The first days of week number one + // may be in the last year. + new RRule({freq: RRule.YEARLY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 9, 0), + datetime(1999, 1, 4, 9, 0), + datetime(2000, 1, 3, 9, 0) + ] +) testRecurring('testYearlyByWeekNoAndWeekDayLarge', - // Another nice test. The last days of week number 52/53 - // may be in the next year. - new RRule({freq: RRule.YEARLY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1998, 12, 27, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testYearlyByWeekNoAndWeekDayLast', new RRule({freq: RRule.YEARLY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1999, 1, 3, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testYearlyByEaster', new RRule({ count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 9, 0), - datetime(1999, 4, 4, 9, 0), - datetime(2000, 4, 23, 9, 0)]) - -testRecurring('testYearlyByEasterPos', new RRule({freq: RRule.YEARLY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 9, 0), - datetime(1999, 4, 5, 9, 0), - datetime(2000, 4, 24, 9, 0)]) - -testRecurring('testYearlyByEasterNeg', new RRule({freq: RRule.YEARLY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 9, 0), - datetime(1999, 4, 3, 9, 0), - datetime(2000, 4, 22, 9, 0)]) - -testRecurring('testYearlyByWeekNoAndWeekDay53', new RRule({freq: RRule.YEARLY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 9, 0), - datetime(2004, 12, 27, 9, 0), - datetime(2009, 12, 28, 9, 0)]); -/* duplication -assertRecurring('testYearlyByWeekNoAndWeekDay53', new RRule({freq: RRule.YEARLY, - count: 3, - byweekno: 53, - byweekday: RRule.MO, - dtstart: parse("19970902T090000")}), - [datetime(1998, 12, 28, 9, 0), - datetime(2004, 12, 27, 9, 0), - datetime(2009, 12, 28, 9, 0)]) - */ -testRecurring('testYearlyByHour', new RRule({freq: RRule.YEARLY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1998, 9, 2, 6, 0), - datetime(1998, 9, 2, 18, 0)]); - -testRecurring('testYearlyByMinute', new RRule({freq: RRule.YEARLY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1998, 9, 2, 9, 6)]); - -testRecurring('testYearlyBySecond', new RRule({freq: RRule.YEARLY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1998, 9, 2, 9, 0, 6)]); - -testRecurring('testYearlyByHourAndMinute', new RRule({freq: RRule.YEARLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1998, 9, 2, 6, 6)]); - -testRecurring('testYearlyByHourAndSecond', new RRule({freq: RRule.YEARLY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1998, 9, 2, 6, 0, 6)]); - -testRecurring('testYearlyByMinuteAndSecond', new RRule({freq: RRule.YEARLY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testYearlyByHourAndMinuteAndSecond', new RRule({freq: RRule.YEARLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testYearlyBySetPos', new RRule({freq: RRule.YEARLY, - count:3, - bymonthday:15, - byhour:[6, 18], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 11, 15, 18, 0), - datetime(1998, 2, 15, 6, 0), - datetime(1998, 11, 15, 18, 0)]); - -testRecurring('testMonthly', new RRule({freq: RRule.MONTHLY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 10, 2, 9, 0), - datetime(1997, 11, 2, 9, 0)]); - -testRecurring('testMonthlyInterval', new RRule({freq: RRule.MONTHLY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 11, 2, 9, 0), - datetime(1998, 1, 2, 9, 0)]); - -testRecurring('testMonthlyIntervalLarge', new RRule({freq: RRule.MONTHLY, - count:3, - interval:18, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1999, 3, 2, 9, 0), - datetime(2000, 9, 2, 9, 0)]); - -testRecurring('testMonthlyByMonth', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 2, 9, 0), - datetime(1998, 3, 2, 9, 0), - datetime(1999, 1, 2, 9, 0)]); - - -testRecurring('testMonthlyByMonthDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 9, 0), - datetime(1997, 10, 1, 9, 0), - datetime(1997, 10, 3, 9, 0)]); - -testRecurring('testMonthlyByMonthAndMonthDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 9, 0), - datetime(1998, 1, 7, 9, 0), - datetime(1998, 3, 5, 9, 0)]); - -testRecurring('testMonthlyByWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); - -testRecurring('testMonthlyByNWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 25, 9, 0), - datetime(1997, 10, 7, 9, 0)]); - -testRecurring('testMonthlyByNWeekDayLarge', new RRule({freq: RRule.MONTHLY, - count:3, - byweekday:[RRule.TU.nth(3), RRule.TH.nth(-3)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 11, 9, 0), - datetime(1997, 9, 16, 9, 0), - datetime(1997, 10, 16, 9, 0)]); - -testRecurring('testMonthlyByMonthAndWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testMonthlyByMonthAndNWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 29, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testMonthlyByMonthAndNWeekDayLarge', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(3), RRule.TH.nth(-3)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 15, 9, 0), - datetime(1998, 1, 20, 9, 0), - datetime(1998, 3, 12, 9, 0)]); - -testRecurring('testMonthlyByMonthDayAndWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 2, 3, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testMonthlyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.MONTHLY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 3, 3, 9, 0), - datetime(2001, 3, 1, 9, 0)]); - -testRecurring('testMonthlyByYearDay', new RRule({freq: RRule.MONTHLY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testMonthlyByYearDayNeg', new RRule({freq: RRule.MONTHLY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testMonthlyByMonthAndYearDay', new RRule({freq: RRule.MONTHLY, - count:4, - bymonth:[4, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 4, 10, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testMonthlyByMonthAndYearDayNeg', new RRule({freq: RRule.MONTHLY, - count:4, - bymonth:[4, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 4, 10, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - - -testRecurring('testMonthlyByWeekNo', new RRule({freq: RRule.MONTHLY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 9, 0), - datetime(1998, 5, 12, 9, 0), - datetime(1998, 5, 13, 9, 0)]); + // Another nice test. The last days of week number 52/53 + // may be in the next year. + new RRule({freq: RRule.YEARLY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1998, 12, 27, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testYearlyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1999, 1, 3, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testYearlyByEaster', + new RRule({ count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 9, 0), + datetime(1999, 4, 4, 9, 0), + datetime(2000, 4, 23, 9, 0) + ] +) + +testRecurring('testYearlyByEasterPos', + new RRule({freq: RRule.YEARLY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 9, 0), + datetime(1999, 4, 5, 9, 0), + datetime(2000, 4, 24, 9, 0) + ] +) + +testRecurring('testYearlyByEasterNeg', + new RRule({freq: RRule.YEARLY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 9, 0), + datetime(1999, 4, 3, 9, 0), + datetime(2000, 4, 22, 9, 0) + ] +) + +testRecurring('testYearlyByWeekNoAndWeekDay53', + new RRule({freq: RRule.YEARLY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 9, 0), + datetime(2004, 12, 27, 9, 0), + datetime(2009, 12, 28, 9, 0) + ] +) + +testRecurring('testYearlyByHour', + new RRule({freq: RRule.YEARLY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1998, 9, 2, 6, 0), + datetime(1998, 9, 2, 18, 0) + ] +) + +testRecurring('testYearlyByMinute', + new RRule({freq: RRule.YEARLY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1998, 9, 2, 9, 6) + ] +) + +testRecurring('testYearlyBySecond', + new RRule({freq: RRule.YEARLY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1998, 9, 2, 9, 0, 6) + ] +) + +testRecurring('testYearlyByHourAndMinute', + new RRule({freq: RRule.YEARLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1998, 9, 2, 6, 6) + ] +) + +testRecurring('testYearlyByHourAndSecond', + new RRule({freq: RRule.YEARLY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1998, 9, 2, 6, 0, 6) + ] +) + +testRecurring('testYearlyByMinuteAndSecond', + new RRule({freq: RRule.YEARLY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testYearlyByHourAndMinuteAndSecond', + new RRule({freq: RRule.YEARLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testYearlyBySetPos', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonthday: 15, + byhour: [6, 18], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 11, 15, 18, 0), + datetime(1998, 2, 15, 6, 0), + datetime(1998, 11, 15, 18, 0) + ] +) + +testRecurring('testMonthly', + new RRule({freq: RRule.MONTHLY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 10, 2, 9, 0), + datetime(1997, 11, 2, 9, 0) + ] +) + +testRecurring('testMonthlyInterval', + new RRule({freq: RRule.MONTHLY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 11, 2, 9, 0), + datetime(1998, 1, 2, 9, 0) + ] +) + +testRecurring('testMonthlyIntervalLarge', + new RRule({freq: RRule.MONTHLY, + count: 3, + interval: 18, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1999, 3, 2, 9, 0), + datetime(2000, 9, 2, 9, 0) + ] +) + +testRecurring('testMonthlyByMonth', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 2, 9, 0), + datetime(1998, 3, 2, 9, 0), + datetime(1999, 1, 2, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 9, 0), + datetime(1997, 10, 1, 9, 0), + datetime(1997, 10, 3, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndMonthDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 9, 0), + datetime(1998, 1, 7, 9, 0), + datetime(1998, 3, 5, 9, 0) + ] +) + +testRecurring('testMonthlyByWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testMonthlyByNWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 25, 9, 0), + datetime(1997, 10, 7, 9, 0) + ] +) + +testRecurring('testMonthlyByNWeekDayLarge', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekday: [RRule.TU.nth(3), RRule.TH.nth(-3)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 11, 9, 0), + datetime(1997, 9, 16, 9, 0), + datetime(1997, 10, 16, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndNWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 29, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndNWeekDayLarge', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(3), RRule.TH.nth(-3)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 15, 9, 0), + datetime(1998, 1, 20, 9, 0), + datetime(1998, 3, 12, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthDayAndWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 2, 3, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 3, 3, 9, 0), + datetime(2001, 3, 1, 9, 0) + ] +) + +testRecurring('testMonthlyByYearDay', + new RRule({freq: RRule.MONTHLY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testMonthlyByYearDayNeg', + new RRule({freq: RRule.MONTHLY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndYearDay', + new RRule({freq: RRule.MONTHLY, + count: 4, + bymonth: [4, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 4, 10, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testMonthlyByMonthAndYearDayNeg', + new RRule({freq: RRule.MONTHLY, + count: 4, + bymonth: [4, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 4, 10, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testMonthlyByWeekNo', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 9, 0), + datetime(1998, 5, 12, 9, 0), + datetime(1998, 5, 13, 9, 0) + ] +) testRecurring('testMonthlyByWeekNoAndWeekDay', - // That's a nice one. The first days of week number one - // may be in the last year. - new RRule({freq: RRule.MONTHLY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 9, 0), - datetime(1999, 1, 4, 9, 0), - datetime(2000, 1, 3, 9, 0)]); + // That's a nice one. The first days of week number one + // may be in the last year. + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 9, 0), + datetime(1999, 1, 4, 9, 0), + datetime(2000, 1, 3, 9, 0) + ] +) testRecurring('testMonthlyByWeekNoAndWeekDayLarge', - // Another nice test. The last days of week number 52/53 - // may be in the next year. - new RRule({freq: RRule.MONTHLY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1998, 12, 27, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testMonthlyByWeekNoAndWeekDayLast', new RRule({freq: RRule.MONTHLY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1999, 1, 3, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testMonthlyByWeekNoAndWeekDay53', new RRule({freq: RRule.MONTHLY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 9, 0), - datetime(2004, 12, 27, 9, 0), - datetime(2009, 12, 28, 9, 0)]); -/* -assertRecurring('testMonthlyByEaster', new RRule({freq: RRule.MONTHLY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 9, 0), - datetime(1999, 4, 4, 9, 0), - datetime(2000, 4, 23, 9, 0)]) - -assertRecurring('testMonthlyByEasterPos', new RRule({freq: RRule.MONTHLY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 9, 0), - datetime(1999, 4, 5, 9, 0), - datetime(2000, 4, 24, 9, 0)]) - -assertRecurring('testMonthlyByEasterNeg', new RRule({freq: RRule.MONTHLY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 9, 0), - datetime(1999, 4, 3, 9, 0), - datetime(2000, 4, 22, 9, 0)]) -*/ -testRecurring('testMonthlyByHour', new RRule({freq: RRule.MONTHLY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 10, 2, 6, 0), - datetime(1997, 10, 2, 18, 0)]); - -testRecurring('testMonthlyByMinute', new RRule({freq: RRule.MONTHLY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1997, 10, 2, 9, 6)]); - -testRecurring('testMonthlyBySecond', new RRule({freq: RRule.MONTHLY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 10, 2, 9, 0, 6)]); - -testRecurring('testMonthlyByHourAndMinute', new RRule({freq: RRule.MONTHLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1997, 10, 2, 6, 6)]); - -testRecurring('testMonthlyByHourAndSecond', new RRule({freq: RRule.MONTHLY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 10, 2, 6, 0, 6)]); - -testRecurring('testMonthlyByMinuteAndSecond', new RRule({freq: RRule.MONTHLY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testMonthlyByHourAndMinuteAndSecond', new RRule({freq: RRule.MONTHLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testMonthlyBySetPos', new RRule({freq: RRule.MONTHLY, - count:3, - bymonthday:[13, 17], - byhour:[6, 18], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 13, 18, 0), - datetime(1997, 9, 17, 6, 0), - datetime(1997, 10, 13, 18, 0)]); - -testRecurring('testMonthlyNegByMonthDayJanFebForNonLeapYear', new RRule({freq: RRule.MONTHLY, + // Another nice test. The last days of week number 52/53 + // may be in the next year. + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1998, 12, 27, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testMonthlyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1999, 1, 3, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testMonthlyByWeekNoAndWeekDay53', + new RRule({freq: RRule.MONTHLY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 9, 0), + datetime(2004, 12, 27, 9, 0), + datetime(2009, 12, 28, 9, 0) + ] +) + +testRecurring('testMonthlyByEaster', + new RRule({freq: RRule.MONTHLY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 9, 0), + datetime(1999, 4, 4, 9, 0), + datetime(2000, 4, 23, 9, 0) + ] +) + +testRecurring('testMonthlyByEasterPos', + new RRule({freq: RRule.MONTHLY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 9, 0), + datetime(1999, 4, 5, 9, 0), + datetime(2000, 4, 24, 9, 0) + ] +) + +testRecurring('testMonthlyByEasterNeg', + new RRule({freq: RRule.MONTHLY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 9, 0), + datetime(1999, 4, 3, 9, 0), + datetime(2000, 4, 22, 9, 0) + ] +) + +testRecurring('testMonthlyByHour', + new RRule({freq: RRule.MONTHLY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 10, 2, 6, 0), + datetime(1997, 10, 2, 18, 0) + ] +) + +testRecurring('testMonthlyByMinute', + new RRule({freq: RRule.MONTHLY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1997, 10, 2, 9, 6) + ] +) + +testRecurring('testMonthlyBySecond', + new RRule({freq: RRule.MONTHLY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 10, 2, 9, 0, 6) + ] +) + +testRecurring('testMonthlyByHourAndMinute', + new RRule({freq: RRule.MONTHLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1997, 10, 2, 6, 6) + ] +) + +testRecurring('testMonthlyByHourAndSecond', + new RRule({freq: RRule.MONTHLY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 10, 2, 6, 0, 6) + ] +) + +testRecurring('testMonthlyByMinuteAndSecond', + new RRule({freq: RRule.MONTHLY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testMonthlyByHourAndMinuteAndSecond', + new RRule({freq: RRule.MONTHLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testMonthlyBySetPos', + new RRule({freq: RRule.MONTHLY, + count: 3, + bymonthday: [13, 17], + byhour: [6, 18], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 13, 18, 0), + datetime(1997, 9, 17, 6, 0), + datetime(1997, 10, 13, 18, 0) + ] +) + +testRecurring('testMonthlyNegByMonthDayJanFebForNonLeapYear', + new RRule({freq: RRule.MONTHLY, count: 4, bymonthday: -1, - dtstart: parse("20131201T0900000")}), - [datetime(2013, 12, 31, 9, 0), - datetime(2014, 1, 31, 9, 0), - datetime(2014, 2, 28, 9, 0), - datetime(2014, 3, 31, 9, 0)]); - -testRecurring('testMonthlyNegByMonthDayJanFebForLeapYear', new RRule({freq: RRule.MONTHLY, + dtstart: parse('20131201T0900000') + }), + [ + datetime(2013, 12, 31, 9, 0), + datetime(2014, 1, 31, 9, 0), + datetime(2014, 2, 28, 9, 0), + datetime(2014, 3, 31, 9, 0) + ] +) + +testRecurring('testMonthlyNegByMonthDayJanFebForLeapYear', + new RRule({freq: RRule.MONTHLY, count: 4, bymonthday: -1, - dtstart: parse("20151201T0900000")}), - [datetime(2015, 12, 31, 9, 0), - datetime(2016, 1, 31, 9, 0), - datetime(2016, 2, 29, 9, 0), - datetime(2016, 3, 31, 9, 0)]); - -testRecurring('testWeekly', new RRule({freq: RRule.WEEKLY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]); - -testRecurring('testWeeklyInterval', new RRule({freq: RRule.WEEKLY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 16, 9, 0), - datetime(1997, 9, 30, 9, 0)]); - -testRecurring('testWeeklyIntervalLarge', new RRule({freq: RRule.WEEKLY, - count:3, - interval:20, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 1, 20, 9, 0), - datetime(1998, 6, 9, 9, 0)]); - -testRecurring('testWeeklyByMonth', new RRule({freq: RRule.WEEKLY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 13, 9, 0), - datetime(1998, 1, 20, 9, 0)]); - -testRecurring('testWeeklyByMonthDay', new RRule({freq: RRule.WEEKLY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 9, 0), - datetime(1997, 10, 1, 9, 0), - datetime(1997, 10, 3, 9, 0)]); - -testRecurring('testWeeklyByMonthAndMonthDay', new RRule({freq: RRule.WEEKLY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 9, 0), - datetime(1998, 1, 7, 9, 0), - datetime(1998, 3, 5, 9, 0)]); - -testRecurring('testWeeklyByWeekDay', new RRule({freq: RRule.WEEKLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); - -testRecurring('testWeeklyByNWeekDay', new RRule({freq: RRule.WEEKLY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); + dtstart: parse('20151201T0900000') + }), + [ + datetime(2015, 12, 31, 9, 0), + datetime(2016, 1, 31, 9, 0), + datetime(2016, 2, 29, 9, 0), + datetime(2016, 3, 31, 9, 0) + ] +) + +testRecurring('testWeekly', + new RRule({freq: RRule.WEEKLY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 9, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testWeeklyInterval', + new RRule({freq: RRule.WEEKLY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 16, 9, 0), + datetime(1997, 9, 30, 9, 0) + ] +) + +testRecurring('testWeeklyIntervalLarge', + new RRule({freq: RRule.WEEKLY, + count: 3, + interval: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1998, 1, 20, 9, 0), + datetime(1998, 6, 9, 9, 0) + ] +) + +testRecurring('testWeeklyByMonth', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 13, 9, 0), + datetime(1998, 1, 20, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 9, 0), + datetime(1997, 10, 1, 9, 0), + datetime(1997, 10, 3, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthAndMonthDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 9, 0), + datetime(1998, 1, 7, 9, 0), + datetime(1998, 3, 5, 9, 0) + ] +) + +testRecurring('testWeeklyByWeekDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testWeeklyByNWeekDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) testRecurring('testWeeklyByMonthAndWeekDay', - // This test is interesting, because it crosses the year - // boundary in a weekly period to find day '1' as a - // valid recurrence. - new RRule({freq: RRule.WEEKLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testWeeklyByMonthAndNWeekDay', new RRule({freq: RRule.WEEKLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testWeeklyByMonthDayAndWeekDay', new RRule({freq: RRule.WEEKLY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 2, 3, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testWeeklyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.WEEKLY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 3, 3, 9, 0), - datetime(2001, 3, 1, 9, 0)]); - -testRecurring('testWeeklyByYearDay', new RRule({freq: RRule.WEEKLY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testWeeklyByYearDayNeg', new RRule({freq: RRule.WEEKLY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testWeeklyByMonthAndYearDay', new RRule({freq: RRule.WEEKLY, - count:4, - bymonth:[1, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 1, 1, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testWeeklyByMonthAndYearDayNeg', new RRule({freq: RRule.WEEKLY, - count:4, - bymonth:[1, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 1, 1, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testWeeklyByWeekNo', new RRule({freq: RRule.WEEKLY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 9, 0), - datetime(1998, 5, 12, 9, 0), - datetime(1998, 5, 13, 9, 0)]); + // This test is interesting, because it crosses the year + // boundary in a weekly period to find day '1' as a + // valid recurrence. + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthAndNWeekDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthDayAndWeekDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 2, 3, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.WEEKLY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 3, 3, 9, 0), + datetime(2001, 3, 1, 9, 0) + ] +) + +testRecurring('testWeeklyByYearDay', + new RRule({freq: RRule.WEEKLY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testWeeklyByYearDayNeg', + new RRule({freq: RRule.WEEKLY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthAndYearDay', + new RRule({freq: RRule.WEEKLY, + count: 4, + bymonth: [1, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 1, 1, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testWeeklyByMonthAndYearDayNeg', + new RRule({freq: RRule.WEEKLY, + count: 4, + bymonth: [1, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 1, 1, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testWeeklyByWeekNo', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 9, 0), + datetime(1998, 5, 12, 9, 0), + datetime(1998, 5, 13, 9, 0) + ] +) testRecurring('testWeeklyByWeekNoAndWeekDay', - // That's a nice one. The first days of week number one - // may be in the last year. - new RRule({freq: RRule.WEEKLY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 9, 0), - datetime(1999, 1, 4, 9, 0), - datetime(2000, 1, 3, 9, 0)]); + // That's a nice one. The first days of week number one + // may be in the last year. + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 9, 0), + datetime(1999, 1, 4, 9, 0), + datetime(2000, 1, 3, 9, 0) + ] +) testRecurring('testWeeklyByWeekNoAndWeekDayLarge', - // Another nice test. The last days of week number 52/53 - // may be in the next year. - new RRule({freq: RRule.WEEKLY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1998, 12, 27, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testWeeklyByWeekNoAndWeekDayLast', new RRule({freq: RRule.WEEKLY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1999, 1, 3, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testWeeklyByWeekNoAndWeekDay53', new RRule({freq: RRule.WEEKLY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 9, 0), - datetime(2004, 12, 27, 9, 0), - datetime(2009, 12, 28, 9, 0)]); -/* -assertRecurring('testWeeklyByEaster', new RRule({freq: RRule.WEEKLY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 9, 0), - datetime(1999, 4, 4, 9, 0), - datetime(2000, 4, 23, 9, 0)]) - -assertRecurring('testWeeklyByEasterPos', new RRule({freq: RRule.WEEKLY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 9, 0), - datetime(1999, 4, 5, 9, 0), - datetime(2000, 4, 24, 9, 0)]) - -assertRecurring('testWeeklyByEasterNeg', new RRule({freq: RRule.WEEKLY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 9, 0), - datetime(1999, 4, 3, 9, 0), - datetime(2000, 4, 22, 9, 0)]) - */ -testRecurring('testWeeklyByHour', new RRule({freq: RRule.WEEKLY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 9, 9, 6, 0), - datetime(1997, 9, 9, 18, 0)]); - -testRecurring('testWeeklyByMinute', new RRule({freq: RRule.WEEKLY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1997, 9, 9, 9, 6)]); - -testRecurring('testWeeklyBySecond', new RRule({freq: RRule.WEEKLY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 9, 9, 9, 0, 6)]); - -testRecurring('testWeeklyByHourAndMinute', new RRule({freq: RRule.WEEKLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1997, 9, 9, 6, 6)]); - -testRecurring('testWeeklyByHourAndSecond', new RRule({freq: RRule.WEEKLY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 9, 9, 6, 0, 6)]); - -testRecurring('testWeeklyByMinuteAndSecond', new RRule({freq: RRule.WEEKLY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testWeeklyByHourAndMinuteAndSecond', new RRule({freq: RRule.WEEKLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testWeeklyBySetPos', new RRule({freq: RRule.WEEKLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - byhour:[6, 18], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 9, 4, 6, 0), - datetime(1997, 9, 9, 18, 0)]); - -testRecurring('testDaily', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]); - -testRecurring('testDailyInterval', new RRule({freq: RRule.DAILY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 6, 9, 0)]); - -testRecurring('testDailyIntervalLarge', new RRule({freq: RRule.DAILY, - count:3, - interval:92, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 12, 3, 9, 0), - datetime(1998, 3, 5, 9, 0)]); - -testRecurring('testDailyByMonth', new RRule({freq: RRule.DAILY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 2, 9, 0), - datetime(1998, 1, 3, 9, 0)]); - -testRecurring('testDailyByMonthDay', new RRule({freq: RRule.DAILY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 9, 0), - datetime(1997, 10, 1, 9, 0), - datetime(1997, 10, 3, 9, 0)]); - -testRecurring('testDailyByMonthAndMonthDay', new RRule({freq: RRule.DAILY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 9, 0), - datetime(1998, 1, 7, 9, 0), - datetime(1998, 3, 5, 9, 0)]); - -testRecurring('testDailyByWeekDay', new RRule({freq: RRule.DAILY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); - -testRecurring('testDailyByNWeekDay', new RRule({freq: RRule.DAILY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]); - -testRecurring('testDailyByMonthAndWeekDay', new RRule({freq: RRule.DAILY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testDailyByMonthAndNWeekDay', new RRule({freq: RRule.DAILY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 1, 8, 9, 0)]); - -testRecurring('testDailyByMonthDayAndWeekDay', new RRule({freq: RRule.DAILY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 2, 3, 9, 0), - datetime(1998, 3, 3, 9, 0)]); - -testRecurring('testDailyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.DAILY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 3, 3, 9, 0), - datetime(2001, 3, 1, 9, 0)]); - -testRecurring('testDailyByYearDay', new RRule({freq: RRule.DAILY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testDailyByYearDayNeg', new RRule({freq: RRule.DAILY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 9, 0), - datetime(1998, 1, 1, 9, 0), - datetime(1998, 4, 10, 9, 0), - datetime(1998, 7, 19, 9, 0)]); - -testRecurring('testDailyByMonthAndYearDay', new RRule({freq: RRule.DAILY, - count:4, - bymonth:[1, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 1, 1, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testDailyByMonthAndYearDayNeg', new RRule({freq: RRule.DAILY, - count:4, - bymonth:[1, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 9, 0), - datetime(1998, 7, 19, 9, 0), - datetime(1999, 1, 1, 9, 0), - datetime(1999, 7, 19, 9, 0)]); - -testRecurring('testDailyByWeekNo', new RRule({freq: RRule.DAILY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 9, 0), - datetime(1998, 5, 12, 9, 0), - datetime(1998, 5, 13, 9, 0)]); + // Another nice test. The last days of week number 52/53 + // may be in the next year. + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1998, 12, 27, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testWeeklyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1999, 1, 3, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testWeeklyByWeekNoAndWeekDay53', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 9, 0), + datetime(2004, 12, 27, 9, 0), + datetime(2009, 12, 28, 9, 0) + ] +) + +testRecurring('testWeeklyByEaster', + new RRule({freq: RRule.WEEKLY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 9, 0), + datetime(1999, 4, 4, 9, 0), + datetime(2000, 4, 23, 9, 0) + ] +) + +testRecurring('testWeeklyByEasterPos', + new RRule({freq: RRule.WEEKLY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 9, 0), + datetime(1999, 4, 5, 9, 0), + datetime(2000, 4, 24, 9, 0) + ] +) + +testRecurring('testWeeklyByEasterNeg', + new RRule({freq: RRule.WEEKLY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 9, 0), + datetime(1999, 4, 3, 9, 0), + datetime(2000, 4, 22, 9, 0) + ] +) + +testRecurring('testWeeklyByHour', + new RRule({freq: RRule.WEEKLY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 9, 9, 6, 0), + datetime(1997, 9, 9, 18, 0) + ] +) + +testRecurring('testWeeklyByMinute', + new RRule({freq: RRule.WEEKLY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1997, 9, 9, 9, 6) + ] +) + +testRecurring('testWeeklyBySecond', + new RRule({freq: RRule.WEEKLY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 9, 9, 9, 0, 6) + ] +) + +testRecurring('testWeeklyByHourAndMinute', + new RRule({freq: RRule.WEEKLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1997, 9, 9, 6, 6) + ] +) + +testRecurring('testWeeklyByHourAndSecond', + new RRule({freq: RRule.WEEKLY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 9, 9, 6, 0, 6) + ] +) + +testRecurring('testWeeklyByMinuteAndSecond', + new RRule({freq: RRule.WEEKLY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testWeeklyByHourAndMinuteAndSecond', + new RRule({freq: RRule.WEEKLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testWeeklyBySetPos', + new RRule({freq: RRule.WEEKLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + byhour: [6, 18], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 9, 4, 6, 0), + datetime(1997, 9, 9, 18, 0) + ] +) + +testRecurring('testDaily', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0) + ] +) + +testRecurring('testDailyInterval', + new RRule({freq: RRule.DAILY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 6, 9, 0) + ] +) + +testRecurring('testDailyIntervalLarge', + new RRule({freq: RRule.DAILY, + count: 3, + interval: 92, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 12, 3, 9, 0), + datetime(1998, 3, 5, 9, 0) + ] +) + +testRecurring('testDailyByMonth', + new RRule({freq: RRule.DAILY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 2, 9, 0), + datetime(1998, 1, 3, 9, 0) + ] +) + +testRecurring('testDailyByMonthDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 9, 0), + datetime(1997, 10, 1, 9, 0), + datetime(1997, 10, 3, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndMonthDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 9, 0), + datetime(1998, 1, 7, 9, 0), + datetime(1998, 3, 5, 9, 0) + ] +) + +testRecurring('testDailyByWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testDailyByNWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndNWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 1, 6, 9, 0), + datetime(1998, 1, 8, 9, 0) + ] +) + +testRecurring('testDailyByMonthDayAndWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 2, 3, 9, 0), + datetime(1998, 3, 3, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.DAILY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 3, 3, 9, 0), + datetime(2001, 3, 1, 9, 0) + ] +) + +testRecurring('testDailyByYearDay', + new RRule({freq: RRule.DAILY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testDailyByYearDayNeg', + new RRule({freq: RRule.DAILY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 9, 0), + datetime(1998, 1, 1, 9, 0), + datetime(1998, 4, 10, 9, 0), + datetime(1998, 7, 19, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndYearDay', + new RRule({freq: RRule.DAILY, + count: 4, + bymonth: [1, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 1, 1, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testDailyByMonthAndYearDayNeg', + new RRule({freq: RRule.DAILY, + count: 4, + bymonth: [1, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 9, 0), + datetime(1998, 7, 19, 9, 0), + datetime(1999, 1, 1, 9, 0), + datetime(1999, 7, 19, 9, 0) + ] +) + +testRecurring('testDailyByWeekNo', + new RRule({freq: RRule.DAILY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 9, 0), + datetime(1998, 5, 12, 9, 0), + datetime(1998, 5, 13, 9, 0) + ] +) testRecurring('testDailyByWeekNoAndWeekDay', - // That's a nice one. The first days of week number one - // may be in the last year. - new RRule({freq: RRule.DAILY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 9, 0), - datetime(1999, 1, 4, 9, 0), - datetime(2000, 1, 3, 9, 0)]); + // That's a nice one. The first days of week number one + // may be in the last year. + new RRule({freq: RRule.DAILY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 9, 0), + datetime(1999, 1, 4, 9, 0), + datetime(2000, 1, 3, 9, 0) + ] +) testRecurring('testDailyByWeekNoAndWeekDayLarge', - // Another nice test. The last days of week number 52/53 - // may be in the next year. - new RRule({freq: RRule.DAILY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1998, 12, 27, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testDailyByWeekNoAndWeekDayLast', new RRule({freq: RRule.DAILY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 9, 0), - datetime(1999, 1, 3, 9, 0), - datetime(2000, 1, 2, 9, 0)]); - -testRecurring('testDailyByWeekNoAndWeekDay53', new RRule({freq: RRule.DAILY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 9, 0), - datetime(2004, 12, 27, 9, 0), - datetime(2009, 12, 28, 9, 0)]); -/* -assertRecurring('testDailyByEaster', new RRule({freq: RRule.DAILY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 9, 0), - datetime(1999, 4, 4, 9, 0), - datetime(2000, 4, 23, 9, 0)]) - -assertRecurring('testDailyByEasterPos', new RRule({freq: RRule.DAILY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 9, 0), - datetime(1999, 4, 5, 9, 0), - datetime(2000, 4, 24, 9, 0)]) - -assertRecurring('testDailyByEasterNeg', new RRule({freq: RRule.DAILY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 9, 0), - datetime(1999, 4, 3, 9, 0), - datetime(2000, 4, 22, 9, 0)]) - */ -testRecurring('testDailyByHour', new RRule({freq: RRule.DAILY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 9, 3, 6, 0), - datetime(1997, 9, 3, 18, 0)]); - -testRecurring('testDailyByMinute', new RRule({freq: RRule.DAILY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1997, 9, 3, 9, 6)]); - -testRecurring('testDailyBySecond', new RRule({freq: RRule.DAILY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 9, 3, 9, 0, 6)]); - -testRecurring('testDailyByHourAndMinute', new RRule({freq: RRule.DAILY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1997, 9, 3, 6, 6)]); - -testRecurring('testDailyByHourAndSecond', new RRule({freq: RRule.DAILY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 9, 3, 6, 0, 6)]); - -testRecurring('testDailyByMinuteAndSecond', new RRule({freq: RRule.DAILY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testDailyByHourAndMinuteAndSecond', new RRule({freq: RRule.DAILY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testDailyBySetPos', new RRule({freq: RRule.DAILY, - count:3, - byhour:[6, 18], - byminute:[15, 45], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 15), - datetime(1997, 9, 3, 6, 45), - datetime(1997, 9, 3, 18, 15)]); - -testRecurring('testHourly', new RRule({freq: RRule.HOURLY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 10, 0), - datetime(1997, 9, 2, 11, 0)]); - -testRecurring('testHourlyInterval', new RRule({freq: RRule.HOURLY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 11, 0), - datetime(1997, 9, 2, 13, 0)]); - -testRecurring('testHourlyIntervalLarge', new RRule({freq: RRule.HOURLY, - count:3, - interval:769, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 10, 4, 10, 0), - datetime(1997, 11, 5, 11, 0)]); - -testRecurring('testHourlyByMonth', new RRule({freq: RRule.HOURLY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 1, 0), - datetime(1998, 1, 1, 2, 0)]); - -testRecurring('testHourlyByMonthDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 0, 0), - datetime(1997, 9, 3, 1, 0), - datetime(1997, 9, 3, 2, 0)]); - -testRecurring('testHourlyByMonthAndMonthDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 0, 0), - datetime(1998, 1, 5, 1, 0), - datetime(1998, 1, 5, 2, 0)]); - -testRecurring('testHourlyByWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 10, 0), - datetime(1997, 9, 2, 11, 0)]); - -testRecurring('testHourlyByNWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 10, 0), - datetime(1997, 9, 2, 11, 0)]); - -testRecurring('testHourlyByMonthAndWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 1, 0), - datetime(1998, 1, 1, 2, 0)]); - -testRecurring('testHourlyByMonthAndNWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 1, 0), - datetime(1998, 1, 1, 2, 0)]); - -testRecurring('testHourlyByMonthDayAndWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 1, 0), - datetime(1998, 1, 1, 2, 0)]); - -testRecurring('testHourlyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 1, 0), - datetime(1998, 1, 1, 2, 0)]); - -testRecurring('testHourlyByYearDay', new RRule({freq: RRule.HOURLY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0), - datetime(1997, 12, 31, 1, 0), - datetime(1997, 12, 31, 2, 0), - datetime(1997, 12, 31, 3, 0)]); - -testRecurring('testHourlyByYearDayNeg', new RRule({freq: RRule.HOURLY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0), - datetime(1997, 12, 31, 1, 0), - datetime(1997, 12, 31, 2, 0), - datetime(1997, 12, 31, 3, 0)]); - -testRecurring('testHourlyByMonthAndYearDay', new RRule({freq: RRule.HOURLY, - count:4, - bymonth:[4, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0), - datetime(1998, 4, 10, 1, 0), - datetime(1998, 4, 10, 2, 0), - datetime(1998, 4, 10, 3, 0)]); - -testRecurring('testHourlyByMonthAndYearDayNeg', new RRule({freq: RRule.HOURLY, - count:4, - bymonth:[4, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0), - datetime(1998, 4, 10, 1, 0), - datetime(1998, 4, 10, 2, 0), - datetime(1998, 4, 10, 3, 0)]); - -testRecurring('testHourlyByWeekNo', new RRule({freq: RRule.HOURLY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 0, 0), - datetime(1998, 5, 11, 1, 0), - datetime(1998, 5, 11, 2, 0)]); - -testRecurring('testHourlyByWeekNoAndWeekDay', new RRule({freq: RRule.HOURLY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 0, 0), - datetime(1997, 12, 29, 1, 0), - datetime(1997, 12, 29, 2, 0)]); - -testRecurring('testHourlyByWeekNoAndWeekDayLarge', new RRule({freq: RRule.HOURLY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0), - datetime(1997, 12, 28, 1, 0), - datetime(1997, 12, 28, 2, 0)]); - -testRecurring('testHourlyByWeekNoAndWeekDayLast', new RRule({freq: RRule.HOURLY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0), - datetime(1997, 12, 28, 1, 0), - datetime(1997, 12, 28, 2, 0)]); - -testRecurring('testHourlyByWeekNoAndWeekDay53', new RRule({freq: RRule.HOURLY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 0, 0), - datetime(1998, 12, 28, 1, 0), - datetime(1998, 12, 28, 2, 0)]); -/* -assertRecurring('testHourlyByEaster', new RRule({freq: RRule.HOURLY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 0, 0), - datetime(1998, 4, 12, 1, 0), - datetime(1998, 4, 12, 2, 0)]) - -assertRecurring('testHourlyByEasterPos', new RRule({freq: RRule.HOURLY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 0, 0), - datetime(1998, 4, 13, 1, 0), - datetime(1998, 4, 13, 2, 0)]) - -assertRecurring('testHourlyByEasterNeg', new RRule({freq: RRule.HOURLY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 0, 0), - datetime(1998, 4, 11, 1, 0), - datetime(1998, 4, 11, 2, 0)]) - */ -testRecurring('testHourlyByHour', new RRule({freq: RRule.HOURLY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 9, 3, 6, 0), - datetime(1997, 9, 3, 18, 0)]); - -testRecurring('testHourlyByMinute', new RRule({freq: RRule.HOURLY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1997, 9, 2, 10, 6)]); - -testRecurring('testHourlyBySecond', new RRule({freq: RRule.HOURLY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 9, 2, 10, 0, 6)]); - -testRecurring('testHourlyByHourAndMinute', new RRule({freq: RRule.HOURLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1997, 9, 3, 6, 6)]); - -testRecurring('testHourlyByHourAndSecond', new RRule({freq: RRule.HOURLY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 9, 3, 6, 0, 6)]); - -testRecurring('testHourlyByMinuteAndSecond', new RRule({freq: RRule.HOURLY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testHourlyByHourAndMinuteAndSecond', new RRule({freq: RRule.HOURLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testHourlyBySetPos', new RRule({freq: RRule.HOURLY, - count:3, - byminute:[15, 45], - bysecond:[15, 45], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 15, 45), - datetime(1997, 9, 2, 9, 45, 15), - datetime(1997, 9, 2, 10, 15, 45)]); - -testRecurring('testMinutely', new RRule({freq: RRule.MINUTELY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 9, 1), - datetime(1997, 9, 2, 9, 2)]); - -testRecurring('testMinutelyInterval', new RRule({freq: RRule.MINUTELY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 9, 2), - datetime(1997, 9, 2, 9, 4)]); - -testRecurring('testMinutelyIntervalLarge', new RRule({freq: RRule.MINUTELY, - count:3, - interval:1501, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 10, 1), - datetime(1997, 9, 4, 11, 2)]); - -testRecurring('testMinutelyByMonth', new RRule({freq: RRule.MINUTELY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 0, 1), - datetime(1998, 1, 1, 0, 2)]); - -testRecurring('testMinutelyByMonthDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 0, 0), - datetime(1997, 9, 3, 0, 1), - datetime(1997, 9, 3, 0, 2)]); - -testRecurring('testMinutelyByMonthAndMonthDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 0, 0), - datetime(1998, 1, 5, 0, 1), - datetime(1998, 1, 5, 0, 2)]); - -testRecurring('testMinutelyByWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 9, 1), - datetime(1997, 9, 2, 9, 2)]); - -testRecurring('testMinutelyByNWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 2, 9, 1), - datetime(1997, 9, 2, 9, 2)]); - -testRecurring('testMinutelyByMonthAndWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 0, 1), - datetime(1998, 1, 1, 0, 2)]); - -testRecurring('testMinutelyByMonthAndNWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 0, 1), - datetime(1998, 1, 1, 0, 2)]); - -testRecurring('testMinutelyByMonthDayAndWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 0, 1), - datetime(1998, 1, 1, 0, 2)]); - -testRecurring('testMinutelyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0), - datetime(1998, 1, 1, 0, 1), - datetime(1998, 1, 1, 0, 2)]); - -testRecurring('testMinutelyByYearDay', new RRule({freq: RRule.MINUTELY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0), - datetime(1997, 12, 31, 0, 1), - datetime(1997, 12, 31, 0, 2), - datetime(1997, 12, 31, 0, 3)]); - -testRecurring('testMinutelyByYearDayNeg', new RRule({freq: RRule.MINUTELY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0), - datetime(1997, 12, 31, 0, 1), - datetime(1997, 12, 31, 0, 2), - datetime(1997, 12, 31, 0, 3)]); - -testRecurring('testMinutelyByMonthAndYearDay', new RRule({freq: RRule.MINUTELY, - count:4, - bymonth:[4, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0), - datetime(1998, 4, 10, 0, 1), - datetime(1998, 4, 10, 0, 2), - datetime(1998, 4, 10, 0, 3)]); - -testRecurring('testMinutelyByMonthAndYearDayNeg', new RRule({freq: RRule.MINUTELY, - count:4, - bymonth:[4, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0), - datetime(1998, 4, 10, 0, 1), - datetime(1998, 4, 10, 0, 2), - datetime(1998, 4, 10, 0, 3)]); - -testRecurring('testMinutelyByWeekNo', new RRule({freq: RRule.MINUTELY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 0, 0), - datetime(1998, 5, 11, 0, 1), - datetime(1998, 5, 11, 0, 2)]); - -testRecurring('testMinutelyByWeekNoAndWeekDay', new RRule({freq: RRule.MINUTELY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 0, 0), - datetime(1997, 12, 29, 0, 1), - datetime(1997, 12, 29, 0, 2)]); - -testRecurring('testMinutelyByWeekNoAndWeekDayLarge', new RRule({freq: RRule.MINUTELY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0), - datetime(1997, 12, 28, 0, 1), - datetime(1997, 12, 28, 0, 2)]); - -testRecurring('testMinutelyByWeekNoAndWeekDayLast', new RRule({freq: RRule.MINUTELY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0), - datetime(1997, 12, 28, 0, 1), - datetime(1997, 12, 28, 0, 2)]); - -testRecurring('testMinutelyByWeekNoAndWeekDay53', new RRule({freq: RRule.MINUTELY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 0, 0), - datetime(1998, 12, 28, 0, 1), - datetime(1998, 12, 28, 0, 2)]); -/* -assertRecurring('testMinutelyByEaster', new RRule({freq: RRule.MINUTELY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 0, 0), - datetime(1998, 4, 12, 0, 1), - datetime(1998, 4, 12, 0, 2)]) - -assertRecurring('testMinutelyByEasterPos', new RRule({freq: RRule.MINUTELY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 0, 0), - datetime(1998, 4, 13, 0, 1), - datetime(1998, 4, 13, 0, 2)]) - -assertRecurring('testMinutelyByEasterNeg', new RRule({freq: RRule.MINUTELY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 0, 0), - datetime(1998, 4, 11, 0, 1), - datetime(1998, 4, 11, 0, 2)]) - */ -testRecurring('testMinutelyByHour', new RRule({freq: RRule.MINUTELY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0), - datetime(1997, 9, 2, 18, 1), - datetime(1997, 9, 2, 18, 2)]); - -testRecurring('testMinutelyByMinute', new RRule({freq: RRule.MINUTELY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6), - datetime(1997, 9, 2, 9, 18), - datetime(1997, 9, 2, 10, 6)]); - -testRecurring('testMinutelyBySecond', new RRule({freq: RRule.MINUTELY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 9, 2, 9, 1, 6)]); - -testRecurring('testMinutelyByHourAndMinute', new RRule({freq: RRule.MINUTELY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6), - datetime(1997, 9, 2, 18, 18), - datetime(1997, 9, 3, 6, 6)]); - -testRecurring('testMinutelyByHourAndSecond', new RRule({freq: RRule.MINUTELY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 9, 2, 18, 1, 6)]); - -testRecurring('testMinutelyByMinuteAndSecond', new RRule({freq: RRule.MINUTELY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testMinutelyByHourAndMinuteAndSecond', new RRule({freq: RRule.MINUTELY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testMinutelyBySetPos', new RRule({freq: RRule.MINUTELY, - count:3, - bysecond:[15, 30, 45], - bysetpos:[3, -3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 15), - datetime(1997, 9, 2, 9, 0, 45), - datetime(1997, 9, 2, 9, 1, 15)]); - -testRecurring('testSecondly', new RRule({freq: RRule.SECONDLY, - count:3, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 0), - datetime(1997, 9, 2, 9, 0, 1), - datetime(1997, 9, 2, 9, 0, 2)]); - -testRecurring('testSecondlyInterval', new RRule({freq: RRule.SECONDLY, - count:3, - interval:2, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 0), - datetime(1997, 9, 2, 9, 0, 2), - datetime(1997, 9, 2, 9, 0, 4)]); - -testRecurring('testSecondlyIntervalLarge', new RRule({freq: RRule.SECONDLY, - count:3, - interval:90061, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 0), - datetime(1997, 9, 3, 10, 1, 1), - datetime(1997, 9, 4, 11, 2, 2)]); - -testRecurring('testSecondlyByMonth', new RRule({freq: RRule.SECONDLY, - count:3, - bymonth:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0, 0), - datetime(1998, 1, 1, 0, 0, 1), - datetime(1998, 1, 1, 0, 0, 2)]); - -testRecurring('testSecondlyByMonthDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonthday:[1, 3], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 3, 0, 0, 0), - datetime(1997, 9, 3, 0, 0, 1), - datetime(1997, 9, 3, 0, 0, 2)]); - -testRecurring('testSecondlyByMonthAndMonthDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonth:[1, 3], - bymonthday:[5, 7], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 5, 0, 0, 0), - datetime(1998, 1, 5, 0, 0, 1), - datetime(1998, 1, 5, 0, 0, 2)]); - -testRecurring('testSecondlyByWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 0), - datetime(1997, 9, 2, 9, 0, 1), - datetime(1997, 9, 2, 9, 0, 2)]); - -testRecurring('testSecondlyByNWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 0), - datetime(1997, 9, 2, 9, 0, 1), - datetime(1997, 9, 2, 9, 0, 2)]); - -testRecurring('testSecondlyByMonthAndWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0, 0), - datetime(1998, 1, 1, 0, 0, 1), - datetime(1998, 1, 1, 0, 0, 2)]); - -testRecurring('testSecondlyByMonthAndNWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonth:[1, 3], - byweekday:[RRule.TU.nth(1), RRule.TH.nth(-1)], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0, 0), - datetime(1998, 1, 1, 0, 0, 1), - datetime(1998, 1, 1, 0, 0, 2)]); - -testRecurring('testSecondlyByMonthDayAndWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0, 0), - datetime(1998, 1, 1, 0, 0, 1), - datetime(1998, 1, 1, 0, 0, 2)]); - -testRecurring('testSecondlyByMonthAndMonthDayAndWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - bymonth:[1, 3], - bymonthday:[1, 3], - byweekday:[RRule.TU, RRule.TH], - dtstart:parse("19970902T090000")}), - [datetime(1998, 1, 1, 0, 0, 0), - datetime(1998, 1, 1, 0, 0, 1), - datetime(1998, 1, 1, 0, 0, 2)]); - -testRecurring('testSecondlyByYearDay', new RRule({freq: RRule.SECONDLY, - count:4, - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0, 0), - datetime(1997, 12, 31, 0, 0, 1), - datetime(1997, 12, 31, 0, 0, 2), - datetime(1997, 12, 31, 0, 0, 3)]); - -testRecurring('testSecondlyByYearDayNeg', new RRule({freq: RRule.SECONDLY, - count:4, - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 31, 0, 0, 0), - datetime(1997, 12, 31, 0, 0, 1), - datetime(1997, 12, 31, 0, 0, 2), - datetime(1997, 12, 31, 0, 0, 3)]); - -testRecurring('testSecondlyByMonthAndYearDay', new RRule({freq: RRule.SECONDLY, - count:4, - bymonth:[4, 7], - byyearday:[1, 100, 200, 365], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0, 0), - datetime(1998, 4, 10, 0, 0, 1), - datetime(1998, 4, 10, 0, 0, 2), - datetime(1998, 4, 10, 0, 0, 3)]); - -testRecurring('testSecondlyByMonthAndYearDayNeg', new RRule({freq: RRule.SECONDLY, - count:4, - bymonth:[4, 7], - byyearday:[-365, -266, -166, -1], - dtstart:parse("19970902T090000")}), - [datetime(1998, 4, 10, 0, 0, 0), - datetime(1998, 4, 10, 0, 0, 1), - datetime(1998, 4, 10, 0, 0, 2), - datetime(1998, 4, 10, 0, 0, 3)]); - -testRecurring('testSecondlyByWeekNo', new RRule({freq: RRule.SECONDLY, - count:3, - byweekno:20, - dtstart:parse("19970902T090000")}), - [datetime(1998, 5, 11, 0, 0, 0), - datetime(1998, 5, 11, 0, 0, 1), - datetime(1998, 5, 11, 0, 0, 2)]); - -testRecurring('testSecondlyByWeekNoAndWeekDay', new RRule({freq: RRule.SECONDLY, - count:3, - byweekno:1, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 29, 0, 0, 0), - datetime(1997, 12, 29, 0, 0, 1), - datetime(1997, 12, 29, 0, 0, 2)]); - -testRecurring('testSecondlyByWeekNoAndWeekDayLarge', new RRule({freq: RRule.SECONDLY, - count:3, - byweekno:52, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0, 0), - datetime(1997, 12, 28, 0, 0, 1), - datetime(1997, 12, 28, 0, 0, 2)]); - -testRecurring('testSecondlyByWeekNoAndWeekDayLast', new RRule({freq: RRule.SECONDLY, - count:3, - byweekno:-1, - byweekday:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 12, 28, 0, 0, 0), - datetime(1997, 12, 28, 0, 0, 1), - datetime(1997, 12, 28, 0, 0, 2)]); - -testRecurring('testSecondlyByWeekNoAndWeekDay53', new RRule({freq: RRule.SECONDLY, - count:3, - byweekno:53, - byweekday:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1998, 12, 28, 0, 0, 0), - datetime(1998, 12, 28, 0, 0, 1), - datetime(1998, 12, 28, 0, 0, 2)]); -/* -assertRecurring('testSecondlyByEaster', new RRule({freq: RRule.SECONDLY, - count: 3, - byeaster: 0, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 12, 0, 0, 0), - datetime(1998, 4, 12, 0, 0, 1), - datetime(1998, 4, 12, 0, 0, 2)]) - -assertRecurring('testSecondlyByEasterPos', new RRule({freq: RRule.SECONDLY, - count: 3, - byeaster: 1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 13, 0, 0, 0), - datetime(1998, 4, 13, 0, 0, 1), - datetime(1998, 4, 13, 0, 0, 2)]) - -assertRecurring('testSecondlyByEasterNeg', new RRule({freq: RRule.SECONDLY, - count: 3, - byeaster: -1, - dtstart: parse("19970902T090000")}), - [datetime(1998, 4, 11, 0, 0, 0), - datetime(1998, 4, 11, 0, 0, 1), - datetime(1998, 4, 11, 0, 0, 2)]) - */ -testRecurring('testSecondlyByHour', new RRule({freq: RRule.SECONDLY, - count:3, - byhour:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 0), - datetime(1997, 9, 2, 18, 0, 1), - datetime(1997, 9, 2, 18, 0, 2)]); - -testRecurring('testSecondlyByMinute', new RRule({freq: RRule.SECONDLY, - count:3, - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 0), - datetime(1997, 9, 2, 9, 6, 1), - datetime(1997, 9, 2, 9, 6, 2)]); - -testRecurring('testSecondlyBySecond', new RRule({freq: RRule.SECONDLY, - count:3, - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0, 6), - datetime(1997, 9, 2, 9, 0, 18), - datetime(1997, 9, 2, 9, 1, 6)]); - -testRecurring('testSecondlyByHourAndMinute', new RRule({freq: RRule.SECONDLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 0), - datetime(1997, 9, 2, 18, 6, 1), - datetime(1997, 9, 2, 18, 6, 2)]); - -testRecurring('testSecondlyByHourAndSecond', new RRule({freq: RRule.SECONDLY, - count:3, - byhour:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 0, 6), - datetime(1997, 9, 2, 18, 0, 18), - datetime(1997, 9, 2, 18, 1, 6)]); - -testRecurring('testSecondlyByMinuteAndSecond', new RRule({freq: RRule.SECONDLY, - count:3, - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 6, 6), - datetime(1997, 9, 2, 9, 6, 18), - datetime(1997, 9, 2, 9, 18, 6)]); - -testRecurring('testSecondlyByHourAndMinuteAndSecond', new RRule({freq: RRule.SECONDLY, - count:3, - byhour:[6, 18], - byminute:[6, 18], - bysecond:[6, 18], - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 18, 6, 6), - datetime(1997, 9, 2, 18, 6, 18), - datetime(1997, 9, 2, 18, 18, 6)]); - -testRecurring('testUntilNotMatching', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000"), - until:parse("19970905T080000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]); - -testRecurring('testUntilMatching', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000"), - until:parse("19970904T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]); - -testRecurring('testUntilSingle', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000"), - until:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0)]); - -testRecurring('testUntilEmpty', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000"), - until:parse("19970901T090000")}), - []); - -testRecurring('testUntilWithDate', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000"), - until:date(1997, 9, 5)}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]); - -testRecurring('testWkStIntervalMO', new RRule({freq: RRule.WEEKLY, - count:3, - interval:2, - byweekday:[RRule.TU, RRule.SU], - wkst:RRule.MO, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 7, 9, 0), - datetime(1997, 9, 16, 9, 0)]); - -testRecurring('testWkStIntervalSU', new RRule({freq: RRule.WEEKLY, - count:3, - interval:2, - byweekday:[RRule.TU, RRule.SU], - wkst:RRule.SU, - dtstart:parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 14, 9, 0), - datetime(1997, 9, 16, 9, 0)]); - -testRecurring('testDTStartIsDate', new RRule({freq: RRule.DAILY, - count:3, - dtstart:date(1997, 9, 2)}), - [datetime(1997, 9, 2, 0, 0), - datetime(1997, 9, 3, 0, 0), - datetime(1997, 9, 4, 0, 0)]); - -testRecurring('testDTStartWithMicroseconds', new RRule({freq: RRule.DAILY, - count:3, - dtstart:parse("19970902T090000.5")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]); - -testRecurring('testMaxYear', new RRule({freq: RRule.YEARLY, - count:3, - bymonth:2, - bymonthday:31, - dtstart:parse("99970902T090000")}), - []); - -testRecurring('testSubsecondStartYearly' , new RRule({ + // Another nice test. The last days of week number 52/53 + // may be in the next year. + new RRule({freq: RRule.DAILY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1998, 12, 27, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testDailyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.DAILY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 9, 0), + datetime(1999, 1, 3, 9, 0), + datetime(2000, 1, 2, 9, 0) + ] +) + +testRecurring('testDailyByWeekNoAndWeekDay53', + new RRule({freq: RRule.DAILY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 9, 0), + datetime(2004, 12, 27, 9, 0), + datetime(2009, 12, 28, 9, 0) + ] +) + +testRecurring('testDailyByEaster', + new RRule({freq: RRule.DAILY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 9, 0), + datetime(1999, 4, 4, 9, 0), + datetime(2000, 4, 23, 9, 0) + ] +) + +testRecurring('testDailyByEasterPos', + new RRule({freq: RRule.DAILY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 9, 0), + datetime(1999, 4, 5, 9, 0), + datetime(2000, 4, 24, 9, 0) + ] +) + +testRecurring('testDailyByEasterNeg', + new RRule({freq: RRule.DAILY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 9, 0), + datetime(1999, 4, 3, 9, 0), + datetime(2000, 4, 22, 9, 0) + ] +) + +testRecurring('testDailyByHour', + new RRule({freq: RRule.DAILY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 9, 3, 6, 0), + datetime(1997, 9, 3, 18, 0) + ] +) + +testRecurring('testDailyByMinute', + new RRule({freq: RRule.DAILY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1997, 9, 3, 9, 6) + ] +) + +testRecurring('testDailyBySecond', + new RRule({freq: RRule.DAILY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 9, 3, 9, 0, 6) + ] +) + +testRecurring('testDailyByHourAndMinute', + new RRule({freq: RRule.DAILY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1997, 9, 3, 6, 6) + ] +) + +testRecurring('testDailyByHourAndSecond', + new RRule({freq: RRule.DAILY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 9, 3, 6, 0, 6) + ] +) + +testRecurring('testDailyByMinuteAndSecond', + new RRule({freq: RRule.DAILY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testDailyByHourAndMinuteAndSecond', + new RRule({freq: RRule.DAILY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testDailyBySetPos', + new RRule({freq: RRule.DAILY, + count: 3, + byhour: [6, 18], + byminute: [15, 45], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 15), + datetime(1997, 9, 3, 6, 45), + datetime(1997, 9, 3, 18, 15) + ] +) + +testRecurring('testHourly', + new RRule({freq: RRule.HOURLY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 10, 0), + datetime(1997, 9, 2, 11, 0) + ] +) + +testRecurring('testHourlyInterval', + new RRule({freq: RRule.HOURLY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 11, 0), + datetime(1997, 9, 2, 13, 0) + ] +) + +testRecurring('testHourlyIntervalLarge', + new RRule({freq: RRule.HOURLY, + count: 3, + interval: 769, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 10, 4, 10, 0), + datetime(1997, 11, 5, 11, 0) + ] +) + +testRecurring('testHourlyByMonth', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 1, 0), + datetime(1998, 1, 1, 2, 0) + ] +) + +testRecurring('testHourlyByMonthDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 0, 0), + datetime(1997, 9, 3, 1, 0), + datetime(1997, 9, 3, 2, 0) + ] +) + +testRecurring('testHourlyByMonthAndMonthDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 0, 0), + datetime(1998, 1, 5, 1, 0), + datetime(1998, 1, 5, 2, 0) + ] +) + +testRecurring('testHourlyByWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 10, 0), + datetime(1997, 9, 2, 11, 0) + ] +) + +testRecurring('testHourlyByNWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 10, 0), + datetime(1997, 9, 2, 11, 0) + ] +) + +testRecurring('testHourlyByMonthAndWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 1, 0), + datetime(1998, 1, 1, 2, 0) + ] +) + +testRecurring('testHourlyByMonthAndNWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 1, 0), + datetime(1998, 1, 1, 2, 0) + ] +) + +testRecurring('testHourlyByMonthDayAndWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 1, 0), + datetime(1998, 1, 1, 2, 0) + ] +) + +testRecurring('testHourlyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 1, 0), + datetime(1998, 1, 1, 2, 0) + ] +) + +testRecurring('testHourlyByYearDay', + new RRule({freq: RRule.HOURLY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0), + datetime(1997, 12, 31, 1, 0), + datetime(1997, 12, 31, 2, 0), + datetime(1997, 12, 31, 3, 0) + ] +) + +testRecurring('testHourlyByYearDayNeg', + new RRule({freq: RRule.HOURLY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0), + datetime(1997, 12, 31, 1, 0), + datetime(1997, 12, 31, 2, 0), + datetime(1997, 12, 31, 3, 0) + ] +) + +testRecurring('testHourlyByMonthAndYearDay', + new RRule({freq: RRule.HOURLY, + count: 4, + bymonth: [4, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0), + datetime(1998, 4, 10, 1, 0), + datetime(1998, 4, 10, 2, 0), + datetime(1998, 4, 10, 3, 0) + ] +) + +testRecurring('testHourlyByMonthAndYearDayNeg', + new RRule({freq: RRule.HOURLY, + count: 4, + bymonth: [4, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0), + datetime(1998, 4, 10, 1, 0), + datetime(1998, 4, 10, 2, 0), + datetime(1998, 4, 10, 3, 0) + ] +) + +testRecurring('testHourlyByWeekNo', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 0, 0), + datetime(1998, 5, 11, 1, 0), + datetime(1998, 5, 11, 2, 0) + ] +) + +testRecurring('testHourlyByWeekNoAndWeekDay', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 0, 0), + datetime(1997, 12, 29, 1, 0), + datetime(1997, 12, 29, 2, 0) + ] +) + +testRecurring('testHourlyByWeekNoAndWeekDayLarge', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0), + datetime(1997, 12, 28, 1, 0), + datetime(1997, 12, 28, 2, 0) + ] +) + +testRecurring('testHourlyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0), + datetime(1997, 12, 28, 1, 0), + datetime(1997, 12, 28, 2, 0) + ] +) + +testRecurring('testHourlyByWeekNoAndWeekDay53', + new RRule({freq: RRule.HOURLY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 0, 0), + datetime(1998, 12, 28, 1, 0), + datetime(1998, 12, 28, 2, 0) + ] +) + +testRecurring.skip('testHourlyByEaster', + new RRule({freq: RRule.HOURLY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 0, 0), + datetime(1998, 4, 12, 1, 0), + datetime(1998, 4, 12, 2, 0) + ] +) + +testRecurring.skip('testHourlyByEasterPos', + new RRule({freq: RRule.HOURLY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 0, 0), + datetime(1998, 4, 13, 1, 0), + datetime(1998, 4, 13, 2, 0) + ] +) + +testRecurring.skip('testHourlyByEasterNeg', + new RRule({freq: RRule.HOURLY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 0, 0), + datetime(1998, 4, 11, 1, 0), + datetime(1998, 4, 11, 2, 0) + ] +) + +testRecurring('testHourlyByHour', + new RRule({freq: RRule.HOURLY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 9, 3, 6, 0), + datetime(1997, 9, 3, 18, 0) + ] +) + +testRecurring('testHourlyByMinute', + new RRule({freq: RRule.HOURLY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1997, 9, 2, 10, 6) + ] +) + +testRecurring('testHourlyBySecond', + new RRule({freq: RRule.HOURLY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 9, 2, 10, 0, 6) + ] +) + +testRecurring('testHourlyByHourAndMinute', + new RRule({freq: RRule.HOURLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1997, 9, 3, 6, 6) + ] +) + +testRecurring('testHourlyByHourAndSecond', + new RRule({freq: RRule.HOURLY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 9, 3, 6, 0, 6) + ] +) + +testRecurring('testHourlyByMinuteAndSecond', + new RRule({freq: RRule.HOURLY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testHourlyByHourAndMinuteAndSecond', + new RRule({freq: RRule.HOURLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testHourlyBySetPos', + new RRule({freq: RRule.HOURLY, + count: 3, + byminute: [15, 45], + bysecond: [15, 45], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 15, 45), + datetime(1997, 9, 2, 9, 45, 15), + datetime(1997, 9, 2, 10, 15, 45) + ] +) + +testRecurring('testMinutely', + new RRule({freq: RRule.MINUTELY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 9, 1), + datetime(1997, 9, 2, 9, 2) + ] +) + +testRecurring('testMinutelyInterval', + new RRule({freq: RRule.MINUTELY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 9, 2), + datetime(1997, 9, 2, 9, 4) + ] +) + +testRecurring('testMinutelyIntervalLarge', + new RRule({freq: RRule.MINUTELY, + count: 3, + interval: 1501, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 10, 1), + datetime(1997, 9, 4, 11, 2) + ] +) + +testRecurring('testMinutelyByMonth', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 0, 1), + datetime(1998, 1, 1, 0, 2) + ] +) + +testRecurring('testMinutelyByMonthDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 0, 0), + datetime(1997, 9, 3, 0, 1), + datetime(1997, 9, 3, 0, 2) + ] +) + +testRecurring('testMinutelyByMonthAndMonthDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 0, 0), + datetime(1998, 1, 5, 0, 1), + datetime(1998, 1, 5, 0, 2) + ] +) + +testRecurring('testMinutelyByWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 9, 1), + datetime(1997, 9, 2, 9, 2) + ] +) + +testRecurring('testMinutelyByNWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 2, 9, 1), + datetime(1997, 9, 2, 9, 2) + ] +) + +testRecurring('testMinutelyByMonthAndWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 0, 1), + datetime(1998, 1, 1, 0, 2) + ] +) + +testRecurring('testMinutelyByMonthAndNWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 0, 1), + datetime(1998, 1, 1, 0, 2) + ] +) + +testRecurring('testMinutelyByMonthDayAndWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 0, 1), + datetime(1998, 1, 1, 0, 2) + ] +) + +testRecurring('testMinutelyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0), + datetime(1998, 1, 1, 0, 1), + datetime(1998, 1, 1, 0, 2) + ] +) + +testRecurring('testMinutelyByYearDay', + new RRule({freq: RRule.MINUTELY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0), + datetime(1997, 12, 31, 0, 1), + datetime(1997, 12, 31, 0, 2), + datetime(1997, 12, 31, 0, 3) + ] +) + +testRecurring('testMinutelyByYearDayNeg', + new RRule({freq: RRule.MINUTELY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0), + datetime(1997, 12, 31, 0, 1), + datetime(1997, 12, 31, 0, 2), + datetime(1997, 12, 31, 0, 3) + ] +) + +testRecurring('testMinutelyByMonthAndYearDay', + new RRule({freq: RRule.MINUTELY, + count: 4, + bymonth: [4, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0), + datetime(1998, 4, 10, 0, 1), + datetime(1998, 4, 10, 0, 2), + datetime(1998, 4, 10, 0, 3) + ] +) + +testRecurring('testMinutelyByMonthAndYearDayNeg', + new RRule({freq: RRule.MINUTELY, + count: 4, + bymonth: [4, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0), + datetime(1998, 4, 10, 0, 1), + datetime(1998, 4, 10, 0, 2), + datetime(1998, 4, 10, 0, 3) + ] +) + +testRecurring('testMinutelyByWeekNo', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 0, 0), + datetime(1998, 5, 11, 0, 1), + datetime(1998, 5, 11, 0, 2) + ] +) + +testRecurring('testMinutelyByWeekNoAndWeekDay', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 0, 0), + datetime(1997, 12, 29, 0, 1), + datetime(1997, 12, 29, 0, 2) + ] +) + +testRecurring('testMinutelyByWeekNoAndWeekDayLarge', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0), + datetime(1997, 12, 28, 0, 1), + datetime(1997, 12, 28, 0, 2) + ] +) + +testRecurring('testMinutelyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0), + datetime(1997, 12, 28, 0, 1), + datetime(1997, 12, 28, 0, 2) + ] +) + +testRecurring('testMinutelyByWeekNoAndWeekDay53', + new RRule({freq: RRule.MINUTELY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 0, 0), + datetime(1998, 12, 28, 0, 1), + datetime(1998, 12, 28, 0, 2) + ] +) + +testRecurring.skip('testMinutelyByEaster', + new RRule({freq: RRule.MINUTELY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 0, 0), + datetime(1998, 4, 12, 0, 1), + datetime(1998, 4, 12, 0, 2) + ] +) + +testRecurring.skip('testMinutelyByEasterPos', + new RRule({freq: RRule.MINUTELY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 0, 0), + datetime(1998, 4, 13, 0, 1), + datetime(1998, 4, 13, 0, 2) + ] +) + +testRecurring.skip('testMinutelyByEasterNeg', + new RRule({freq: RRule.MINUTELY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 0, 0), + datetime(1998, 4, 11, 0, 1), + datetime(1998, 4, 11, 0, 2) + ] +) + +testRecurring('testMinutelyByHour', + new RRule({freq: RRule.MINUTELY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0), + datetime(1997, 9, 2, 18, 1), + datetime(1997, 9, 2, 18, 2) + ] +) + +testRecurring('testMinutelyByMinute', + new RRule({freq: RRule.MINUTELY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6), + datetime(1997, 9, 2, 9, 18), + datetime(1997, 9, 2, 10, 6) + ] +) + +testRecurring('testMinutelyBySecond', + new RRule({freq: RRule.MINUTELY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 9, 2, 9, 1, 6) + ] +) + +testRecurring('testMinutelyByHourAndMinute', + new RRule({freq: RRule.MINUTELY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6), + datetime(1997, 9, 2, 18, 18), + datetime(1997, 9, 3, 6, 6) + ] +) + +testRecurring('testMinutelyByHourAndSecond', + new RRule({freq: RRule.MINUTELY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 9, 2, 18, 1, 6) + ] +) + +testRecurring('testMinutelyByMinuteAndSecond', + new RRule({freq: RRule.MINUTELY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testMinutelyByHourAndMinuteAndSecond', + new RRule({freq: RRule.MINUTELY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testMinutelyBySetPos', + new RRule({freq: RRule.MINUTELY, + count: 3, + bysecond: [15, 30, 45], + bysetpos: [3, -3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 15), + datetime(1997, 9, 2, 9, 0, 45), + datetime(1997, 9, 2, 9, 1, 15) + ] +) + +testRecurring('testSecondly', + new RRule({freq: RRule.SECONDLY, + count: 3, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 0), + datetime(1997, 9, 2, 9, 0, 1), + datetime(1997, 9, 2, 9, 0, 2) + ] +) + +testRecurring('testSecondlyInterval', + new RRule({freq: RRule.SECONDLY, + count: 3, + interval: 2, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 0), + datetime(1997, 9, 2, 9, 0, 2), + datetime(1997, 9, 2, 9, 0, 4) + ] +) + +testRecurring('testSecondlyIntervalLarge', + new RRule({freq: RRule.SECONDLY, + count: 3, + interval: 90061, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 0), + datetime(1997, 9, 3, 10, 1, 1), + datetime(1997, 9, 4, 11, 2, 2) + ] +) + +testRecurring('testSecondlyByMonth', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonth: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0, 0), + datetime(1998, 1, 1, 0, 0, 1), + datetime(1998, 1, 1, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonthday: [1, 3], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 3, 0, 0, 0), + datetime(1997, 9, 3, 0, 0, 1), + datetime(1997, 9, 3, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthAndMonthDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonth: [1, 3], + bymonthday: [5, 7], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 5, 0, 0, 0), + datetime(1998, 1, 5, 0, 0, 1), + datetime(1998, 1, 5, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 0), + datetime(1997, 9, 2, 9, 0, 1), + datetime(1997, 9, 2, 9, 0, 2) + ] +) + +testRecurring('testSecondlyByNWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 0), + datetime(1997, 9, 2, 9, 0, 1), + datetime(1997, 9, 2, 9, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthAndWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0, 0), + datetime(1998, 1, 1, 0, 0, 1), + datetime(1998, 1, 1, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthAndNWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonth: [1, 3], + byweekday: [RRule.TU.nth(1), RRule.TH.nth(-1)], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0, 0), + datetime(1998, 1, 1, 0, 0, 1), + datetime(1998, 1, 1, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthDayAndWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0, 0), + datetime(1998, 1, 1, 0, 0, 1), + datetime(1998, 1, 1, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByMonthAndMonthDayAndWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + bymonth: [1, 3], + bymonthday: [1, 3], + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 1, 1, 0, 0, 0), + datetime(1998, 1, 1, 0, 0, 1), + datetime(1998, 1, 1, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByYearDay', + new RRule({freq: RRule.SECONDLY, + count: 4, + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0, 0), + datetime(1997, 12, 31, 0, 0, 1), + datetime(1997, 12, 31, 0, 0, 2), + datetime(1997, 12, 31, 0, 0, 3) + ] +) + +testRecurring('testSecondlyByYearDayNeg', + new RRule({freq: RRule.SECONDLY, + count: 4, + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 31, 0, 0, 0), + datetime(1997, 12, 31, 0, 0, 1), + datetime(1997, 12, 31, 0, 0, 2), + datetime(1997, 12, 31, 0, 0, 3) + ] +) + +testRecurring('testSecondlyByMonthAndYearDay', + new RRule({freq: RRule.SECONDLY, + count: 4, + bymonth: [4, 7], + byyearday: [1, 100, 200, 365], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0, 0), + datetime(1998, 4, 10, 0, 0, 1), + datetime(1998, 4, 10, 0, 0, 2), + datetime(1998, 4, 10, 0, 0, 3) + ] +) + +testRecurring('testSecondlyByMonthAndYearDayNeg', + new RRule({freq: RRule.SECONDLY, + count: 4, + bymonth: [4, 7], + byyearday: [-365, -266, -166, -1], + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 10, 0, 0, 0), + datetime(1998, 4, 10, 0, 0, 1), + datetime(1998, 4, 10, 0, 0, 2), + datetime(1998, 4, 10, 0, 0, 3) + ] +) + +testRecurring('testSecondlyByWeekNo', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekno: 20, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 5, 11, 0, 0, 0), + datetime(1998, 5, 11, 0, 0, 1), + datetime(1998, 5, 11, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByWeekNoAndWeekDay', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekno: 1, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 29, 0, 0, 0), + datetime(1997, 12, 29, 0, 0, 1), + datetime(1997, 12, 29, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByWeekNoAndWeekDayLarge', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekno: 52, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0, 0), + datetime(1997, 12, 28, 0, 0, 1), + datetime(1997, 12, 28, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByWeekNoAndWeekDayLast', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekno: -1, + byweekday: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 12, 28, 0, 0, 0), + datetime(1997, 12, 28, 0, 0, 1), + datetime(1997, 12, 28, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByWeekNoAndWeekDay53', + new RRule({freq: RRule.SECONDLY, + count: 3, + byweekno: 53, + byweekday: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 12, 28, 0, 0, 0), + datetime(1998, 12, 28, 0, 0, 1), + datetime(1998, 12, 28, 0, 0, 2) + ] +) + +testRecurring.skip('testSecondlyByEaster', + new RRule({freq: RRule.SECONDLY, + count: 3, + byeaster: 0, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 12, 0, 0, 0), + datetime(1998, 4, 12, 0, 0, 1), + datetime(1998, 4, 12, 0, 0, 2) + ] +) + +testRecurring.skip('testSecondlyByEasterPos', + new RRule({freq: RRule.SECONDLY, + count: 3, + byeaster: 1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 13, 0, 0, 0), + datetime(1998, 4, 13, 0, 0, 1), + datetime(1998, 4, 13, 0, 0, 2) + ] +) + +testRecurring.skip('testSecondlyByEasterNeg', + new RRule({freq: RRule.SECONDLY, + count: 3, + byeaster: -1, + dtstart: parse('19970902T090000') + }), + [ + datetime(1998, 4, 11, 0, 0, 0), + datetime(1998, 4, 11, 0, 0, 1), + datetime(1998, 4, 11, 0, 0, 2) + ] +) + +testRecurring('testSecondlyByHour', + new RRule({freq: RRule.SECONDLY, + count: 3, + byhour: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 0), + datetime(1997, 9, 2, 18, 0, 1), + datetime(1997, 9, 2, 18, 0, 2) + ] +) + +testRecurring('testSecondlyByMinute', + new RRule({freq: RRule.SECONDLY, + count: 3, + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 0), + datetime(1997, 9, 2, 9, 6, 1), + datetime(1997, 9, 2, 9, 6, 2) + ] +) + +testRecurring('testSecondlyBySecond', + new RRule({freq: RRule.SECONDLY, + count: 3, + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0, 6), + datetime(1997, 9, 2, 9, 0, 18), + datetime(1997, 9, 2, 9, 1, 6) + ] +) + +testRecurring('testSecondlyByHourAndMinute', + new RRule({freq: RRule.SECONDLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 0), + datetime(1997, 9, 2, 18, 6, 1), + datetime(1997, 9, 2, 18, 6, 2) + ] +) + +testRecurring('testSecondlyByHourAndSecond', + new RRule({freq: RRule.SECONDLY, + count: 3, + byhour: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 0, 6), + datetime(1997, 9, 2, 18, 0, 18), + datetime(1997, 9, 2, 18, 1, 6) + ] +) + +testRecurring('testSecondlyByMinuteAndSecond', + new RRule({freq: RRule.SECONDLY, + count: 3, + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 6, 6), + datetime(1997, 9, 2, 9, 6, 18), + datetime(1997, 9, 2, 9, 18, 6) + ] +) + +testRecurring('testSecondlyByHourAndMinuteAndSecond', + new RRule({freq: RRule.SECONDLY, + count: 3, + byhour: [6, 18], + byminute: [6, 18], + bysecond: [6, 18], + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 18, 6, 6), + datetime(1997, 9, 2, 18, 6, 18), + datetime(1997, 9, 2, 18, 18, 6) + ] +) + +testRecurring('testUntilNotMatching', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000'), + until: parse('19970905T080000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0) + ] +) + +testRecurring('testUntilMatching', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000'), + until: parse('19970904T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0) + ] +) + +testRecurring('testUntilSingle', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000'), + until: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0) + ] +) + +testRecurring('testUntilEmpty', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000'), + until: parse('19970901T090000') + }), + [] +) + +testRecurring('testUntilWithDate', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000'), + until: datetime(1997, 9, 5) + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0) + ] +) + +testRecurring('testWkStIntervalMO', + new RRule({freq: RRule.WEEKLY, + count: 3, + interval: 2, + byweekday: [RRule.TU, RRule.SU], + wkst: RRule.MO, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 7, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testWkStIntervalSU', + new RRule({freq: RRule.WEEKLY, + count: 3, + interval: 2, + byweekday: [RRule.TU, RRule.SU], + wkst: RRule.SU, + dtstart: parse('19970902T090000') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 14, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testDTStartIsDate', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: datetime(1997, 9, 2) + }), + [ + datetime(1997, 9, 2, 0, 0), + datetime(1997, 9, 3, 0, 0), + datetime(1997, 9, 4, 0, 0) + ] +) + +testRecurring('testDTStartWithMicroseconds', + new RRule({freq: RRule.DAILY, + count: 3, + dtstart: parse('19970902T090000.5') + }), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 3, 9, 0), + datetime(1997, 9, 4, 9, 0) + ] +) + +testRecurring('testMaxYear', + new RRule({freq: RRule.YEARLY, + count: 3, + bymonth: 2, + bymonthday: 31, + dtstart: parse('99970902T090000') + }), + [] +) + +testRecurring('testSubsecondStartYearly', + new RRule({ freq: RRule.YEARLY, - count:1, - dtstart:new Date(1420063200001) - }), - - [new Date(1420063200001)]); - -testRecurring('testSubsecondStartMonthlyByMonthDay' , new RRule({ - + count: 1, + dtstart: new Date(1420063200001) + }), + [ + new Date(1420063200001) + ] +) + +testRecurring('testSubsecondStartMonthlyByMonthDay', + new RRule({ freq: RRule.MONTHLY, count: 1, - bysetpos: [-1,1] , - dtstart:new Date(1356991200001) - }), - [new Date(1356991200001)]); - -test('testAfterBefore', function(){ - "YEARLY,MONTHLY,DAILY,HOURLY,MINUTELY,SECONDLY".split(',').forEach( function (freq_str){ - var date= new Date(1356991200001); - var rr= new RRule({ - freq:RRule[freq_str], - dtstart:date - }); - equal(date.getTime(), rr.options.dtstart.getTime(), "the supplied dtstart differs from RRule.options.dtstart"); - res=rr.before(rr.after(rr.options.dtstart)); - if (!(res == null)) { - // on purpose ==, so that undefined also matches - res=res.getTime(); - } - equal( res, rr.options.dtstart.getTime(), 'after dtstart , followed by before does not return dtstart'); - }); + bysetpos: [-1, 1], + dtstart: new Date(1356991200001) + }), + [ + new Date(1356991200001) + ] +) + +test('testAfterBefore', function () { + 'YEARLY,MONTHLY,DAILY,HOURLY,MINUTELY,SECONDLY'.split(',').forEach(function (freq_str) { + var date = new Date(1356991200001) + var rr = new RRule({ + freq: RRule[freq_str], + dtstart: date + }) + + equal(date.getTime(), rr.options.dtstart.getTime(), + 'the supplied dtstart differs from RRule.options.dtstart') + var res = rr.before(rr.after(rr.options.dtstart)) + + if (res != null) res = res.getTime() + equal(res, rr.options.dtstart.getTime(), + 'after dtstart , followed by before does not return dtstart') + }) +}) + +module('RRuleSet', { + setup: function () { + // Enable additional toString() / fromString() tests + // for each testRecurring(). + this.ALSO_TEST_STRING_FUNCTIONS = false + + // Enable additional toText() / fromText() tests + // for each testRecurring(). + // Many of the tests fail because the conversion is only approximate, + // but it gives an idea about how well or bad it converts. + this.ALSO_TEST_NLP_FUNCTIONS = false + + // Thorough after()/before()/between() tests. + // NOTE: can take a longer time. + this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true + } }) -/* these tests basically test the iterator implementation only */ -/* -assertRecurring('testGetItem', new RRule({freq: RRule.DAILY, - count: 3, - dtstart: parse("19970902T090000"))}[0], - datetime(1997, 9, 2, 9, 0)) - -assertRecurring('testGetItemNeg', new RRule({freq: RRule.DAILY, - count: 3, - dtstart: parse("19970902T090000"))[-1], - datetime(1997, 9, 4, 9, 0)) - -assertRecurring('testGetItemSlice', new RRule({freq: RRule.DAILY, - //count: 3, - dtstart: parse("19970902T090000"))[1:2], - [datetime(1997, 9, 3, 9, 0)]) - -assertRecurring('testGetItemSliceEmpty', new RRule({freq: RRule.DAILY, - count: 3, - dtstart: parse("19970902T090000"))[:], - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0)]) - -assertRecurring('testGetItemSliceStep', new RRule({freq: RRule.DAILY, - count: 3, - dtstart: parse("19970902T090000"))[::-2], - [datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 2, 9, 0)]) - -assertRecurring('testCount', new RRule({freq: RRule.DAILY, - count: 3, - dtstart: parse("19970902T090000")).count(), - 3) - -assertRecurring('testContains', - rr : new RRule({freq: RRule.DAILY, count: 3, dtstart: parse("19970902T090000")) - datetime(1997, 9, 3, 9, 0) in rr, True) - -assertRecurring('testContainsNot', - rr : new RRule({freq: RRule.DAILY, count: 3, dtstart: parse("19970902T090000")) - datetime(1997, 9, 3, 9, 0) not in rr, False) -*/ - - - - -//================================================ -// Cache tests, irrelevant to the JS version -//================================================ -/* -assertRecurring('testCachePre', - rr : new RRule({freq: RRule.DAILY, count: 15, cache: True, - dtstart: parse("19970902T090000")) - rr), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 5, 9, 0), - datetime(1997, 9, 6, 9, 0), - datetime(1997, 9, 7, 9, 0), - datetime(1997, 9, 8, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 10, 9, 0), - datetime(1997, 9, 11, 9, 0), - datetime(1997, 9, 12, 9, 0), - datetime(1997, 9, 13, 9, 0), - datetime(1997, 9, 14, 9, 0), - datetime(1997, 9, 15, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testCachePost', - rr : new RRule({freq: RRule.DAILY, count: 15, cache: True, - dtstart: parse("19970902T090000")) - for x in rr: pass - rr), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 5, 9, 0), - datetime(1997, 9, 6, 9, 0), - datetime(1997, 9, 7, 9, 0), - datetime(1997, 9, 8, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 10, 9, 0), - datetime(1997, 9, 11, 9, 0), - datetime(1997, 9, 12, 9, 0), - datetime(1997, 9, 13, 9, 0), - datetime(1997, 9, 14, 9, 0), - datetime(1997, 9, 15, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testCachePostInternal', - rr : new RRule({freq: RRule.DAILY, count: 15, cache: True, - dtstart: parse("19970902T090000")) - for x in rr: pass - rr._cache, - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 3, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 5, 9, 0), - datetime(1997, 9, 6, 9, 0), - datetime(1997, 9, 7, 9, 0), - datetime(1997, 9, 8, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 10, 9, 0), - datetime(1997, 9, 11, 9, 0), - datetime(1997, 9, 12, 9, 0), - datetime(1997, 9, 13, 9, 0), - datetime(1997, 9, 14, 9, 0), - datetime(1997, 9, 15, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testCachePreContains', - rr : new RRule({freq: RRule.DAILY, count: 3, cache: True, - dtstart: parse("19970902T090000")) - datetime(1997, 9, 3, 9, 0) in rr, True) - -assertRecurring('testCachePostContains', - rr : new RRule({freq: RRule.DAILY, count: 3, cache: True, - dtstart: parse("19970902T090000")) - for x in rr: pass - datetime(1997, 9, 3, 9, 0) in rr, True) - */ - -/* sets and other unimplemented stuff */ - -/* -assertRecurring('testSet', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000"))) - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testSetDate', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TU, - dtstart: parse("19970902T090000"))) +testRecurring('testSet', + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 2, + byweekday: RRule.TU, + dtstart: parse('19970902T090000') + })) + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 1, + byweekday: RRule.TH, + dtstart: parse('19970902T090000') + })) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testSetDate', + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 1, + byweekday: RRule.TU, + dtstart: parse('19970902T090000') + })) set.rdate(datetime(1997, 9, 4, 9)) set.rdate(datetime(1997, 9, 9, 9)) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testSetExRule', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 6, byweekday: [RRule.TU, RRule.TH], - dtstart: parse("19970902T090000"))) - set.exrule(new RRule({freq: RRule.YEARLY, count: 3, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testSetExDate', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 6, byweekday: [RRule.TU, RRule.TH], - dtstart: parse("19970902T090000"))) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testSetExRule', + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 6, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + })) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 3, + byweekday: RRule.TH, + dtstart: parse('19970902T090000') + })) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 9, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testSetExDate', + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 6, + byweekday: [RRule.TU, RRule.TH], + dtstart: parse('19970902T090000') + })) set.exdate(datetime(1997, 9, 4, 9)) set.exdate(datetime(1997, 9, 11, 9)) set.exdate(datetime(1997, 9, 18, 9)) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testSetExDateRevOrder', - set : rruleset() - set.rrule(new RRule({freq: RRule.MONTHLY, count: 5, bymonthday: 10, - dtstart: parse("20040101T090000"))) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 9, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testSetExDateRevOrder', + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.MONTHLY, + count: 5, + bymonthday: 10, + dtstart: parse('20040101T090000') + })) set.exdate(datetime(2004, 4, 10, 9, 0)) set.exdate(datetime(2004, 2, 10, 9, 0)) - set), - [datetime(2004, 1, 10, 9, 0), - datetime(2004, 3, 10, 9, 0), - datetime(2004, 5, 10, 9, 0)]) + return set + }, + [ + datetime(2004, 1, 10, 9, 0), + datetime(2004, 3, 10, 9, 0), + datetime(2004, 5, 10, 9, 0) + ] +) + +testRecurring('testSetDateAndExDate', + function () { + var set = new RRuleSet() -assertRecurring('testSetDateAndExDate', - set : rruleset() set.rdate(datetime(1997, 9, 2, 9)) set.rdate(datetime(1997, 9, 4, 9)) set.rdate(datetime(1997, 9, 9, 9)) @@ -2504,631 +3638,457 @@ assertRecurring('testSetDateAndExDate', set.exdate(datetime(1997, 9, 4, 9)) set.exdate(datetime(1997, 9, 11, 9)) set.exdate(datetime(1997, 9, 18, 9)) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 9, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testSetDateAndExRule', + function () { + var set = new RRuleSet() -assertRecurring('testSetDateAndExRule', - set : rruleset() set.rdate(datetime(1997, 9, 2, 9)) set.rdate(datetime(1997, 9, 4, 9)) set.rdate(datetime(1997, 9, 9, 9)) set.rdate(datetime(1997, 9, 11, 9)) set.rdate(datetime(1997, 9, 16, 9)) set.rdate(datetime(1997, 9, 18, 9)) - set.exrule(new RRule({freq: RRule.YEARLY, count: 3, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testSetCount', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 6, byweekday: [RRule.TU, RRule.TH], - dtstart: parse("19970902T090000"))) - set.exrule(new RRule({freq: RRule.YEARLY, count: 3, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - set.count(), 3) - -assertRecurring('testSetCachePre', - set : rruleset() - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000"))) - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testSetCachePost', - set : rruleset(cache: True) - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000"))) - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - for x in set: pass - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testSetCachePostInternal', - set : rruleset(cache: True) - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000"))) - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000"))) - for x in set: pass - set._cache), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testStr', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 3\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrType', isinstance(rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 3\n" - ), rrule), True) - -assertRecurring('testStrForceSetType', isinstance(rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 3\n" - , forceset: True), rruleset), True) - -assertRecurring('testStrSetType', isinstance(rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 2;BYDAY: RRule.TU\n" - "RRULE:FREQ: YEARLY;COUNT: 1;BYDAY: RRule.TH\n" - ), rruleset), True) - -assertRecurring('testStrCase', rrulestr( - "dtstart:19970902T090000\n" - "rrule:freq: yearly;count: 3\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrSpaces', rrulestr( - " DTSTART:19970902T090000 " - " RRULE:FREQ: YEARLY;COUNT: 3 " - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrSpacesAndLines', rrulestr( - " DTSTART:19970902T090000 \n" - " \n" - " RRULE:FREQ: YEARLY;COUNT: 3 \n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrNoDTStart', rrulestr( - "RRULE:FREQ: YEARLY;COUNT: 3\n" - , dtstart: parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrValueOnly', rrulestr( - "FREQ: YEARLY;COUNT: 3\n" - , dtstart: parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrUnfold', rrulestr( - "FREQ: YEA\n RLY;COUNT: 3\n", unfold: True, - dtstart: parse("19970902T090000")}), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -assertRecurring('testStrSet', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 2;BYDAY: RRule.TU\n" - "RRULE:FREQ: YEARLY;COUNT: 1;BYDAY: RRule.TH\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testStrSetDate', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 1;BYDAY: RRule.TU\n" - "RDATE:19970904T090000\n" - "RDATE:19970909T090000\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -assertRecurring('testStrSetExRule', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 6;BYDAY: RRule.TU, RRule.TH\n" - "EXRULE:FREQ: YEARLY;COUNT: 3;BYDAY: RRule.TH\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testStrSetExDate', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 6;BYDAY: RRule.TU, RRule.TH\n" - "EXDATE:19970904T090000\n" - "EXDATE:19970911T090000\n" - "EXDATE:19970918T090000\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testStrSetDateAndExDate', rrulestr( - "DTSTART:19970902T090000\n" - "RDATE:19970902T090000\n" - "RDATE:19970904T090000\n" - "RDATE:19970909T090000\n" - "RDATE:19970911T090000\n" - "RDATE:19970916T090000\n" - "RDATE:19970918T090000\n" - "EXDATE:19970904T090000\n" - "EXDATE:19970911T090000\n" - "EXDATE:19970918T090000\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testStrSetDateAndExRule', rrulestr( - "DTSTART:19970902T090000\n" - "RDATE:19970902T090000\n" - "RDATE:19970904T090000\n" - "RDATE:19970909T090000\n" - "RDATE:19970911T090000\n" - "RDATE:19970916T090000\n" - "RDATE:19970918T090000\n" - "EXRULE:FREQ: YEARLY;COUNT: 3;BYDAY: RRule.TH\n" - }), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -assertRecurring('testStrKeywords', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 3;INTERVAL: 3;" - "BYMONTH: 3;byweekday: RRule.TH;BYMONTHDAY: 3;" - "BYHOUR: 3;BYMINUTE: 3;BYSECOND: 3\n" - }), - [datetime(2033, 3, 3, 3, 3, 3), - datetime(2039, 3, 3, 3, 3, 3), - datetime(2072, 3, 3, 3, 3, 3)]) - -assertRecurring('testStrNWeekDay', rrulestr( - "DTSTART:19970902T090000\n" - "RRULE:FREQ: YEARLY;COUNT: 3;BYDAY: 1TU, -1TH\n" - }), - [datetime(1997, 12, 25, 9, 0), - datetime(1998, 1, 6, 9, 0), - datetime(1998, 12, 31, 9, 0)]) - */ -/* -assertRecurring('testBadBySetPos', - self.assertRaises(ValueError, - rrule, MONTHLY, - count: 1, - bysetpos: 0, - dtstart: parse("19970902T090000")) - -assertRecurring('testBadBySetPosMany', - self.assertRaises(ValueError, - rrule, MONTHLY, - count: 1, - bysetpos: [-1, 0, 1], - dtstart: parse("19970902T090000")) */ - -module("RRuleSet", { - - setup: function() { - - // Enable additional toString() / fromString() tests - //for each testRecurring(). - this.ALSO_TEST_STRING_FUNCTIONS = false; - - // Enable additional toText() / fromText() tests - // for each testRecurring(). - // Many of the tests fail because the conversion is only approximate, - // but it gives an idea about how well or bad it converts. - this.ALSO_TEST_NLP_FUNCTIONS = false; - - // Thorough after()/before()/between() tests. - // NOTE: can take a longer time. - this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true; - - } - -}); - -testRecurring('testSet',( - set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000")})), - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -testRecurring('testSetDate',( - set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TU, - dtstart: parse("19970902T090000")})), - set.rdate(datetime(1997, 9, 4, 9)), - set.rdate(datetime(1997, 9, 9, 9)), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) - -testRecurring('testSetExRule', - (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, count: 6, byweekday: [RRule.TU, RRule.TH], - dtstart: parse("19970902T090000")})), - set.exrule(new RRule({freq: RRule.YEARLY, count: 3, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -testRecurring('testSetExDate', - (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, count: 6, byweekday: [RRule.TU, RRule.TH], - dtstart: parse("19970902T090000")})), - set.exdate(datetime(1997, 9, 4, 9)), - set.exdate(datetime(1997, 9, 11, 9)), - set.exdate(datetime(1997, 9, 18, 9)), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -testRecurring('testSetExDateRevOrder', - (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.MONTHLY, count: 5, bymonthday: 10, - dtstart: parse("20040101T090000")})), - set.exdate(datetime(2004, 4, 10, 9, 0)), - set.exdate(datetime(2004, 2, 10, 9, 0)), - set), - [datetime(2004, 1, 10, 9, 0), - datetime(2004, 3, 10, 9, 0), - datetime(2004, 5, 10, 9, 0)]) - -testRecurring('testSetDateAndExDate', - (set = new RRuleSet(), - set.rdate(datetime(1997, 9, 2, 9)), - set.rdate(datetime(1997, 9, 4, 9)), - set.rdate(datetime(1997, 9, 9, 9)), - set.rdate(datetime(1997, 9, 11, 9)), - set.rdate(datetime(1997, 9, 16, 9)), - set.rdate(datetime(1997, 9, 18, 9)), - set.exdate(datetime(1997, 9, 4, 9)), - set.exdate(datetime(1997, 9, 11, 9)), - set.exdate(datetime(1997, 9, 18, 9)), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) - -testRecurring('testSetDateAndExRule', - (set = new RRuleSet(), - set.rdate(datetime(1997, 9, 2, 9)), - set.rdate(datetime(1997, 9, 4, 9)), - set.rdate(datetime(1997, 9, 9, 9)), - set.rdate(datetime(1997, 9, 11, 9)), - set.rdate(datetime(1997, 9, 16, 9)), - set.rdate(datetime(1997, 9, 18, 9)), - set.exrule(new RRule({freq: RRule.YEARLY, count: 3, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 3, + byweekday: RRule.TH, + dtstart: parse('19970902T090000') + })) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 9, 9, 0), + datetime(1997, 9, 16, 9, 0) + ] +) testRecurring('testSetCachePre', - (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000")})), - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 2, + byweekday: RRule.TU, + dtstart: parse('19970902T090000') + })) + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 1, + byweekday: RRule.TH, + dtstart: parse('19970902T090000') + })) + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) testRecurring('testSetCachePost', - (set = new RRuleSet(true), - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000")})), - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set.all(), - set), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 2, + byweekday: RRule.TU, + dtstart: parse('19970902T090000') + })) + set.rrule(new RRule({ + freq: RRule.YEARLY, + count: 1, + byweekday: RRule.TH, + dtstart: parse('19970902T090000') + })) + set.all() + return set + }, + [ + datetime(1997, 9, 2, 9, 0), + datetime(1997, 9, 4, 9, 0), + datetime(1997, 9, 9, 9, 0) + ] +) testRecurring('testSetInfiniteAll', - { - rrule: (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, dtstart: parse("19970902T090000")})), - set.exrule(new RRule({freq: RRule.YEARLY, count: 10, dtstart: parse("19970902T090000")})), - set), - method: 'all', - args: [function (date, count) { - return count < 3 - }] - }, - [ - datetime(2007, 9, 2, 9, 0), - datetime(2008, 9, 2, 9, 0), - datetime(2009, 9, 2, 9, 0) - ] -); + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + dtstart: parse('19970902T090000') + })) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 10, + dtstart: parse('19970902T090000') + })) + + return { + rrule: set, + method: 'all', + args: [ + function (date, count) { + return count < 3 + } + ] + } + }, + [ + datetime(2007, 9, 2, 9, 0), + datetime(2008, 9, 2, 9, 0), + datetime(2009, 9, 2, 9, 0) + ] +) testRecurring('testSetInfiniteBetween', - { - rrule: (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, dtstart: parse("19970902T090000")})), - set.exrule(new RRule({freq: RRule.YEARLY, count: 10, dtstart: parse("19970902T090000")})), - set), - method: 'between', - args: [datetime(2000, 9, 2, 9, 0), datetime(2010, 9, 2, 9, 0)] - }, - [ - datetime(2007, 9, 2, 9, 0), - datetime(2008, 9, 2, 9, 0), - datetime(2009, 9, 2, 9, 0) - ] -); + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + dtstart: parse('19970902T090000') + })) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 10, + dtstart: parse('19970902T090000') + })) + return { + rrule: set, + method: 'between', + args: [ + datetime(2000, 9, 2, 9, 0), + datetime(2010, 9, 2, 9, 0) + ] + } + }, + [ + datetime(2007, 9, 2, 9, 0), + datetime(2008, 9, 2, 9, 0), + datetime(2009, 9, 2, 9, 0) + ] +) testRecurring('testSetInfiniteBefore', - { - rrule: (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, dtstart: parse("19970902T090000")})), - set.exrule(new RRule({freq: RRule.YEARLY, count: 10, dtstart: parse("19970902T090000")})), - set), - method: 'before', - args: [datetime(2015, 9, 2, 9, 0), false] - }, - [ - datetime(2014, 9, 2, 9, 0) - ] -); + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + dtstart: parse('19970902T090000') + })) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 10, + dtstart: parse('19970902T090000') + })) + return { + rrule: set, + method: 'before', + args: [ + datetime(2015, 9, 2, 9, 0), + false + ] + } + }, + [ + datetime(2014, 9, 2, 9, 0) + ] +) testRecurring('testSetInfiniteAfter', - { - rrule: (set = new RRuleSet(), - set.rrule(new RRule({freq: RRule.YEARLY, dtstart: parse("19970902T090000")})), - set.exrule(new RRule({freq: RRule.YEARLY, count: 10, dtstart: parse("19970902T090000")})), - set), - method: 'after', - args: [datetime(2000, 9, 2, 9, 0), false] - }, - [ - datetime(2007, 9, 2, 9, 0) - ] -); - -/* -testRecurring('testSetCachePostInternal', - (set = new RRuleSet(true), - set.rrule(new RRule({freq: RRule.YEARLY, count: 2, byweekday: RRule.TU, - dtstart: parse("19970902T090000")})), - set.rrule(new RRule({freq: RRule.YEARLY, count: 1, byweekday: RRule.TH, - dtstart: parse("19970902T090000")})), - set.all(), - set._cache), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)])*/ - -module("rrulestr", { - - setup: function() { - - // Enable additional toString() / fromString() tests - //for each testRecurring(). - this.ALSO_TEST_STRING_FUNCTIONS = false; - - // Enable additional toText() / fromText() tests - // for each testRecurring(). - // Many of the tests fail because the conversion is only approximate, - // but it gives an idea about how well or bad it converts. - this.ALSO_TEST_NLP_FUNCTIONS = false; - - // Thorough after()/before()/between() tests. - // NOTE: can take a longer time. - this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true; - + function () { + var set = new RRuleSet() + + set.rrule(new RRule({ + freq: RRule.YEARLY, + dtstart: parse('19970902T090000') + })) + set.exrule(new RRule({ + freq: RRule.YEARLY, + count: 10, + dtstart: parse('19970902T090000') + })) + return { + rrule: set, + method: 'after', + args: [ + datetime(2000, 9, 2, 9, 0), + false + ] } + }, + [ + datetime(2007, 9, 2, 9, 0) + ] +) + +module('rrulestr', { + setup: function () { + // Enable additional toString() / fromString() tests + // for each testRecurring(). + this.ALSO_TEST_STRING_FUNCTIONS = false + + // Enable additional toText() / fromText() tests + // for each testRecurring(). + // Many of the tests fail because the conversion is only approximate, + // but it gives an idea about how well or bad it converts. + this.ALSO_TEST_NLP_FUNCTIONS = false + + // Thorough after()/before()/between() tests. + // NOTE: can take a longer time. + this.ALSO_TEST_BEFORE_AFTER_BETWEEN = true + } +}) -}); - -testRecurring('testStr', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=3\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1998, 9, 2, 9, 0), - datetimeUTC(1999, 9, 2, 9, 0)]) - -assertStrType('testStrType', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=3\n" - ), RRule) - -assertStrType('testStrForceSetType', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=3\n", { - forceset: true -}), RRuleSet) - -assertStrType('testStrSetType', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n" + - "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n" -), RRuleSet) - -testRecurring('testStrCase', rrulestr( - "dtstart:19970902T090000Z\n" + - "rrule:freq=yearly;count=3\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1998, 9, 2, 9, 0), - datetimeUTC(1999, 9, 2, 9, 0)]) - -testRecurring('testStrSpaces', rrulestr( - " DTSTART:19970902T090000Z " + - " RRULE:FREQ=YEARLY;COUNT=3 " - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1998, 9, 2, 9, 0), - datetimeUTC(1999, 9, 2, 9, 0)]) - - -testRecurring('testStrSpacesAndLines', rrulestr( - " DTSTART:19970902T090000Z \n"+ - " \n RRULE:FREQ=YEARLY;COUNT=3 \n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1998, 9, 2, 9, 0), - datetimeUTC(1999, 9, 2, 9, 0)]) - - -testRecurring('testStrNoDTStart', rrulestr( - "RRULE:FREQ=YEARLY;COUNT=3\n", - {dtstart: parse("19970902T090000")} - ), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -testRecurring('testStrValueOnly', rrulestr( - "FREQ=YEARLY;COUNT=3\n", - { dtstart: parse("19970902T090000") } - ), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -testRecurring('testStrUnfold', rrulestr( - "FREQ=YEA\n RLY;COUNT=3\n", - { - unfold: true, - dtstart: parse("19970902T090000") - } - ), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) - -testRecurring('testStrSet', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n" + - "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 4, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0)]) - -testRecurring('testStrSetDate', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TU\n" + - "RDATE:19970904T090000Z\n" + - "RDATE:19970909T090000Z\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 4, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0)]) - -testRecurring('testStrSetExRule', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n" + - "EXRULE:FREQ=YEARLY;COUNT=3;BYDAY=TH\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0), - datetimeUTC(1997, 9, 16, 9, 0)]) - -testRecurring('testStrSetExDate', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n" + - "EXDATE:19970904T090000Z\n" + - "EXDATE:19970911T090000Z\n" + - "EXDATE:19970918T090000Z\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0), - datetimeUTC(1997, 9, 16, 9, 0)]) - -testRecurring('testStrSetDateAndExDate', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RDATE:19970902T090000Z\n" + - "RDATE:19970904T090000Z\n" + - "RDATE:19970909T090000Z\n" + - "RDATE:19970911T090000Z\n" + - "RDATE:19970916T090000Z\n" + - "RDATE:19970918T090000Z\n" + - "EXDATE:19970904T090000Z\n" + - "EXDATE:19970911T090000Z\n" + - "EXDATE:19970918T090000Z\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0), - datetimeUTC(1997, 9, 16, 9, 0)]) - -testRecurring('testStrSetDateAndExRule', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RDATE:19970902T090000Z\n" + - "RDATE:19970904T090000Z\n" + - "RDATE:19970909T090000Z\n" + - "RDATE:19970911T090000Z\n" + - "RDATE:19970916T090000Z\n" + - "RDATE:19970918T090000Z\n" + - "EXRULE:FREQ=YEARLY;COUNT=3;BYDAY=TH\n" - ), - [datetimeUTC(1997, 9, 2, 9, 0), - datetimeUTC(1997, 9, 9, 9, 0), - datetimeUTC(1997, 9, 16, 9, 0)]) -/* -testRecurring('testStrKeywords', rrulestr( - "DTSTART:19970902T030000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=3;INTERVAL=3;" + - "BYMONTH=3;byweekday=TH;BYMONTHDAY=3;" + - "BYHOUR=3;BYMINUTE=3;BYSECOND=3\n" - ), - [datetimeUTC(2033, 3, 3, 3, 3, 3), - datetimeUTC(2039, 3, 3, 3, 3, 3), - datetimeUTC(2072, 3, 3, 3, 3, 3)])*/ - -testRecurring('testStrNWeekDay', rrulestr( - "DTSTART:19970902T090000Z\n" + - "RRULE:FREQ=YEARLY;COUNT=3;BYDAY=1TU,-1TH\n" - ), - [datetimeUTC(1997, 12, 25, 9, 0), - datetimeUTC(1998, 1, 6, 9, 0), - datetimeUTC(1998, 12, 31, 9, 0)]) +assertStrType('testStrType', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=3\n' + ), + RRule +) + +assertStrType('testStrForceSetType', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=3\n', + { + forceset: true + } + ), + RRuleSet +) + +assertStrType('testStrSetType', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n' + + 'RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n' + ), + RRuleSet +) + +testRecurring('testStr', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=3\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrCase', + rrulestr( + 'dtstart:19970902T090000Z\n' + + 'rrule:freq=yearly;count=3\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrSpaces', + rrulestr( + ' DTSTART:19970902T090000Z ' + + ' RRULE:FREQ=YEARLY;COUNT=3 ' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrSpacesAndLines', + rrulestr( + ' DTSTART:19970902T090000Z \n' + + ' \n RRULE:FREQ=YEARLY;COUNT=3 \n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrNoDTStart', + rrulestr( + 'RRULE:FREQ=YEARLY;COUNT=3\n', + { + dtstart: parse('19970902T090000') + } + ), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1998, 9, 2, 9, 0), + datetime(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrValueOnly', + rrulestr( + 'FREQ=YEARLY;COUNT=3\n', + { + dtstart: parse('19970902T090000') + } + ), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1998, 9, 2, 9, 0), + datetime(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrUnfold', + rrulestr( + 'FREQ=YEA\n RLY;COUNT=3\n', + { + unfold: true, + dtstart: parse('19970902T090000') + } + ), + [ + datetime(1997, 9, 2, 9, 0), + datetime(1998, 9, 2, 9, 0), + datetime(1999, 9, 2, 9, 0) + ] +) + +testRecurring('testStrSet', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n' + + 'RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 4, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testStrSetDate', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TU\n' + + 'RDATE:19970904T090000Z\n' + + 'RDATE:19970909T090000Z\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 4, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0) + ] +) + +testRecurring('testStrSetExRule', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n' + + 'EXRULE:FREQ=YEARLY;COUNT=3;BYDAY=TH\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testStrSetExDate', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n' + + 'EXDATE:19970904T090000Z\n' + + 'EXDATE:19970911T090000Z\n' + + 'EXDATE:19970918T090000Z\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testStrSetDateAndExDate', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RDATE:19970902T090000Z\n' + + 'RDATE:19970904T090000Z\n' + + 'RDATE:19970909T090000Z\n' + + 'RDATE:19970911T090000Z\n' + + 'RDATE:19970916T090000Z\n' + + 'RDATE:19970918T090000Z\n' + + 'EXDATE:19970904T090000Z\n' + + 'EXDATE:19970911T090000Z\n' + + 'EXDATE:19970918T090000Z\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0) + ] +) + +testRecurring('testStrSetDateAndExRule', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RDATE:19970902T090000Z\n' + + 'RDATE:19970904T090000Z\n' + + 'RDATE:19970909T090000Z\n' + + 'RDATE:19970911T090000Z\n' + + 'RDATE:19970916T090000Z\n' + + 'RDATE:19970918T090000Z\n' + + 'EXRULE:FREQ=YEARLY;COUNT=3;BYDAY=TH\n' + ), + [ + datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0) + ] +) + +testRecurring.skip('testStrKeywords', + rrulestr( + 'DTSTART:19970902T030000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=3;INTERVAL=3;' + + 'BYMONTH=3;byweekday=TH;BYMONTHDAY=3;' + + 'BYHOUR=3;BYMINUTE=3;BYSECOND=3\n' + ), + [ + datetimeUTC(2033, 3, 3, 3, 3, 3), + datetimeUTC(2039, 3, 3, 3, 3, 3), + datetimeUTC(2072, 3, 3, 3, 3, 3) + ] +) + +testRecurring('testStrNWeekDay', + rrulestr( + 'DTSTART:19970902T090000Z\n' + + 'RRULE:FREQ=YEARLY;COUNT=3;BYDAY=1TU,-1TH\n' + ), + [ + datetimeUTC(1997, 12, 25, 9, 0), + datetimeUTC(1998, 1, 6, 9, 0), + datetimeUTC(1998, 12, 31, 9, 0) + ] +) diff --git a/tests/utils.js b/tests/utils.js index 157916fd..0d58dfb7 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,207 +1,193 @@ - -/** - * datetime.date/datetime.datetime - */ -var date, datetime, datetimeUTC; -date = datetime = function(y, m, d, h, i, s) { - h = h || 0; - i = i || 0; - s = s || 0; - return new Date(y, m - 1, d, h, i, s); -}; - -datetimeUTC = function(y, m, d, h, i, s) { - h = h || 0; - i = i || 0; - s = s || 0; - return new Date(Date.UTC(y, m - 1, d, h, i, s)); -}; - - -/** - * dateutil.parser.parse - */ -var parse = function(str) { - var parts = str.match(/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/); - var y = parts[1], m = parts[2], - d = parts[3], h = parts[4], - i = parts[5], s = parts[6]; - m = Number(m[0] == '0' ? m[1]: m) - 1; - d = d[0] == '0' ? d[1]: d; - h = h[0] == '0' ? h[1]: h; - i = i[0] == '0' ? i[1]: i; - s = s[0] == '0' ? s[1]: s; - return new Date(y, m, d, h, i, s); -}; - - -var assertDatesEqual = function(actual, expected, msg) { - msg = msg ? ' [' + msg + '] ' : ''; - - if (!(actual instanceof Array)) { - actual = [actual]; - } - - if (!(expected instanceof Array)) { - expected = [expected]; - } +/* global test, equal, deepEqual, ok, RRule, RRuleSet */ + +;(function (root) { + /** + * datetime.datetime + */ + root.datetime = function (y, m, d, h, i, s) { + h = h || 0 + i = i || 0 + s = s || 0 + return new Date(y, m - 1, d, h, i, s) + } + + root.datetimeUTC = function (y, m, d, h, i, s) { + h = h || 0 + i = i || 0 + s = s || 0 + return new Date(Date.UTC(y, m - 1, d, h, i, s)) + } + + /** + * dateutil.parser.parse + */ + root.parse = function (str) { + var parts = str.match(/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/) + var y = parts[1] + var m = parts[2] + var d = parts[3] + var h = parts[4] + var i = parts[5] + var s = parts[6] + m = Number(m[0] === '0' ? m[1] : m) - 1 + d = d[0] === '0' ? d[1] : d + h = h[0] === '0' ? h[1] : h + i = i[0] === '0' ? i[1] : i + s = s[0] === '0' ? s[1] : s + return new Date(y, m, d, h, i, s) + } + + var assertDatesEqual = root.assertDatesEqual = function (actual, expected, msg) { + msg = msg ? ' [' + msg + '] ' : '' + + if (!(actual instanceof Array)) actual = [actual] + if (!(expected instanceof Array)) expected = [expected] if (expected.length > 1) { - equal(actual.length, expected.length, - msg + 'number of recurrences'); - msg = ' - '; + equal(actual.length, expected.length, msg + 'number of recurrences') + msg = ' - ' } for (var exp, act, i = 0; i < expected.length; i++) { - act = actual[i]; - exp = expected[i]; - equal(exp instanceof Date ? exp.toString() : exp, - act.toString(), msg + (i + 1) + '/' + expected.length); + act = actual[i] + exp = expected[i] + equal(exp instanceof Date ? exp.toString() : exp, + act.toString(), msg + (i + 1) + '/' + expected.length) } -}; + } -var extractTime = function(date) { - return date != null ? date.getTime() : void 0; -} + var extractTime = function (date) { + return date != null ? date.getTime() : void 0 + } -var testRecurring = function(msg, rruleOrObjOrRRuleSetObj, expectedDates) { - console.log(msg) - var rruleOrRRuleSet, method, args; + root.testRecurring = function (msg, rruleOrObjOrRRuleSetObj, expectedDates) { + var rruleOrRRuleSet, method, args + + if (typeof rruleOrObjOrRRuleSetObj === 'function') { + rruleOrObjOrRRuleSetObj = rruleOrObjOrRRuleSetObj() + } if (rruleOrObjOrRRuleSetObj instanceof RRule || rruleOrObjOrRRuleSetObj instanceof RRuleSet) { - rruleOrRRuleSet = rruleOrObjOrRRuleSetObj; - method = 'all'; - args = []; + rruleOrRRuleSet = rruleOrObjOrRRuleSetObj + method = 'all' + args = [] } else { - rruleOrRRuleSet = rruleOrObjOrRRuleSetObj.rrule; - method = rruleOrObjOrRRuleSetObj.method; - args = rruleOrObjOrRRuleSetObj.args; + rruleOrRRuleSet = rruleOrObjOrRRuleSetObj.rrule + method = rruleOrObjOrRRuleSetObj.method + args = rruleOrObjOrRRuleSetObj.args } // Use text and string representation of the rrule as the message. if (rruleOrRRuleSet instanceof RRule) { - msg = msg + ' [' + - (rruleOrRRuleSet.isFullyConvertibleToText() ? rruleOrRRuleSet.toText() : 'no text repr') + - ']' + - ' [' + rruleOrRRuleSet.toString() + ']'; + msg = msg + ' [' + + (rruleOrRRuleSet.isFullyConvertibleToText() ? rruleOrRRuleSet.toText() : 'no text repr') + + ']' + ' [' + rruleOrRRuleSet.toString() + ']' } else { - msg = msg + rruleOrRRuleSet.toString(); + msg = msg + rruleOrRRuleSet.toString() } - test(msg, function() { - var time = Date.now(); - var actualDates = rruleOrRRuleSet[method].apply(rruleOrRRuleSet, args); - time = Date.now() - time - - equal(time < 100, true, - rruleOrRRuleSet + '\' method "' + method + '" should finish in 100 ms, but ' + time + ' ms'); + test(msg, function () { + var time = Date.now() + var actualDates = rruleOrRRuleSet[method].apply(rruleOrRRuleSet, args) + time = Date.now() - time - if (!(actualDates instanceof Array)) actualDates = [actualDates]; - if (!(expectedDates instanceof Array)) expectedDates = [expectedDates]; + equal(time < 100, true, + rruleOrRRuleSet + '\' method "' + method + '" should finish in 100 ms, but ' + time + ' ms') - assertDatesEqual(actualDates, expectedDates); + if (!(actualDates instanceof Array)) actualDates = [actualDates] + if (!(expectedDates instanceof Array)) expectedDates = [expectedDates] - // Additional tests using the expected dates - // ========================================================== + assertDatesEqual(actualDates, expectedDates) - if(this.ALSO_TEST_SUBSECOND_PRECISION) + // Additional tests using the expected dates + // ========================================================== - deepEqual( actualDates.map(extractTime), expectedDates.map(extractTime)); - if (this.ALSO_TEST_STRING_FUNCTIONS) { - // Test toString()/fromString() - var string = rruleOrRRuleSet.toString(); - var rrule2 = RRule.fromString(string, rruleOrRRuleSet.options.dtstart); - var string2 = rrule2.toString(); - equal(string, string2, - 'toString() == fromString(toString()).toString()'); - if (method == 'all') { - assertDatesEqual( - rrule2.all(), - expectedDates, - 'fromString().all()' - ); - } + if (this.ALSO_TEST_SUBSECOND_PRECISION) { + deepEqual(actualDates.map(extractTime), expectedDates.map(extractTime)) + } + if (this.ALSO_TEST_STRING_FUNCTIONS) { + // Test toString()/fromString() + var string = rruleOrRRuleSet.toString() + var rrule2 = RRule.fromString(string, rruleOrRRuleSet.options.dtstart) + var string2 = rrule2.toString() + equal(string, string2, 'toString() == fromString(toString()).toString()') + if (method === 'all') { + assertDatesEqual(rrule2.all(), expectedDates, 'fromString().all()') } - - - if (this.ALSO_TEST_NLP_FUNCTIONS && rruleOrRRuleSet.isFullyConvertibleToText()) { - // Test fromText()/toText(). - var text = rruleOrRRuleSet.toText(); - var rrule2 = RRule.fromText(text, rruleOrRRuleSet.options.dtstart); - var text2 = rrule2.toText(); - equal(text2, text, 'toText() == fromText(toText()).toText()'); - - // Test fromText()/toString(). - var text = rruleOrRRuleSet.toText(); - var rrule3 = RRule.fromText(text, rruleOrRRuleSet.options.dtstart); - var string3 = rrule2.toString(); - equal(string3, string, - 'toString() == fromText(toText()).toString()'); + } + + if (this.ALSO_TEST_NLP_FUNCTIONS && rruleOrRRuleSet.isFullyConvertibleToText()) { + // Test fromText()/toText(). + var text = rruleOrRRuleSet.toText() + var text2 = rrule2.toText() + rrule2 = RRule.fromText(text, rruleOrRRuleSet.options.dtstart) + equal(text2, text, 'toText() == fromText(toText()).toText()') + + // Test fromText()/toString(). + text = rruleOrRRuleSet.toText() + var rrule3 = RRule.fromText(text, rruleOrRRuleSet.options.dtstart) + equal(rrule3.toString(), string, 'toString() == fromText(toText()).toString()') + } + + if (method === 'all' && this.ALSO_TEST_BEFORE_AFTER_BETWEEN) { + // Test before, after, and between - use the expected dates. + // create a clean copy of the rrule object to bypass caching + rruleOrRRuleSet = rruleOrRRuleSet.clone() + + if (expectedDates.length > 2) { + // Test between() + assertDatesEqual( + rruleOrRRuleSet.between( + expectedDates[0], + expectedDates[expectedDates.length - 1], + true + ), + expectedDates, + 'between, inc=true' + ) + + assertDatesEqual( + rruleOrRRuleSet.between( + expectedDates[0], + expectedDates[expectedDates.length - 1], + false + ), + expectedDates.slice(1, expectedDates.length - 1), + 'between, inc=false' + ) } - - if (method == 'all' && this.ALSO_TEST_BEFORE_AFTER_BETWEEN) { - - // Test before, after, and between - use the expected dates. - - // create a clean copy of the rrule object to bypass caching - rruleOrRRuleSet = rruleOrRRuleSet.clone(); - - if (expectedDates.length > 2) { - // Test between() - assertDatesEqual( - rruleOrRRuleSet.between( - expectedDates[0], - expectedDates[expectedDates.length - 1], - true - ), - expectedDates, - 'between, inc=true' - ); - assertDatesEqual( - rruleOrRRuleSet.between( - expectedDates[0], - expectedDates[expectedDates.length - 1], - false - ), - expectedDates.slice(1, expectedDates.length - 1), - 'between, inc=false' - ); - } - - if (expectedDates.length > 1) { - - for (var date, next, prev, i = 0; - i < expectedDates.length; i++) - { - - date = expectedDates[i]; - next = expectedDates[i + 1]; - prev = expectedDates[i - 1]; - - // Test after() and before() with inc=true. - assertDatesEqual( - rruleOrRRuleSet.after(date, true), date, 'after, inc=true'); - assertDatesEqual( - rruleOrRRuleSet.before(date, true), date, 'before, inc=true'); - - // Test after() and before() with inc=false. - next && assertDatesEqual( - rruleOrRRuleSet.after(date, false), next, 'after, inc=false'); - prev && assertDatesEqual( - rruleOrRRuleSet.before(date, false), prev, 'before, inc=false'); - - } - } - + if (expectedDates.length > 1) { + for (var date, next, prev, i = 0; i < expectedDates.length; i++) { + date = expectedDates[i] + next = expectedDates[i + 1] + prev = expectedDates[i - 1] + + // Test after() and before() with inc=true. + assertDatesEqual( + rruleOrRRuleSet.after(date, true), date, 'after, inc=true') + assertDatesEqual( + rruleOrRRuleSet.before(date, true), date, 'before, inc=true') + + // Test after() and before() with inc=false. + next && assertDatesEqual( + rruleOrRRuleSet.after(date, false), next, 'after, inc=false') + prev && assertDatesEqual( + rruleOrRRuleSet.before(date, false), prev, 'before, inc=false') + } } + } + }) + } - }); -}; + root.testRecurring.skip = function (msg) { + console.warn('Skip ' + msg + '.') + } -var assertStrType = function (msg, rruleOrrruleSet, type) { + root.assertStrType = function (msg, rruleOrrruleSet, type) { test(msg, function () { - ok(rruleOrrruleSet instanceof type) + ok(rruleOrrruleSet instanceof type) }) -} + } +}(this))