Comparing version 0.3.2 to 0.4.1
@@ -271,6 +271,10 @@ // Copyright (C) 2011 Tri Tech Computers Ltd. | ||
VEvent.prototype.inTimeRange = function(start, end) { | ||
// TODO: Add support for RRULEs here | ||
var dtstart = this.getPropertyValue('DTSTART'); | ||
var dtend = this.getPropertyValue('DTEND'); | ||
var rr = this.getPropertyValue('RRULE'); | ||
if(rr) { | ||
rr = new RRule(rr, dtstart, dtend); | ||
var next = rr.next(start); | ||
return (next !== null && (!end || next <= end)); | ||
} | ||
@@ -300,3 +304,16 @@ if(!dtend) { | ||
VTimezone.prototype.getRRule = function(section) { | ||
var comp = this.getComponents('STANDARD')[0]; | ||
if(!comp || !comp.getPropertyValue('RRULE')) | ||
return null; | ||
return new RRule(comp.getPropertyValue('RRULE'), | ||
comp.getPropertyValue('DTSTART'), | ||
comp.getPropertyValue('DTEND')); | ||
} | ||
VTimezone.prototype.getOffsetForDate = function(dt) { | ||
if(!this.getComponents('DAYLIGHT').length) | ||
return this.getComponents('STANDARD').getPropertyValue('TZOFFSETTO'); | ||
// Right now we're only supporting a single element | ||
@@ -306,11 +323,8 @@ assert.equal(1, this.components['STANDARD'].length); | ||
var std = this.components['STANDARD'][0]; | ||
var dst = this.components['DAYLIGHT'][0]; | ||
var next_std = std.getPropertyValue('RRULE').nextOccurs(std.getPropertyValue('DTSTART'), dt); | ||
var next_dst = dst.getPropertyValue('RRULE').nextOccurs(dst.getPropertyValue('DTSTART'), dt); | ||
var next_std = this.getRRule('STANDARD').next(dt); | ||
var next_dst = this.getRRule('DAYLIGHT').next(dt); | ||
// TODO: Using prevOccurs would be a better solution | ||
if(next_std < next_dst) | ||
return dst.getPropertyValue('TZOFFSETTO'); | ||
return std.getPropertyValue('TZOFFSETTO'); | ||
return this.getComponents(next_std < next_dst ? 'STANDARD' : 'DAYLIGHT')[0] | ||
.getPropertyValue('TZOFFSETTO'); | ||
} | ||
@@ -317,0 +331,0 @@ |
363
lib/recur.js
@@ -29,2 +29,6 @@ // Copyright (C) 2011 Tri Tech Computers Ltd. | ||
var types = require('./types'); | ||
var SUPPORTED_PARTS = ['FREQ','INTERVAL','COUNT','UNTIL','BYDAY','BYMONTH','BYMONTHDAY']; | ||
var WKDAYS = ['SU','MO','TU','WE','TH','FR','SA']; | ||
@@ -55,19 +59,26 @@ | ||
function set_y(dt, v) { dt.setUTCFullYear(v); } | ||
function set_m(dt, v) { dt.setUTCMonth(v-1); } | ||
function set_d(dt, v) { dt.setUTCDate(v); } | ||
function set_hr(dt, v) { dt.setUTCHours(v); } | ||
function set_min(dt, v) { dt.setUTCMinutes(v); } | ||
function set_sec(dt, v) { dt.setUTCSeconds(v); } | ||
function set_y(dt, v) { dt.setUTCFullYear(v); return dt; } | ||
function set_m(dt, v) { dt.setUTCMonth(v-1); return dt; } | ||
function set_d(dt, v) { dt.setUTCDate(v); return dt; } | ||
function set_hr(dt, v) { dt.setUTCHours(v); return dt; } | ||
function set_min(dt, v) { dt.setUTCMinutes(v); return dt; } | ||
function set_sec(dt, v) { dt.setUTCSeconds(v); return dt; } | ||
function add_y(dt, v) { set_y(dt, y(dt)+v); } | ||
function add_m(dt, v) { set_m(dt, m(dt)+v); } | ||
function add_d(dt, v) { set_d(dt, d(dt)+v); } | ||
function add_hr(dt, v) { set_hr(dt, hr(dt)+v); } | ||
function add_min(dt, v) { set_min(dt, min(dt)+v); } | ||
function add_sec(dt, v) { set_sec(dt, sec(dt)+v); } | ||
function add_y(dt, v) { return set_y(dt, y(dt)+v); } | ||
function add_m(dt, v) { return set_m(dt, m(dt)+v); } | ||
function add_d(dt, v) { return set_d(dt, d(dt)+v); } | ||
function add_hr(dt, v) { return set_hr(dt, hr(dt)+v); } | ||
function add_min(dt, v) { return set_min(dt, min(dt)+v); } | ||
function add_sec(dt, v) { return set_sec(dt, sec(dt)+v); } | ||
// Day of week (0-6) | ||
function wkday(dt) { return dt.getUTCDay(); } | ||
// First of the month | ||
function fst(dt) { | ||
return new Date(y(dt), m(dt)-1, 1); | ||
} | ||
// Day of week (0-6), adjust for the start of week | ||
function wkday(dt) { | ||
return dt.getUTCDay(); | ||
} | ||
// Return the number of days between dt1 and dt2 | ||
@@ -84,9 +95,24 @@ function daydiff(dt1, dt2) { | ||
// Week of month | ||
function m_wk(dt, wkst) { | ||
return (0 | d(dt)/7) + (d(dt) % 7 === 0 ? 0 : 1); | ||
} | ||
var RRule = exports.RRule = function(rule) { | ||
this.rule = rule || {}; | ||
for(var i in this.rule) | ||
var RRule = exports.RRule = function(rule, start, end) { | ||
this.start = start ? to_utc_date(start) : null; | ||
this.end = end ? to_utc_date(end) : null; | ||
if(typeof rule === 'string') | ||
rule = RRule.parse(rule); | ||
this.rule = {}; | ||
for(var i in (rule||{})) { | ||
if(SUPPORTED_PARTS.indexOf(i) == -1) | ||
throw new Error(i+" is not currently supported!"); | ||
this.rule[i] = RULE_PARTS[i] | ||
? RULE_PARTS[i].parse(this.rule[i]) | ||
: this.rule[i]; | ||
? RULE_PARTS[i].parse(rule[i]) | ||
: rule[i]; | ||
} | ||
} | ||
@@ -100,3 +126,3 @@ | ||
} | ||
return new RRule(rrule); | ||
return rrule; | ||
} | ||
@@ -123,25 +149,62 @@ | ||
RRule.prototype.getDates = function(until_or_count) { | ||
} | ||
// Return the next occurrence after dt | ||
RRule.prototype.nextOccurs = function(start, after, end) { | ||
// Degenerate case; events don't occur before they start... | ||
if(start > after) return null; | ||
RRule.prototype.next = function(after) { | ||
after = after && to_utc_date(after); | ||
start = to_utc_date(start); | ||
after = to_utc_date(after); | ||
// Events don't occur before the start or after the end... | ||
if(!after || after < this.start) | ||
return from_utc_date(this.start); | ||
if(this.end && after > this.end) return null; | ||
var freq = FREQ[this.rule.FREQ]; | ||
var next = freq.next(this.rule, start, after); | ||
if(!freq) | ||
throw new Error(this.rule.FREQ+' recurrence is not supported'); | ||
var next = freq.next(this.rule, this.start, after); | ||
// Date is off the end of the spectrum... | ||
if(end !== undefined && next > to_utc_date(end)) | ||
if(this.end && next > this.end) | ||
return null; | ||
if(this.rule.COUNT && this.count_end !== null) { | ||
if(this.count_end === undefined) { | ||
// Don't check this while we're trying to compute it... | ||
this.count_end = null; | ||
this.count_end = this.nextOccurences(this.rule.COUNT).pop(); | ||
} | ||
if(next > this.count_end) | ||
return null; | ||
} | ||
if(this.rule.UNTIL && next > this.rule.UNTIL) | ||
return null; | ||
return from_utc_date(next); | ||
} | ||
RRule.prototype.nextOccurences = function(after, count) { | ||
if(arguments.length === 1) { | ||
count = after; | ||
after = undefined; | ||
} | ||
var arr = []; | ||
while(count-- && after !== null) { | ||
after = this.next(after); | ||
if(after) | ||
arr.push(after); | ||
} | ||
return arr; | ||
} | ||
var RULE_PARTS = { | ||
INTERVAL: { | ||
parse: function(v) { return parseInt(v,10); } | ||
}, | ||
UNTIL: { | ||
parse: function(v) { return types.parse_value('DATE-TIME', v); }, | ||
format: function(v) { return types.format_value('DATE-TIME', v); } | ||
}, | ||
FREQ: { | ||
@@ -151,11 +214,34 @@ parse: function(v) { return v; }, | ||
BYMONTH: { | ||
parse: function(v) { return parseInt(v,10); }, | ||
parse: function(v) { | ||
if(typeof v === 'number') return [v]; | ||
return v.split(',').map(function(mo) { | ||
return parseInt(mo,10); | ||
}); | ||
}, | ||
format: function(v) { | ||
return v.join(','); | ||
} | ||
}, | ||
BYDAY: { // 2TH (second thursday) -> [2,4] | ||
parse: function(v) { | ||
var m = v.match(/([+-]?\d)?(SU|MO|TU|WE|TH|FR|SA)/); | ||
return [parseInt(m[1],10)||0, WKDAYS.indexOf(m[2])]; | ||
var days = v.split(',').map(function(day) { | ||
var m = day.match(/([+-]?\d)?(SU|MO|TU|WE|TH|FR|SA)/); | ||
return [parseInt(m[1],10)||0, WKDAYS.indexOf(m[2])]; | ||
}); | ||
days.sort(function(d1, d2) { | ||
// Sort by week, day of week | ||
if(d1[0] == d2[0]) | ||
return d1[1] - d2[1]; | ||
else | ||
return d1[0] - d2[0]; | ||
}); | ||
return days; | ||
}, | ||
format: function(v) { | ||
return (v[0] || '')+WKDAYS[v[1]]; | ||
return v.map(function(day) { | ||
return (day[0] || '')+WKDAYS[day[1]]; | ||
}).join(','); | ||
} | ||
@@ -165,9 +251,87 @@ } | ||
// These parts use the same format... | ||
RULE_PARTS['BYMONTHDAY'] = RULE_PARTS['BYMONTH']; | ||
RULE_PARTS['COUNT'] = RULE_PARTS['INTERVAL']; | ||
var FREQ = { | ||
SECONDLY: {}, | ||
MINUTELY: {}, | ||
HOURLY: {}, | ||
DAILY: {}, | ||
WEEKLY: {}, | ||
MONTHLY: {}, | ||
DAILY: { | ||
next: function(rule, start, after) { | ||
var next = new Date(after); | ||
var interval = rule.INTERVAL || 1; | ||
// Adjust for interval... | ||
var mod_days = daydiff(next, start) % interval; | ||
if(mod_days) | ||
add_d(next, interval - mod_days); | ||
for(var i=0; i<2; ++i) { | ||
next = byday(rule.BYDAY, next); | ||
set_hr(next, hr(start)); | ||
set_min(next, min(start)); | ||
set_sec(next, sec(start)); | ||
if(after.valueOf() != next.valueOf()) | ||
break; | ||
add_d(next, interval); | ||
} | ||
return next; | ||
} | ||
}, | ||
WEEKLY: { | ||
next: function(rule, start, after) { | ||
var next = new Date(after); | ||
var interval = rule.INTERVAL || 1; | ||
// Adjust for interval... | ||
var days = daydiff(start, next); | ||
if(days) add_d(next, 7 - (days % 7)); | ||
var mod_weeks = Math.floor(daydiff(next, start) / 7) % interval; | ||
if(mod_weeks) | ||
add_d(next, (interval - mod_weeks) * 7); | ||
while(true) { | ||
next = byday(rule.BYDAY, next); | ||
set_hr(next, hr(start)); | ||
set_min(next, min(start)); | ||
set_sec(next, sec(start)); | ||
if(after.valueOf() != next.valueOf() | ||
&& check_bymonth(rule.BYMONTH, next)) | ||
break; | ||
add_d(next, interval * 7); | ||
} | ||
return next; | ||
} | ||
}, | ||
MONTHLY: { | ||
next: function(rule, start, after) { | ||
var next = new Date(after); | ||
var interval = rule.INTERVAL || 1; | ||
// Adjust interval to be correct | ||
var delta = (m(next) - m(start)) + (y(next) - y(start)) * 12; | ||
if(delta % interval) | ||
add_m(next, interval - (delta % interval)); | ||
for(var i=0; i<2; ++i) { | ||
next = byday(rule.BYDAY, next); | ||
next = bymonthday(rule.BYMONTHDAY, next); | ||
set_hr(next, hr(start)); | ||
set_min(next, min(start)); | ||
set_sec(next, sec(start)); | ||
if(after.valueOf() != next.valueOf()) | ||
break; | ||
add_m(next, interval); | ||
} | ||
return next; | ||
} | ||
}, | ||
YEARLY: { | ||
@@ -186,4 +350,5 @@ next: function(rule, start, after) { | ||
for(var i=0; i<2; ++i) { | ||
bymonth(rule.BYMONTH || m(start), next, i); | ||
byday(rule.BYDAY, next, i); | ||
next = bymonth(rule.BYMONTH || m(start), next, i); | ||
next = bymonthday(rule.BYMONTHDAY, next); | ||
next = byday(rule.BYDAY, next, i); | ||
@@ -199,3 +364,3 @@ // TODO: Add actual byhour/minute/second methods | ||
add_y(next, interval); | ||
set_d(set_m(add_y(next, interval), 1), 1); | ||
} | ||
@@ -208,40 +373,92 @@ | ||
function sort_dates(dateary) { | ||
return dateary.sort(function(dt1, dt2) { | ||
if(dt1 === null && dt2 === null) return 0; | ||
if(dt1 === null) return 1; | ||
if(dt2 === null) return -1; | ||
return dt1.valueOf() - dt2.valueOf(); | ||
}); | ||
} | ||
// Check that a particular date is within the limits | ||
// designated by the BYMONTH rule | ||
function check_bymonth(rules, dt) { | ||
if(!rules || !rules.length) return true; | ||
return rules.indexOf(m(dt)) !== -1; | ||
} | ||
// Advance to the next month that satisfies the rule... | ||
function bymonth(rule, dt) { | ||
if(!rule) return; | ||
var delta = rule-m(dt); | ||
add_m(dt, delta < 0 ? delta + 12 : delta); | ||
function bymonth(rules, dt) { | ||
if(!rules || !rules.length) return dt; | ||
var candidates = rules.map(function(rule) { | ||
var delta = rule-m(dt); | ||
if(delta < 0) delta += 12; | ||
var newdt = add_m(new Date(dt), delta); | ||
set_d(newdt, 1); | ||
return newdt; | ||
}); | ||
// If the month changed, move to the first of the month | ||
if(delta) set_d(dt, 1); | ||
var newdt = sort_dates(candidates).shift(); | ||
return newdt || dt; | ||
} | ||
function bymonthday(rules, dt) { | ||
if(!rules || !rules.length) return dt; | ||
var candidates = rules.map(function(rule) { | ||
var delta = rule-m(dt); | ||
if(delta < 0) delta += 12; | ||
var newdt = set_d(new Date(dt), rule); | ||
return (newdt < dt ? null : newdt); | ||
}); | ||
var newdt = sort_dates(candidates).shift(); | ||
return newdt || dt; | ||
} | ||
// Advance to the next day that satisfies the byday rule... | ||
function byday(rule, dt) { | ||
if(!rule) return; | ||
var ord = rule[0], day = rule[1]; | ||
if(ord > 0) { | ||
// First, second, third, etc... | ||
function byday(rules, dt) { | ||
if(!rules || !rules.length) return dt; | ||
// Line us up with the correct day of week | ||
var delta = day-wkday(dt); | ||
add_d(dt, delta < 0 ? delta + 7 : delta); | ||
// Generate a list of candiDATES. (HA!) | ||
var candidates = rules.map(function(rule) { | ||
// Align on the correct day of the week... | ||
var days = rule[1]-wkday(dt); | ||
if(days < 0) days += 7; | ||
var newdt = add_d(new Date(dt), days); | ||
var delta2 = (ord - (Math.floor(d(dt)/7)+1)) * 7; | ||
add_d(dt, delta2); | ||
} | ||
else { | ||
// Last, second to last, etc... | ||
} | ||
if(rule[0] > 0) { | ||
var wk = 0 | (d(newdt) / 7) + 1; | ||
if(wk > rule[0]) return null; | ||
add_d(newdt, (rule[0] - wk) * 7); | ||
} | ||
else if(rule[0] < 0) { | ||
// Find all the matching days in the month... | ||
var dt2 = new Date(newdt); | ||
var days = []; | ||
while(m(dt2) === m(newdt)) { | ||
days.push(d(dt2)); | ||
add_d(dt2, 7); | ||
} | ||
// Then grab the nth from the end... | ||
set_d(newdt, days.reverse()[(-rule[0])-1]); | ||
} | ||
// Don't look outside the current month... | ||
if(m(newdt) !== m(dt)) return null; | ||
return newdt; | ||
}); | ||
// Select the date occurring next... | ||
var newdt = sort_dates(candidates).shift(); | ||
return newdt || dt; | ||
} | ||
// BYSECOND | ||
// BYMINUTE | ||
// BYHOUR | ||
// BYDAY | ||
// BYMONTHDAY | ||
// BYYEARDAY | ||
// BYWEEKNO | ||
// BYMONTH |
@@ -80,2 +80,5 @@ // Copyright (C) 2011 Tri Tech Computers Ltd. | ||
if(value.date_only) | ||
return format_value('DATE', value); | ||
return value.getUTCFullYear() | ||
@@ -87,3 +90,3 @@ +pad(value.getUTCMonth()+1) | ||
parse: function(value, parameters, calendar) { | ||
if(parameters['VALUE'] === 'DATE') | ||
if(parameters['VALUE'] === 'DATE' || value.length <= 8) | ||
return _types['DATE'].parse(value); | ||
@@ -90,0 +93,0 @@ |
{ | ||
"name": "icalendar", | ||
"version": "0.3.2", | ||
"version": "0.4.1", | ||
"author": "James Emerton <james@tri-tech.com>", | ||
@@ -5,0 +5,0 @@ "description": "RFC5545 iCalendar parser/generator", |
@@ -34,3 +34,3 @@ iCalendar for Node | ||
// data is a string containing RFC5545 data | ||
var ical = icalendar.iCalendar.parse(data); | ||
var ical = icalendar.parse_calendar(data); | ||
@@ -42,2 +42,20 @@ Access an array of the events defined within: | ||
Implementation Status | ||
--------------------- | ||
Several portions of the iCalendar spec remain unimplemented: | ||
* HOURLY, MINUTELY, and SECONDLY recurrence are not implemented. | ||
- Support for these is not currently planned, as they do not | ||
seem to be found in actual use. | ||
* BYHOUR, BYMINUTE, and BYSECOND modifiers are not implement as above. | ||
* BYSETPOS | ||
* WKST | ||
- This could very likely become important | ||
* BYWEEKNO | ||
* BYYEARDAY | ||
* RDATE and EXDATE are not implemented, but support is planned | ||
Contact | ||
@@ -44,0 +62,0 @@ ------- |
@@ -82,3 +82,3 @@ // Test search | ||
dst.addProperty('DTSTART', new Date(1970,2,8,2,0,0)); | ||
dst.addProperty('RRULE', new icalendar.RRule({FREQ: 'YEARLY', BYMONTH: 3, BYDAY: '2SU'})); | ||
dst.addProperty('RRULE', {FREQ: 'YEARLY', BYMONTH: 3, BYDAY: '2SU'}); | ||
dst.addProperty('TZOFFSETFROM', -500); | ||
@@ -89,3 +89,3 @@ dst.addProperty('TZOFFSETTO', -400); | ||
std.addProperty('DTSTART', new Date(1970,10,1,2,0,0)); | ||
std.addProperty('RRULE', new icalendar.RRule({FREQ: 'YEARLY', BYMONTH: 11, BYDAY: '1SU'})); | ||
std.addProperty('RRULE', {FREQ: 'YEARLY', BYMONTH: 11, BYDAY: '1SU'}); | ||
std.addProperty('TZOFFSETFROM', -400); | ||
@@ -92,0 +92,0 @@ std.addProperty('TZOFFSETTO', -500); |
@@ -6,33 +6,140 @@ | ||
it("should parse RRULEs correctly", function() { | ||
expect(RRule.parse('FREQ=YEARLY;BYMONTH=11;BYDAY=1SU').valueOf()) | ||
.toEqual({FREQ: 'YEARLY', BYMONTH: 11, BYDAY: [1,0]}); | ||
expect(new RRule(RRule.parse('FREQ=YEARLY;BYMONTH=11;BYDAY=1SU')).valueOf()) | ||
.toEqual({FREQ: 'YEARLY', BYMONTH: [11], BYDAY: [[1,0]]}); | ||
expect(new RRule(RRule.parse('FREQ=YEARLY;BYDAY=-1SU')).valueOf()) | ||
.toEqual({FREQ: 'YEARLY', BYDAY: [[-1,0]]}); | ||
expect(new RRule('FREQ=WEEKLY;BYMONTH=1,2,3').valueOf()) | ||
.toEqual({FREQ: 'WEEKLY', BYMONTH: [1,2,3]}); | ||
}); | ||
it("handles yearly recurrence", function() { | ||
var start = new Date(1970,2,8,2,0,0); | ||
var rrule = RRule.parse('FREQ=YEARLY;BYMONTH=3;BYDAY=1SU'); | ||
var rrule5 = RRule.parse('FREQ=YEARLY;INTERVAL=5;BYMONTH=3;BYDAY=1SU'); | ||
it("respects UNTIL parts", function() { | ||
var start = new Date(2011,0,1,2,0,0); | ||
var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;UNTIL=20110201'), start); | ||
expect(rrule.nextOccurs(start, new Date(2011,1,4))) | ||
.toEqual(new Date(2011,2,6,2,0,0)); | ||
expect(rrule.next(new Date(2011,0,1,2,0,0))) | ||
.toEqual(new Date(2011,0,2,2,0,0)); | ||
expect(rrule.nextOccurs(start, new Date(2011,3,7))) | ||
.toEqual(new Date(2012,2,4,2,0,0)); | ||
expect(rrule.next(new Date(2011,0,2,2,0,0))) | ||
.toEqual(null); | ||
}); | ||
expect(rrule.nextOccurs(start, new Date(2011,2,6,2,0,0))) | ||
.toEqual(new Date(2012,2,4,2,0,0)); | ||
it("respects COUNT parts", function() { | ||
var rrule = new RRule('FREQ=MONTHLY;COUNT=3', new Date(2011,0,1)); | ||
expect(rrule.nextOccurences(new Date(2010,11,31), 4)) | ||
.toEqual([ | ||
new Date(2011,0,1), | ||
new Date(2011,1,1), | ||
new Date(2011,2,1) | ||
]); | ||
}); | ||
expect(rrule5.nextOccurs(start, new Date(2011,1,4))) | ||
.toEqual(new Date(2015,2,1,2,0,0)); | ||
describe("yearly recurrence", function() { | ||
it("handles yearly recurrence", function() { | ||
var start = new Date(1970,2,8,2,0,0); | ||
var rrule = new RRule('FREQ=YEARLY;BYMONTH=3;BYDAY=1SU', new Date(1970,2,8,2,0,0)); | ||
expect(rrule.next(new Date(2011,1,4))) | ||
.toEqual(new Date(2011,2,6,2,0,0)); | ||
expect(rrule.next(new Date(2011,3,7))) | ||
.toEqual(new Date(2012,2,4,2,0,0)); | ||
expect(rrule.next(new Date(2011,2,6,2,0,0))) | ||
.toEqual(new Date(2012,2,4,2,0,0)); | ||
}); | ||
it("handles yearly recurrence with an interval", function() { | ||
var rrule5 = new RRule('FREQ=YEARLY;INTERVAL=5;BYMONTH=3;BYDAY=1SU', new Date(1970,2,8,2,0,0)); | ||
expect(rrule5.next(new Date(2011,1,4))) | ||
.toEqual(new Date(2015,2,1,2,0,0)); | ||
}); | ||
}); | ||
// it("handles monthly recurrence", function() { | ||
// var start = new Date(2011,0,1,2,0,0); | ||
// var rrule = RRule.parse('FREQ=MONTHLY;BYDAY=1SU'); | ||
// | ||
// expect(rrule.nextOccurs(start, new Date(2011,0,1,2,0,0))) | ||
// .toEqual(new Date(2011,1, | ||
// }); | ||
describe("monthly recurrence", function() { | ||
it("handles monthly recurrence", function() { | ||
var start = new Date(2011,0,1,2,0,0); | ||
var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU'), start); | ||
expect(rrule.next(new Date(2011,0,1,2,0,0))) | ||
.toEqual(new Date(2011,0,2,2,0,0)); | ||
expect(rrule.next(new Date(2011,0,2,2,0,0))) | ||
.toEqual(new Date(2011,1,6,2,0,0)); | ||
}); | ||
it("handles monthly recurrence with an interval", function() { | ||
var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;INTERVAL=3'), | ||
new Date(2011,0,1,2,0,0)); | ||
expect(rrule.nextOccurences(new Date(2011,0,1,2,0,0), 3)) | ||
.toEqual([ | ||
new Date(2011,0,2,2,0,0), | ||
new Date(2011,3,3,2,0,0), | ||
new Date(2011,6,3,2,0,0) | ||
]); | ||
}); | ||
it("handles MONTHLY recurrence with BYMONTHDAY", function() { | ||
var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYMONTHDAY=14'), | ||
new Date(2011,0,1)); | ||
expect(rrule.nextOccurences(new Date(2011,0,2), 3)) | ||
.toEqual([ | ||
new Date(2011,0,14), | ||
new Date(2011,1,14), | ||
new Date(2011,2,14) | ||
]); | ||
}); | ||
it("handles MONTHLY recurrence with negative BYDAY", function() { | ||
var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=-2SU'), | ||
new Date(2012,0,22)); | ||
expect(rrule.next(new Date(2012,1,1))) | ||
.toEqual(new Date(2012,1,19)); | ||
}); | ||
}); | ||
describe("weekly recurrence", function() { | ||
it("handles simple weekly recurrence", function() { | ||
var rrule = new RRule('FREQ=WEEKLY', new Date(2012,0,1)); | ||
expect(rrule.next(new Date(2012,0,1))) | ||
.toEqual(new Date(2012,0,8)); | ||
expect(rrule.next(new Date(2012,0,5))) | ||
.toEqual(new Date(2012,0,8)); | ||
}); | ||
it("handles weekly recurrence with BYDAY", function() { | ||
var rrule = new RRule('FREQ=WEEKLY;BYDAY=TU', new Date(2012,0,1)); | ||
expect(rrule.next(new Date(2012,0,1))) | ||
.toEqual(new Date(2012,0,3)); | ||
expect(rrule.next(new Date(2012,0,5))) | ||
.toEqual(new Date(2012,0,10)); | ||
}); | ||
it("limits results of weekly recurrences with BYMONTH", function() { | ||
var rrule = new RRule('FREQ=WEEKLY;BYMONTH=2,3,5', new Date(2012,0,3)); | ||
expect(rrule.next(new Date(2012,0,4))) | ||
.toEqual(new Date(2012,1,7)); | ||
}); | ||
}); | ||
describe("daily recurrence", function() { | ||
it("handles daily recurrence", function() { | ||
var rrule = new RRule('FREQ=DAILY;BYDAY=1MO,2TU,3WE', new Date(2012,0,1)); | ||
expect(rrule.nextOccurences(new Date(2012,0,1), 3)) | ||
.toEqual([new Date(2012,0,2), | ||
new Date(2012,0,10), | ||
new Date(2012,0,18)]); | ||
}); | ||
}); | ||
}); | ||
@@ -78,6 +78,6 @@ | ||
assert.deepEqual({FREQ: 'YEARLY', BYMONTH: 11, BYDAY: [1,0]}, | ||
icalendar.parse_value('RECUR', 'FREQ=YEARLY;BYMONTH=11;BYDAY=1SU').valueOf()); | ||
expect(icalendar.parse_value('RECUR', 'FREQ=YEARLY;BYMONTH=11;BYDAY=1SU')) | ||
.toEqual({FREQ: 'YEARLY', BYMONTH: '11', BYDAY: '1SU'}); | ||
}); | ||
}); |
@@ -89,3 +89,20 @@ | ||
}); | ||
it('correctly matches recurring events', function() { | ||
var vevent = new icalendar.VEvent(); | ||
vevent.addProperty('DTSTART', new Date(2011,11,1,5,0,0)); | ||
vevent.addProperty('RRULE', 'FREQ=MONTHLY;COUNT=3'); | ||
expect(vevent.inTimeRange(new Date(2011,10,1), new Date(2011,11,1))) | ||
.toEqual(false); | ||
expect(vevent.inTimeRange(new Date(2011,11,1), new Date(2011,11,2))) | ||
.toEqual(true); | ||
expect(vevent.inTimeRange(new Date(2011,11,1), null)) | ||
.toEqual(true); | ||
expect(vevent.inTimeRange(new Date(2012,0,1)), new Date(2012,5,1)) | ||
.toEqual(true); | ||
expect(vevent.inTimeRange(new Date(2012,2,3), null)) | ||
.toEqual(false); | ||
}); | ||
}); | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
189516
18
2476
63