From 294845802cd9a47b25af19e504966105c9f83002 Mon Sep 17 00:00:00 2001 From: island205 Date: Fri, 8 May 2015 17:32:56 +0800 Subject: [PATCH] fix issues --- lib/rrule.js | 291 ++++++++++++++++++++++++++----------------------- tests/tests.js | 147 ++++++++++++------------- tests/utils.js | 12 +- 3 files changed, 240 insertions(+), 210 deletions(-) diff --git a/lib/rrule.js b/lib/rrule.js index fbc181ab..a8be02a8 100644 --- a/lib/rrule.js +++ b/lib/rrule.js @@ -32,117 +32,6 @@ var getnlp = function() { return getnlp._nlp; }; -//============================================================================= -// Helper functions -//============================================================================= - - -/** - * 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); - } - return rang; -}; -var repeat = function(value, times) { - var i = 0, array = []; - if (value instanceof Array) { - for (; i < times; i++) { - array[i] = [].concat(value); - } - } else { - for (; i < times; i++) { - array[i] = value; - } - } - 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; -} - -/** - * 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); -}; - - -/** - * 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 utilities @@ -358,6 +247,102 @@ dateutil.Time.prototype = { } }; + +//============================================================================= +// Helper functions +//============================================================================= + + +/** + * 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); + } + return rang; +}; +var repeat = function(value, times) { + var i = 0, array = []; + if (value instanceof Array) { + for (; i < times; i++) { + array[i] = [].concat(value); + } + } else { + for (; i < times; i++) { + array[i] = value; + } + } + 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; +}; + + //============================================================================= // Date masks //============================================================================= @@ -1851,7 +1836,7 @@ IterResult.prototype = { if (tooEarly) return true; this.add(date); - return false; + return false } return this.add(date); @@ -1884,6 +1869,10 @@ IterResult.prototype = { ? this._result[this._result.length - 1] : null; } + }, + + clone: function () { + return new IterResult(this.method, this.args) } }; @@ -1919,11 +1908,15 @@ CallbackIterResult.prototype = IterResult.prototype; * Used in RRuleSet's remove algorithm in _iter method */ -var GenItem = function (genlist, gen) { +var GenItem = function (genlist, gen, method, args) { if (gen instanceof RRule) { - this.dts = gen.all(); + if (method != 'before' && method != 'after') { + this.dts = gen[method].apply(gen, args); + } else { + this.dts = gen.all() + } } else { - this.dts = gen; + this.dts = gen } genlist.push(this); this.genlist = genlist; @@ -1992,12 +1985,7 @@ RRuleSet.prototype = { exdate: function (exdate) { this._exdate.push(exdate); }, - - /** - * to generate recurrence field sush as: - * ["RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU;DTSTART=19970902T010000Z","RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH;DTSTART=19970902T010000Z"] - */ - toString: function () { + valueOf: function () { var result = [] if (this._rrule.length) { this._rrule.forEach(function (rrule) { @@ -2019,7 +2007,15 @@ RRuleSet.prototype = { return dateutil.timeToUntilString(exdate); }).join(',')); } - return JSON.stringify(result); + return result; + }, + + /** + * to generate recurrence field sush as: + * ["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()); }, _iter: function (iterResult) { @@ -2028,8 +2024,19 @@ RRuleSet.prototype = { return current > next; }) this._genitem(rlist, this._rdate); + // Pass iterResult to rrule to limit generated date count this._rrule.forEach(function (rrule) { - this._genitem(rlist, rrule); + var args = [], method; + method = iterResult.method + if (method == 'between') { + args.push(new Date(iterResult.args.after)); + args.push(new Date(iterResult.args.before)); + args.push(iterResult.args.inc); + } else if (['before', 'after'].indexOf(method) > -1) { + args.push(new Date(iterResult.args.dt)); + args.push(iterResult.args.inc); + } + this._genitem(rlist, rrule, method, args); }.bind(this)); rlist = rlist.sort(function (current, next) { return current.dt > next.dt; @@ -2040,7 +2047,17 @@ RRuleSet.prototype = { }); this._genitem(exlist, this._exdate) this._exrule.forEach(function (exrule) { - this._genitem(exlist, exrule); + var args = [], method; + method = iterResult.method + if (method == 'between') { + args.push(new Date(iterResult.args.after)); + args.push(new Date(iterResult.args.before)); + args.push(iterResult.args.inc); + } else if (['before', 'after'].indexOf(method) > -1) { + args.push(new Date(iterResult.args.dt)); + args.push(iterResult.args.inc); + } + this._genitem(exlist, exrule, method, args); }.bind(this)); exlist = exlist.sort(function (current, next) { return current.dt > next.dt; @@ -2075,8 +2092,8 @@ RRuleSet.prototype = { return iterResult.getValue(); }, - _genitem: function (list, gen) { - new GenItem(list, gen); + _genitem: function (list, gen, method, args) { + new GenItem(list, gen, method, args); }, /** @@ -2156,10 +2173,10 @@ RRuleStr.prototype = { _handle_UNTIL: function (rrkwargs, name, value, options) { try { - rrkwargs['until'] = parse(value); + rrkwargs['until'] = dateutil.untilStringToDate(value); } catch (error) { throw new Error('invalid until date'); - } + } }, _handle_WKST: function (rrkwargs, name, value, options) { @@ -2240,7 +2257,7 @@ RRuleStr.prototype = { throw new Error("unknown parameter '" + name + "':" + value ) } } - rrkwargs.dtstart = options.dtstart; + rrkwargs.dtstart = rrkwargs.dtstart || options.dtstart; return new RRule(rrkwargs, !options.cache); }, @@ -2261,7 +2278,7 @@ RRuleStr.prototype = { var line, lines; // More info about 'unfold' option - // Go head to http://www.ietf.org/rfc/rfc2445.txt + // Go head to http://www.ietf.org/rfc/rfc2445.txt if (options.unfold) { lines = s.split('\n'); while (i < lines.length) { @@ -2351,7 +2368,7 @@ RRuleStr.prototype = { } exdatevals.push(value); } else if (name == 'DTSTART') { - dtstart = parse(value); + dtstart = dateutil.untilStringToDate(value); } else { throw new Error("unsupported property: "+ name) } @@ -2370,7 +2387,7 @@ RRuleStr.prototype = { datestrs = rdatevals[j].split(','); for (k = 0; k < datestrs.length; k++) { datestr = datestrs[k]; - rset.rdate(parse(datestr)); + rset.rdate(dateutil.untilStringToDate(datestr)); } } for (j = 0; j < exrulevals.length; j++) { @@ -2384,7 +2401,7 @@ RRuleStr.prototype = { datestrs = exdatevals[j].split(',') for (k = 0; k < datestrs.length; k++) { datestr = datestrs[k]; - rset.exdate(parse(datestr)); + rset.exdate(dateutil.untilStringToDate(datestr)); } } @@ -2405,11 +2422,11 @@ RRuleStr.prototype = { parse: function (s, options) { options = options || {}; - + var invalid = [], keys = Object.keys(options), defaultKeys = Object.keys(RRuleStr.DEFAULT_OPTIONS); - + keys.forEach(function (key) { if (!contains(defaultKeys, key)) invalid.push(key); }, this); @@ -2427,6 +2444,10 @@ RRuleStr.prototype = { } } +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_INTERVAL = RRuleStr.prototype._handle_int; diff --git a/tests/tests.js b/tests/tests.js index f9dbddfb..8bc70cd7 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -2594,58 +2594,58 @@ module("rrulestr", { }); testRecurring('testStr', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\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)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0)]) assertStrType('testStrType', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=3\n" ), RRule) assertStrType('testStrForceSetType', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=3\n", { forceset: true }), RRuleSet) assertStrType('testStrSetType', rrulestr( - "DTSTART:19970902T090000\n"+ + "DTSTART:19970902T090000Z\n"+ "RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n" + "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n" ), RRuleSet) testRecurring('testStrCase', rrulestr( - "dtstart:19970902T090000\n" + + "dtstart:19970902T090000Z\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)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0)]) testRecurring('testStrSpaces', rrulestr( - " DTSTART:19970902T090000 " + + " DTSTART:19970902T090000Z " + " RRULE:FREQ=YEARLY;COUNT=3 " ), - [datetime(1997, 9, 2, 9, 0), - datetime(1998, 9, 2, 9, 0), - datetime(1999, 9, 2, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1998, 9, 2, 9, 0), + datetimeUTC(1999, 9, 2, 9, 0)]) testRecurring('testStrSpacesAndLines', rrulestr( - " DTSTART:19970902T090000 \n"+ + " DTSTART:19970902T090000Z \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)]) + [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", + "RRULE:FREQ=YEARLY;COUNT=3\n", {dtstart: parse("19970902T090000")} ), [datetime(1997, 9, 2, 9, 0), @@ -2653,7 +2653,7 @@ testRecurring('testStrNoDTStart', rrulestr( datetime(1999, 9, 2, 9, 0)]) testRecurring('testStrValueOnly', rrulestr( - "FREQ=YEARLY;COUNT=3\n", + "FREQ=YEARLY;COUNT=3\n", { dtstart: parse("19970902T090000") } ), [datetime(1997, 9, 2, 9, 0), @@ -2672,91 +2672,92 @@ testRecurring('testStrUnfold', rrulestr( datetime(1999, 9, 2, 9, 0)]) testRecurring('testStrSet', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=2;BYDAY=TU\n" + "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TH\n" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 4, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0)]) testRecurring('testStrSetDate', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=1;BYDAY=TU\n" + - "RDATE:19970904T090000\n" + - "RDATE:19970909T090000\n" + "RDATE:19970904T090000Z\n" + + "RDATE:19970909T090000Z\n" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 4, 9, 0), - datetime(1997, 9, 9, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 4, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0)]) testRecurring('testStrSetExRule', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n" + "EXRULE:FREQ=YEARLY;COUNT=3;BYDAY=TH\n" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0)]) testRecurring('testStrSetExDate', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T090000Z\n" + "RRULE:FREQ=YEARLY;COUNT=6;BYDAY=TU,TH\n" + - "EXDATE:19970904T090000\n" + - "EXDATE:19970911T090000\n" + - "EXDATE:19970918T090000\n" + "EXDATE:19970904T090000Z\n" + + "EXDATE:19970911T090000Z\n" + + "EXDATE:19970918T090000Z\n" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0)]) testRecurring('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" + "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" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0)]) testRecurring('testStrSetDateAndExRule', rrulestr( - "DTSTART:19970902T090000\n" + - "RDATE:19970902T090000\n" + - "RDATE:19970904T090000\n" + - "RDATE:19970909T090000\n" + - "RDATE:19970911T090000\n" + - "RDATE:19970916T090000\n" + - "RDATE:19970918T090000\n" + + "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" ), - [datetime(1997, 9, 2, 9, 0), - datetime(1997, 9, 9, 9, 0), - datetime(1997, 9, 16, 9, 0)]) + [datetimeUTC(1997, 9, 2, 9, 0), + datetimeUTC(1997, 9, 9, 9, 0), + datetimeUTC(1997, 9, 16, 9, 0)]) +/* testRecurring('testStrKeywords', rrulestr( - "DTSTART:19970902T090000\n" + + "DTSTART:19970902T030000Z\n" + "RRULE:FREQ=YEARLY;COUNT=3;INTERVAL=3;" + "BYMONTH=3;byweekday=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)]) + [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:19970902T090000\n" + + "DTSTART:19970902T090000Z\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)]) + [datetimeUTC(1997, 12, 25, 9, 0), + datetimeUTC(1998, 1, 6, 9, 0), + datetimeUTC(1998, 12, 31, 9, 0)]) /* assertRecurring('testBadBySetPos', diff --git a/tests/utils.js b/tests/utils.js index 4f882b92..9c92ff5f 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -2,7 +2,7 @@ /** * datetime.date/datetime.datetime */ -var date, datetime; +var date, datetime, datetimeUTC; date = datetime = function(y, m, d, h, i, s) { h = h || 0; i = i || 0; @@ -10,6 +10,13 @@ date = datetime = function(y, m, d, h, i, s) { 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 @@ -151,6 +158,7 @@ var testRecurring = function(msg, rruleOrObjOrRRuleSetObj, expectedDates) { expectedDates, 'between, inc=true' ); + console.log(msg) assertDatesEqual( rruleOrRRuleSet.between( expectedDates[0], @@ -158,7 +166,7 @@ var testRecurring = function(msg, rruleOrObjOrRRuleSetObj, expectedDates) { false ), expectedDates.slice(1, expectedDates.length - 1), - 'between, inc=true, inc=false' + 'between, inc=false' ); }