├── LICENSE ├── rrecur-parser-tests.js ├── rrecur-parser.js └── test.html /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2012 Vincent Romagnoli, digITpro sàrl 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /rrecur-parser-tests.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | Testing case of scheduler.js inspired from 3 | http://www.kanzaki.com/docs/ical/rrule.html 4 | 5 | date : jan 09 2012 6 | 7 | ---------------------------------------------------------------------------- */ 8 | 9 | 10 | var d = new Date(2011, 10, 15, 19, 18, 00); // tue 15/11/2011 19:18 11 | var scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;UNTIL=20111231T090000Z", true); // sat 31/12/2011 09:00 12 | console.assert(scheduler.rrule_freq == "DAILY"); 13 | console.assert(scheduler.rrule_until.getTime() == (new Date(2011, 11, 31, 9, 0, 0)).getTime()); 14 | console.assert(scheduler.rrule_interval == 1); 15 | console.assert(scheduler.rrule_bysecond.in_array("0")); 16 | console.assert(scheduler.rrule_byminute.in_array("18")); 17 | console.assert(scheduler.rrule_byhour.in_array("19")); 18 | console.assert(!scheduler.rrule_byday); 19 | console.assert(!scheduler.rrule_bymonthday); 20 | //console.assert(scheduler.rrule_byyearday == ); 21 | //console.assert(scheduler.rrule_byweekno == ); 22 | console.assert(!scheduler.rrule_bymonth); 23 | //console.assert(scheduler.rrule_bysetpos == ); 24 | console.assert(scheduler.rrule_wkst == "MO"); 25 | 26 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;COUNT=5", true); 27 | console.assert(scheduler.rrule_freq == "DAILY"); 28 | console.assert(scheduler.rrule_count == 5); 29 | console.assert(scheduler.rrule_interval == 1); 30 | console.assert(scheduler.rrule_bysecond.in_array("0")); 31 | console.assert(scheduler.rrule_byminute.in_array("18")); 32 | console.assert(scheduler.rrule_byhour.in_array("19")); 33 | console.assert(!scheduler.rrule_byday); 34 | console.assert(!scheduler.rrule_bymonthday); 35 | //console.assert(scheduler.rrule_byyearday == ); 36 | //console.assert(scheduler.rrule_byweekno == ); 37 | console.assert(!scheduler.rrule_bymonth); 38 | //console.assert(scheduler.rrule_bysetpos == ); 39 | console.assert(scheduler.rrule_wkst == "MO"); 40 | 41 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;COUNT=5;INTERVAL=10", true); 42 | console.assert(scheduler.rrule_freq == "DAILY"); 43 | console.assert(scheduler.rrule_count == 5); 44 | console.assert(scheduler.rrule_interval == 10); 45 | 46 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;COUNT=5;BYDAY=MO,TU", true); 47 | console.assert(scheduler.rrule_freq == "WEEKLY"); 48 | console.assert(scheduler.rrule_count == 5); 49 | console.assert(scheduler.rrule_bysecond.in_array("0")); 50 | console.assert(scheduler.rrule_byminute.in_array("18")); 51 | console.assert(scheduler.rrule_byhour.in_array("19")); 52 | console.assert(scheduler.rrule_byday.in_array("MO") && scheduler.rrule_byday.in_array("TU")); 53 | console.assert(!scheduler.rrule_bymonthday); 54 | //console.assert(scheduler.rrule_byyearday == ); 55 | //console.assert(scheduler.rrule_byweekno == ); 56 | console.assert(!scheduler.rrule_bymonth); 57 | //console.assert(scheduler.rrule_bysetpos == ); 58 | console.assert(scheduler.rrule_wkst == "MO"); 59 | 60 | 61 | console.log("--- Daily for 5 occurrences ---"); 62 | d = new Date(2011, 10, 14, 20, 05, 12); 63 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;COUNT=5", true); 64 | var occurrences = scheduler.all_occurrences(); 65 | console.assert(occurrences.length == 5); 66 | console.assert(occurrences.in_array(d.getTime())); 67 | console.assert(occurrences.in_array(new Date(2011, 10, 15, 20, 05, 12).getTime())); 68 | console.assert(occurrences.in_array(new Date(2011, 10, 16, 20, 05, 12).getTime())); 69 | console.assert(occurrences.in_array(new Date(2011, 10, 17, 20, 05, 12).getTime())); 70 | console.assert(occurrences.in_array(new Date(2011, 10, 18, 20, 05, 12).getTime())); 71 | // ==> "Mon Nov 14 2011 20:05:12 GMT+0100 (CET)", 72 | // "Tue Nov 15 2011 20:05:12 GMT+0100 (CET)", 73 | // …, 74 | // "Wed Nov 18 2011 20:05:12 GMT+0100 (CET)" 75 | 76 | 77 | 78 | console.log("--- Daily for 5 occurrences, but get only 2 days ---"); 79 | d = new Date(2011, 10, 14, 20, 05, 12); 80 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;COUNT=5", true); 81 | start_at = new Date(2011, 10, 16); 82 | end_at = new Date(2011, 10, 18); 83 | occurrences = scheduler.occurrences_between(start_at, end_at); 84 | console.assert(occurrences.length == 2); 85 | console.assert(occurrences.in_array(new Date(2011, 10, 16, 20, 05, 12).getTime())); 86 | console.assert(occurrences.in_array(new Date(2011, 10, 17, 20, 05, 12).getTime())); 87 | 88 | start_at = new Date(2012, 10, 16); 89 | end_at = new Date(2012, 10, 18); 90 | occurrences = scheduler.occurrences_between(start_at, end_at); 91 | console.assert(occurrences.length == 0); 92 | 93 | // ==> "Mon Nov 14 2011 20:05:12 GMT+0100 (CET)", 94 | // "Tue Nov 15 2011 20:05:12 GMT+0100 (CET)", 95 | // …, 96 | // "Wed Nov 18 2011 20:05:12 GMT+0100 (CET)" 97 | 98 | 99 | 100 | console.log("--- Daily for 200 occurrences ---"); 101 | d = new Date(2011, 10, 14, 20, 05, 12) 102 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;COUNT=200", true); 103 | console.assert(scheduler.all_occurrences().length == 200); 104 | 105 | 106 | console.log("--- Daily until 2 december ---"); 107 | d = new Date(2011, 10, 29, 20, 05, 12); 108 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;UNTIL=20111202T000000Z", true); 109 | occurrences = scheduler.all_occurrences(); 110 | console.assert(occurrences.in_array(d.getTime())); 111 | console.assert(occurrences.in_array(new Date(2011, 10, 30, 20, 05, 12).getTime())); 112 | console.assert(occurrences.in_array(new Date(2011, 11, 1, 20, 05, 12).getTime())); 113 | // ==> "Mon Nov 29 2011 20:05:12 GMT+0100 (CET)", 114 | // "Tue Nov 30 2011 20:05:12 GMT+0100 (CET)", 115 | // "Wed Dec 01 2011 20:05:12 GMT+0100 (CET)" 116 | 117 | 118 | 119 | console.log("--- Every other day - forever ---"); 120 | d = new Date(2011, 10, 14, 20, 05, 12); 121 | start_at = new Date(2011, 10, 17, 20, 05, 12) 122 | end_at = new Date(2011, 10, 26, 20, 05, 12); 123 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;INTERVAL=2", true); 124 | console.assert(scheduler.all_occurrences() == null); 125 | occurrences = scheduler.occurrences_between(start_at, end_at); 126 | console.assert(occurrences.in_array(new Date(2011, 10, 18, 20, 05, 12).getTime())); 127 | console.assert(occurrences.in_array(new Date(2011, 10, 20, 20, 05, 12).getTime())); 128 | console.assert(occurrences.in_array(new Date(2011, 10, 22, 20, 05, 12).getTime())); 129 | console.assert(occurrences.in_array(new Date(2011, 10, 24, 20, 05, 12).getTime())); 130 | console.assert(occurrences.in_array(new Date(2011, 10, 26, 20, 05, 12).getTime())); 131 | // ==> "Nov 18 2011 20:05:12 GMT+0100 (CET)", 132 | // "Nov 20 2011 20:05:12 GMT+0100 (CET)", 133 | // "Nov 22 2011 20:05:12 GMT+0100 (CET)" 134 | // "Nov 24 2011 20:05:12 GMT+0100 (CET)" 135 | // "Nov 26 2011 20:05:12 GMT+0100 (CET)" 136 | 137 | 138 | 139 | console.log("--- Every 10 days, 5 occurrences ---"); 140 | d = new Date(2011, 10, 14, 20, 05, 12); 141 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5", true); 142 | occurrences = scheduler.all_occurrences(); 143 | console.assert(occurrences.length == 5); 144 | console.assert(occurrences[0] == new Date(2011, 10, 14, 20, 05, 12).getTime()); 145 | console.assert(occurrences[1] == new Date(2011, 10, 24, 20, 05, 12).getTime()); 146 | console.assert(occurrences[2] == new Date(2011, 11, 4, 20, 05, 12).getTime()); 147 | console.assert(occurrences[3] == new Date(2011, 11, 14, 20, 05, 12).getTime()); 148 | console.assert(occurrences[4] == new Date(2011, 11, 24, 20, 05, 12).getTime()); 149 | // ==> "Nov 14 2011 20:05:12 GMT+0100 (CET)", 150 | // ..., 151 | // "Dec 24 2011 20:05:12 GMT+0100 (CET)" 152 | // ...modification... 153 | 154 | 155 | 156 | console.log("--- Everyday in January, for 2 years ---"); 157 | d = new Date(2011, 0, 1, 9, 0, 0); 158 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;UNTIL=20130131T090000Z;BYMONTH=1", true); 159 | occurrences = scheduler.all_occurrences(); 160 | console.assert(occurrences[0] == new Date(2011, 0, 1, 9, 0, 0).getTime()); 161 | console.assert(occurrences[31] == new Date(2012, 0, 1, 9, 0, 0).getTime()); 162 | console.assert(occurrences[62] == new Date(2013, 0, 1, 9, 0, 0).getTime()); 163 | 164 | 165 | 166 | d = new Date(2011, 0, 1, 9); 167 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;UNTIL=20130131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA", true); 168 | occurrences = scheduler.all_occurrences(); 169 | console.assert(occurrences[0] == new Date(2011, 0, 1, 9).getTime()); 170 | console.assert(occurrences[31] == new Date(2012, 0, 1, 9).getTime()); 171 | console.assert(occurrences[62] == new Date(2013, 0, 1, 9).getTime()); 172 | // 173 | // ==> (2011 9:00 AM EDT)January 1-31 174 | // (2012 9:00 AM EDT)January 1-31 175 | // (2013 9:00 AM EDT)January 1-31 176 | // ...modification... 177 | 178 | 179 | 180 | console.log("--- Weekly for 10 occurrences ---"); 181 | d = new Date(1997, 8, 2, 9, 0, 0); 182 | start_at = new Date(1997, 8, 11, 9, 0, 0); 183 | end_at = new Date(1997, 9, 1, 9, 0, 0); 184 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;COUNT=10", true); 185 | console.assert(scheduler.all_occurrences().length == 10); 186 | occurrences = scheduler.occurrences_between(start_at, end_at); 187 | console.assert(occurrences.length == 3); 188 | console.assert(occurrences.in_array(new Date(1997, 8, 16, 9, 0, 0).getTime())); 189 | console.assert(occurrences.in_array(new Date(1997, 8, 23, 9, 0, 0).getTime())); 190 | console.assert(occurrences.in_array(new Date(1997, 8, 30, 9, 0, 0).getTime())); 191 | // ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21 192 | // (1997 9:00 AM EST)October 28;November 4 193 | 194 | 195 | 196 | console.log("--- Toutes les semaines le mardi, seulement 2 fois ---"); 197 | d = new Date(2011, 11, 5, 8); // lundi 5 déc 2011 198 | start_at = new Date(2011, 11, 1); // 1er déc. 2011 199 | end_at = new Date(2012, 0, 1); // 1er janv. 2012 200 | scheduler = new Scheduler(d, "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU;COUNT=2", true); 201 | occurrences = scheduler.occurrences_between(start_at, end_at); 202 | console.assert(occurrences.length == 2); 203 | console.assert(occurrences.in_array(new Date(2011, 11, 6, 8).getTime())); 204 | console.assert(occurrences.in_array(new Date(2011, 11, 13, 8).getTime())); 205 | 206 | // La dernière occurence tombe le mardi 13 il ne devrait donc plus en avoir après cette date 207 | start_at = new Date(2011, 11, 14); // mercredi 14 déc. 208 | end_at = new Date(2012, 11, 31); // 31 dec. 2012 209 | occurrences = scheduler.occurrences_between(start_at, end_at); 210 | console.assert(occurrences.length == 0); 211 | 212 | 213 | 214 | console.log("--- Toutes les semaines le mardi, du 8 dec au 31 dec ---"); 215 | d = new Date(2011, 11, 8, 8); // jeudi 8 déc 2011 216 | start_at = new Date(2011, 11, 1); // 1er déc. 2011 217 | end_at = new Date(2012, 0, 1); // 1er janv. 2013 218 | scheduler = new Scheduler(d, "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU;UNTIL=20111231", true); 219 | // ==> (2011 8:00 AM EDT) December 13, 20, 27 220 | 221 | console.assert(scheduler.all_occurrences().length == 3); 222 | occurrences = scheduler.occurrences_between(start_at, end_at); 223 | console.assert(occurrences.length == 3); 224 | console.assert(occurrences.in_array(new Date(2011, 11, 13, 8).getTime())); 225 | console.assert(occurrences.in_array(new Date(2011, 11, 20, 8).getTime())); 226 | console.assert(occurrences.in_array(new Date(2011, 11, 27, 8).getTime())); 227 | 228 | // La dernière occurence tombe le mardi 27 il ne devrait donc plus en avoir après cette date 229 | start_at = new Date(2011, 11, 28); // 1er janv. 2012 230 | end_at = new Date(2012, 11, 31); // 31 dec. 2012 231 | occurrences = scheduler.occurrences_between(start_at, end_at); 232 | console.assert(occurrences.length == 0); 233 | 234 | 235 | 236 | console.log("--- Weekly until December 24, 2011 ---"); 237 | d = new Date(2011, 10, 14, 8); 238 | start_at = new Date(1950, 0, 1); 239 | end_at = new Date(2050, 0, 1); 240 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;UNTIL=20111224T000000Z", true); 241 | occurrences = scheduler.occurrences_between(start_at, end_at); 242 | console.assert(occurrences.length == 6); 243 | console.assert(occurrences.in_array(new Date(2011, 10, 14, 8).getTime())); 244 | console.assert(occurrences.in_array(new Date(2011, 10, 21, 8).getTime())); 245 | console.assert(occurrences.in_array(new Date(2011, 10, 28, 8).getTime())); 246 | console.assert(occurrences.in_array(new Date(2011, 11, 5, 8).getTime())); 247 | console.assert(occurrences.in_array(new Date(2011, 11, 12, 8).getTime())); 248 | console.assert(occurrences.in_array(new Date(2011, 11, 19, 8).getTime())); 249 | // ==> (2011 8:00 AM EDT)November 14,21,28;December 5,12,19 250 | 251 | 252 | 253 | //// --- Every other week - forever -- 254 | // 255 | // RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU 256 | // 257 | 258 | 259 | console.log("--- Weekly on Tuesday and Thursday for 5 weeks ---"); 260 | d = new Date(1997, 8, 2, 8, 0, 0); 261 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH", true); 262 | occurrences = scheduler.all_occurrences(); 263 | console.assert(occurrences.length == 10); 264 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 8, 0, 0).getTime())); 265 | console.assert(occurrences.in_array(new Date(1997, 8, 4, 8, 0, 0).getTime())); 266 | console.assert(occurrences.in_array(new Date(1997, 8, 9, 8, 0, 0).getTime())); 267 | console.assert(occurrences.in_array(new Date(1997, 8, 11, 8, 0, 0).getTime())); 268 | console.assert(occurrences.in_array(new Date(1997, 8, 16, 8, 0, 0).getTime())); 269 | console.assert(occurrences.in_array(new Date(1997, 8, 18, 8, 0, 0).getTime())); 270 | console.assert(occurrences.in_array(new Date(1997, 8, 23, 8, 0, 0).getTime())); 271 | console.assert(occurrences.in_array(new Date(1997, 8, 25, 8, 0, 0).getTime())); 272 | console.assert(occurrences.in_array(new Date(1997, 8, 30, 8, 0, 0).getTime())); 273 | console.assert(occurrences.in_array(new Date(1997, 9, 2, 8, 0, 0).getTime())); 274 | 275 | 276 | 277 | d = new Date(1997, 8, 2, 8, 0, 0); 278 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH", true); 279 | occurrences = scheduler.all_occurrences(); 280 | console.assert(occurrences.length == 10); 281 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 8, 0, 0).getTime())); 282 | console.assert(occurrences.in_array(new Date(1997, 8, 4, 8, 0, 0).getTime())); 283 | console.assert(occurrences.in_array(new Date(1997, 8, 9, 8, 0, 0).getTime())); 284 | console.assert(occurrences.in_array(new Date(1997, 8, 11, 8, 0, 0).getTime())); 285 | console.assert(occurrences.in_array(new Date(1997, 8, 16, 8, 0, 0).getTime())); 286 | console.assert(occurrences.in_array(new Date(1997, 8, 18, 8, 0, 0).getTime())); 287 | console.assert(occurrences.in_array(new Date(1997, 8, 23, 8, 0, 0).getTime())); 288 | console.assert(occurrences.in_array(new Date(1997, 8, 25, 8, 0, 0).getTime())); 289 | console.assert(occurrences.in_array(new Date(1997, 8, 30, 8, 0, 0).getTime())); 290 | console.assert(occurrences.in_array(new Date(1997, 9, 2, 8, 0, 0).getTime())); 291 | // ==> (1997 8:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2 292 | 293 | 294 | 295 | console.log("--- Every other week on Monday, Wednesday and Friday until December 24, 1997, but starting on Tuesday, September 2, 1997 ---"); 296 | d = new Date(1997, 8, 2, 8); 297 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR", true); 298 | occurrences = scheduler.all_occurrences(); 299 | console.assert(occurrences.length == 24); 300 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 8).getTime())); 301 | console.assert(occurrences.in_array(new Date(1997, 8, 5, 8).getTime())); 302 | console.assert(occurrences.in_array(new Date(1997, 8, 15, 8).getTime())); 303 | console.assert(occurrences.in_array(new Date(1997, 8, 17, 8).getTime())); 304 | console.assert(occurrences.in_array(new Date(1997, 8, 19, 8).getTime())); 305 | console.assert(occurrences.in_array(new Date(1997, 8, 29, 8).getTime())); 306 | console.assert(occurrences.in_array(new Date(1997, 9, 1, 8).getTime())); 307 | console.assert(occurrences.in_array(new Date(1997, 9, 3, 8).getTime())); 308 | console.assert(occurrences.in_array(new Date(1997, 9, 13, 8).getTime())); 309 | console.assert(occurrences.in_array(new Date(1997, 9, 15, 8).getTime())); 310 | console.assert(occurrences.in_array(new Date(1997, 9, 17, 8).getTime())); 311 | console.assert(occurrences.in_array(new Date(1997, 9, 27, 8).getTime())); 312 | console.assert(occurrences.in_array(new Date(1997, 9, 29, 8).getTime())); 313 | console.assert(occurrences.in_array(new Date(1997, 9, 31, 8).getTime())); 314 | console.assert(occurrences.in_array(new Date(1997, 10, 10, 8).getTime())); 315 | console.assert(occurrences.in_array(new Date(1997, 10, 12, 8).getTime())); 316 | console.assert(occurrences.in_array(new Date(1997, 10, 14, 8).getTime())); 317 | console.assert(occurrences.in_array(new Date(1997, 10, 24, 8).getTime())); 318 | console.assert(occurrences.in_array(new Date(1997, 10, 26, 8).getTime())); 319 | console.assert(occurrences.in_array(new Date(1997, 10, 28, 8).getTime())); 320 | console.assert(occurrences.in_array(new Date(1997, 11, 8, 8).getTime())); 321 | console.assert(occurrences.in_array(new Date(1997, 11, 10, 8).getTime())); 322 | console.assert(occurrences.in_array(new Date(1997, 11, 12, 8).getTime())); 323 | console.assert(occurrences.in_array(new Date(1997, 11, 22, 8).getTime())); 324 | // ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October 325 | // 1,3,13,15,17 326 | // (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28; 327 | // December 8,10,12,22 328 | //// ...modification... 329 | 330 | 331 | 332 | console.log("--- Monthly on the 1st Friday for 3 occurrences ---"); 333 | d = new Date(1997, 8, 5, 8); 334 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=1FR", true); 335 | occurrences = scheduler.all_occurrences(); 336 | console.assert(occurrences.length == 3); 337 | console.assert(occurrences.in_array(new Date(1997, 8, 5, 8).getTime())); 338 | console.assert(occurrences.in_array(new Date(1997, 9, 3, 8).getTime())); 339 | console.assert(occurrences.in_array(new Date(1997, 10, 7, 8).getTime())); 340 | // ==> (1997 9:00 AM EDT)September 5;October 3;November 7; 341 | 342 | // occurrence after nov 7 shouldn't exist 343 | start_at = new Date(1997, 10, 8); 344 | end_at = new Date(1997, 11, 31); 345 | occurrences = scheduler.occurrences_between(start_at, end_at); 346 | console.assert(occurrences.length == 0); 347 | 348 | 349 | 350 | 351 | console.log("--- Monthly on the 1st Friday until December 24, 1997 ---"); 352 | d = new Date(1997, 8, 5, 11); 353 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR", true); 354 | occurrences = scheduler.all_occurrences(); 355 | console.assert(occurrences.length == 4); 356 | console.assert(occurrences.in_array(new Date(1997, 8, 5, 11).getTime())); 357 | console.assert(occurrences.in_array(new Date(1997, 9, 3, 11).getTime())); 358 | console.assert(occurrences.in_array(new Date(1997, 10, 7, 11).getTime())); 359 | console.assert(occurrences.in_array(new Date(1997, 11, 5, 11).getTime())); 360 | // ==> (1997 11:00 AM EDT)September 5;October 3 361 | // (1997 11:00 AM EST)November 7;December 5 362 | 363 | // occurrence after dec 5 shouldn't exist 364 | start_at = new Date(1997, 11, 6); 365 | end_at = new Date(1998, 1, 31); 366 | occurrences = scheduler.occurrences_between(start_at, end_at); 367 | console.assert(occurrences.length == 0); 368 | 369 | 370 | 371 | console.log("--- Every other month on the 1st and last Sunday of the month for 5 occurrences ---"); 372 | d = new Date(1997, 8, 7, 9); 373 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=5;BYDAY=1SU,-1SU", true); 374 | occurrences = scheduler.all_occurrences(); 375 | console.assert(occurrences.length == 5); 376 | console.assert(occurrences.in_array(new Date(1997, 8, 7, 9).getTime())); 377 | console.assert(occurrences.in_array(new Date(1997, 8, 28, 9).getTime())); 378 | console.assert(occurrences.in_array(new Date(1997, 10, 2, 9).getTime())); 379 | console.assert(occurrences.in_array(new Date(1997, 10, 30, 9).getTime())); 380 | console.assert(occurrences.in_array(new Date(1998, 0, 4, 9).getTime())); 381 | // ==> (1997 9:00 AM EDT)September 7,28 382 | // (1997 9:00 AM EST)November 2,30 383 | // (1998 9:00 AM EST)January 4 384 | 385 | 386 | 387 | console.log("--- Monthly on the second to last Monday of the month for 6 months ---"); 388 | d = new Date(1997, 8, 22, 9); 389 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO", true); 390 | occurrences = scheduler.all_occurrences(); 391 | console.assert(occurrences.length == 6); 392 | console.assert(occurrences.in_array(new Date(1997, 8, 22, 9).getTime())); 393 | console.assert(occurrences.in_array(new Date(1997, 9, 20, 9).getTime())); 394 | console.assert(occurrences.in_array(new Date(1997, 10, 17, 9).getTime())); 395 | console.assert(occurrences.in_array(new Date(1997, 11, 22, 9).getTime())); 396 | console.assert(occurrences.in_array(new Date(1998, 0, 19, 9).getTime())); 397 | console.assert(occurrences.in_array(new Date(1998, 1, 16, 9).getTime())); 398 | // ==> (1997 9:00 AM EDT)September 22;October 20 399 | // (1997 9:00 AM EST)November 17;December 22 400 | // (1998 9:00 AM EST)January 19;February 16 401 | 402 | 403 | 404 | console.log("--- Monthly on the third to the last day of the month, forever ---"); 405 | d = new Date(1997, 8, 28, 9); 406 | start_at = new Date(d); 407 | end_at = new Date(1998, 2, 28); 408 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;BYMONTHDAY=-3", true); 409 | occurrences = scheduler.occurrences_between(start_at, end_at); 410 | console.assert(occurrences.length == 6); 411 | console.assert(occurrences.in_array(new Date(1997, 8, 28, 9).getTime())); 412 | console.assert(occurrences.in_array(new Date(1997, 9, 29, 9).getTime())); 413 | console.assert(occurrences.in_array(new Date(1997, 10, 28, 9).getTime())); 414 | console.assert(occurrences.in_array(new Date(1997, 11, 29, 9).getTime())); 415 | console.assert(occurrences.in_array(new Date(1998, 0, 29, 9).getTime())); 416 | console.assert(occurrences.in_array(new Date(1998, 1, 26, 9).getTime())); 417 | // ==> (1997 9:00 AM EDT)September 28 418 | // (1997 9:00 AM EST)October 29;November 28;December 29 419 | // (1998 9:00 AM EST)January 29;February 26 420 | 421 | 422 | 423 | console.log("--- Monthly on the 2nd and 15th of the month for 6 occurrences ---"); 424 | d = new Date(1997, 8, 2, 9); 425 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;COUNT=6;BYMONTHDAY=2,15", true); 426 | occurrences = scheduler.all_occurrences(); 427 | console.assert(occurrences.length == 6); 428 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 9).getTime())); 429 | console.assert(occurrences.in_array(new Date(1997, 8, 15, 9).getTime())); 430 | console.assert(occurrences.in_array(new Date(1997, 9, 2, 9).getTime())); 431 | console.assert(occurrences.in_array(new Date(1997, 9, 15, 9).getTime())); 432 | console.assert(occurrences.in_array(new Date(1997, 10, 2, 9).getTime())); 433 | console.assert(occurrences.in_array(new Date(1997, 10, 15, 9).getTime())); 434 | // ==> (1997 9:00 AM EDT)September 2,15;October 2,15;November 2,15; 435 | 436 | 437 | 438 | console.log("--- Monthly on the first and last day of the month for 10 occurrences ---"); 439 | d = new Date(1997, 8, 30, 9); 440 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1", true); 441 | occurrences = scheduler.all_occurrences(); 442 | console.assert(occurrences.length == 10); 443 | console.assert(occurrences.in_array(new Date(1997, 8, 30, 9).getTime())); 444 | console.assert(occurrences.in_array(new Date(1997, 9, 1, 9).getTime())); 445 | console.assert(occurrences.in_array(new Date(1997, 9, 31, 9).getTime())); 446 | console.assert(occurrences.in_array(new Date(1997, 10, 1, 9).getTime())); 447 | console.assert(occurrences.in_array(new Date(1997, 10, 30, 9).getTime())); 448 | console.assert(occurrences.in_array(new Date(1997, 11, 1, 9).getTime())); 449 | console.assert(occurrences.in_array(new Date(1997, 11, 31, 9).getTime())); 450 | console.assert(occurrences.in_array(new Date(1998, 0, 1, 9).getTime())); 451 | console.assert(occurrences.in_array(new Date(1998, 0, 31, 9).getTime())); 452 | console.assert(occurrences.in_array(new Date(1998, 1, 1, 9).getTime())); 453 | // ==> (1997 9:00 AM EDT)September 30;October 1,31;November 1,30;December 1,31 454 | // (1998 9:00 AM EST)January 1,31;February 1 455 | 456 | 457 | 458 | console.log("--- Every 18 months on the 10th thru 15th of the month for 10 occurrences ---"); 459 | d = new Date(1997, 8, 10, 9); 460 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15", true); 461 | occurrences = scheduler.all_occurrences(); 462 | console.assert(occurrences.length == 10); 463 | console.assert(occurrences.in_array(new Date(1997, 8, 10, 9).getTime())); 464 | console.assert(occurrences.in_array(new Date(1997, 8, 11, 9).getTime())); 465 | console.assert(occurrences.in_array(new Date(1997, 8, 12, 9).getTime())); 466 | console.assert(occurrences.in_array(new Date(1997, 8, 13, 9).getTime())); 467 | console.assert(occurrences.in_array(new Date(1997, 8, 14, 9).getTime())); 468 | console.assert(occurrences.in_array(new Date(1997, 8, 15, 9).getTime())); 469 | console.assert(occurrences.in_array(new Date(1999, 2, 10, 9).getTime())); 470 | console.assert(occurrences.in_array(new Date(1999, 2, 11, 9).getTime())); 471 | console.assert(occurrences.in_array(new Date(1999, 2, 12, 9).getTime())); 472 | console.assert(occurrences.in_array(new Date(1999, 2, 13, 9).getTime())); 473 | //// ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15 474 | // (1999 9:00 AM EST)March 10,11,12,13 475 | 476 | 477 | 478 | console.log("--- Every Tuesday, every other month ---"); 479 | d = new Date(1997, 8, 2, 9); 480 | start_at = new Date(1997, 10, 18); 481 | end_at = new Date(1998, 0, 18); 482 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU", true); 483 | occurrences = scheduler.occurrences_between(start_at, end_at); 484 | console.assert(occurrences.length == 4); 485 | console.assert(occurrences.in_array(new Date(1997, 10, 18, 9).getTime())); 486 | console.assert(occurrences.in_array(new Date(1997, 10, 25, 9).getTime())); 487 | console.assert(occurrences.in_array(new Date(1998, 0, 6, 9).getTime())); 488 | console.assert(occurrences.in_array(new Date(1998, 0, 13, 9).getTime())); 489 | // ==> (1997 9:00 AM EST)November 18,25 490 | // (1998 9:00 AM EST)January 6,13 491 | 492 | 493 | 494 | console.log("--- Yearly in June and July for 10 occurrences ---"); 495 | d = new Date(1997, 5, 10, 9); 496 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7", true); 497 | occurrences = scheduler.all_occurrences(); 498 | console.assert(occurrences.length == 10); 499 | console.assert(occurrences.in_array(new Date(1997, 5, 10, 9).getTime())); 500 | console.assert(occurrences.in_array(new Date(1997, 6, 10, 9).getTime())); 501 | console.assert(occurrences.in_array(new Date(1998, 5, 10, 9).getTime())); 502 | console.assert(occurrences.in_array(new Date(1998, 6, 10, 9).getTime())); 503 | console.assert(occurrences.in_array(new Date(1999, 5, 10, 9).getTime())); 504 | console.assert(occurrences.in_array(new Date(1999, 6, 10, 9).getTime())); 505 | console.assert(occurrences.in_array(new Date(2000, 5, 10, 9).getTime())); 506 | console.assert(occurrences.in_array(new Date(2000, 6, 10, 9).getTime())); 507 | console.assert(occurrences.in_array(new Date(2001, 5, 10, 9).getTime())); 508 | console.assert(occurrences.in_array(new Date(2001, 6, 10, 9).getTime())); 509 | // ==> (1997 9:00 AM EDT)June 10;July 10 510 | // (1998 9:00 AM EDT)June 10;July 10 511 | // (1999 9:00 AM EDT)June 10;July 10 512 | // (2000 9:00 AM EDT)June 10;July 10 513 | // (2001 9:00 AM EDT)June 10;July 10 514 | // 515 | // Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components are specified, the day is gotten from DTSTART 516 | 517 | 518 | 519 | console.log("--- Every other year on January, February, and March for 10 occurrences ---"); 520 | d = new Date(1997, 2, 10, 9); 521 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3", true); 522 | occurrences = scheduler.all_occurrences(); 523 | console.assert(occurrences.length == 10); 524 | console.assert(occurrences.in_array(new Date(1997, 2, 10, 9).getTime())); 525 | console.assert(occurrences.in_array(new Date(1999, 0, 10, 9).getTime())); 526 | console.assert(occurrences.in_array(new Date(1999, 1, 10, 9).getTime())); 527 | console.assert(occurrences.in_array(new Date(1999, 2, 10, 9).getTime())); 528 | console.assert(occurrences.in_array(new Date(2001, 0, 10, 9).getTime())); 529 | console.assert(occurrences.in_array(new Date(2001, 1, 10, 9).getTime())); 530 | console.assert(occurrences.in_array(new Date(2001, 2, 10, 9).getTime())); 531 | console.assert(occurrences.in_array(new Date(2003, 0, 10, 9).getTime())); 532 | console.assert(occurrences.in_array(new Date(2003, 1, 10, 9).getTime())); 533 | console.assert(occurrences.in_array(new Date(2003, 2, 10, 9).getTime())); 534 | // ==> (1997 9:00 AM EST)March 10 535 | // (1999 9:00 AM EST)January 10;February 10;March 10 536 | // (2001 9:00 AM EST)January 10;February 10;March 10 537 | // (2003 9:00 AM EST)January 10;February 10;March 10 538 | 539 | // occurrence after 10 march 2003 shouldn't exist 540 | start_at = new Date(2003, 2, 11); 541 | end_at = new Date(2010, 0, 1); 542 | occurrences = scheduler.occurrences_between(start_at, end_at); 543 | console.assert(occurrences.length == 0); 544 | 545 | 546 | 547 | console.log("--- Every 3rd year on the 1st, 100th and 200th day for 10 occurrences ---"); 548 | d = new Date(1997, 0, 1, 9); 549 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200", true); 550 | occurrences = scheduler.all_occurrences(); 551 | console.assert(occurrences.length == 10); 552 | console.assert(occurrences.in_array(new Date(1997, 0, 1, 9).getTime())); 553 | console.assert(occurrences.in_array(new Date(1997, 3, 10, 9).getTime())); 554 | console.assert(occurrences.in_array(new Date(1997, 6, 19, 9).getTime())); 555 | console.assert(occurrences.in_array(new Date(2000, 0, 1, 9).getTime())); 556 | console.assert(occurrences.in_array(new Date(2000, 3, 9, 9).getTime())); 557 | console.assert(occurrences.in_array(new Date(2000, 6, 18, 9).getTime())); 558 | console.assert(occurrences.in_array(new Date(2003, 0, 1, 9).getTime())); 559 | console.assert(occurrences.in_array(new Date(2003, 3, 10, 9).getTime())); 560 | console.assert(occurrences.in_array(new Date(2003, 6, 19, 9).getTime())); 561 | console.assert(occurrences.in_array(new Date(2006, 0, 1, 9).getTime())); 562 | // ==> (1997 9:00 AM EST)January 1 563 | // (1997 9:00 AM EDT)April 10;July 19 564 | // (2000 9:00 AM EST)January 1 565 | // (2000 9:00 AM EDT)April 9;July 18 566 | // (2003 9:00 AM EST)January 1 567 | // (2003 9:00 AM EDT)April 10;July 19 568 | // (2006 9:00 AM EST)January 1 569 | 570 | 571 | 572 | console.log("--- Every 20th Monday of the year, forever ---"); 573 | d = new Date(1997, 4, 19, 9); 574 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;BYDAY=20MO", true); 575 | start_at = new Date(1997, 4, 1); 576 | end_at = new Date(1997, 4, 31); 577 | occurrences = scheduler.occurrences_between(start_at, end_at); 578 | console.assert(occurrences.length == 1); 579 | console.assert(occurrences.in_array(new Date(1997, 4, 19, 9).getTime())); 580 | 581 | 582 | start_at = new Date(1999, 4, 1); 583 | end_at = new Date(1999, 4, 31); 584 | occurrences = scheduler.occurrences_between(start_at, end_at); 585 | console.assert(occurrences.length == 1); 586 | console.assert(occurrences.in_array(new Date(1999, 4, 17, 9).getTime())); 587 | // ==> (1997 9:00 AM EDT)May 19 588 | // (1998 9:00 AM EDT)May 18 589 | // (1999 9:00 AM EDT)May 17 590 | // ... 591 | 592 | 593 | 594 | console.log("--- Monday of week number 20 (where the default start of the week is Monday), forever ---"); 595 | d = new Date(1997, 4, 12, 9); 596 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO", true); 597 | start_at = new Date(1997, 4, 1); 598 | end_at = new Date(1997, 4, 31); 599 | occurrences = scheduler.occurrences_between(start_at, end_at); 600 | console.assert(occurrences.length == 1); 601 | console.assert(occurrences.in_array(new Date(1997, 4, 12, 9).getTime())); 602 | 603 | 604 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO", true); 605 | start_at = new Date(1999, 4, 1); 606 | end_at = new Date(1999, 4, 31); 607 | occurrences = scheduler.occurrences_between(start_at, end_at); 608 | console.assert(occurrences.length == 1); 609 | console.assert(occurrences.in_array(new Date(1999, 4, 17, 9).getTime())); 610 | // ==> (1997 9:00 AM EDT)May 12 611 | // (1998 9:00 AM EDT)May 11 612 | // (1999 9:00 AM EDT)May 17 613 | // ... 614 | 615 | 616 | 617 | console.log("--- Every Thursday in March, forever ---"); 618 | d = new Date(1997, 2, 13, 9); 619 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH", true); 620 | start_at = new Date(1997, 2, 1); 621 | end_at = new Date(1997, 2, 31); 622 | occurrences = scheduler.occurrences_between(start_at, end_at); 623 | console.assert(occurrences.length == 3); 624 | console.assert(occurrences.in_array(new Date(1997, 2, 13, 9).getTime())); 625 | console.assert(occurrences.in_array(new Date(1997, 2, 20, 9).getTime())); 626 | console.assert(occurrences.in_array(new Date(1997, 2, 27, 9).getTime())); 627 | 628 | 629 | start_at = new Date(1999, 2, 1); 630 | end_at = new Date(1999, 2, 31); 631 | occurrences = scheduler.occurrences_between(start_at, end_at); 632 | console.assert(occurrences.length == 4); 633 | console.assert(occurrences.in_array(new Date(1999, 2, 4, 9).getTime())); 634 | console.assert(occurrences.in_array(new Date(1999, 2, 11, 9).getTime())); 635 | console.assert(occurrences.in_array(new Date(1999, 2, 18, 9).getTime())); 636 | console.assert(occurrences.in_array(new Date(1999, 2, 25, 9).getTime())); 637 | // ==> (1997 9:00 AM EST)March 13,20,27 638 | // (1998 9:00 AM EST)March 5,12,19,26 639 | // (1999 9:00 AM EST)March 4,11,18,25 640 | // ... 641 | 642 | 643 | 644 | console.log("--- Every Thursday, but only during June, July, and August, forever ---"); 645 | d = new Date(1997, 5, 5, 9); 646 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8", true); 647 | start_at = new Date(1997, 5, 15); 648 | end_at = new Date(1997, 6, 15); 649 | occurrences = scheduler.occurrences_between(start_at, end_at); 650 | console.assert(occurrences.length == 4); 651 | console.assert(occurrences.in_array(new Date(1997, 5, 19, 9).getTime())); 652 | console.assert(occurrences.in_array(new Date(1997, 5, 26, 9).getTime())); 653 | console.assert(occurrences.in_array(new Date(1997, 6, 3, 9).getTime())); 654 | console.assert(occurrences.in_array(new Date(1997, 6, 10, 9).getTime())); 655 | 656 | 657 | start_at = new Date(1999, 5, 15); 658 | end_at = new Date(1999, 6, 15); 659 | occurrences = scheduler.occurrences_between(start_at, end_at); 660 | console.assert(occurrences.length == 4); 661 | console.assert(occurrences.in_array(new Date(1999, 5, 17, 9).getTime())); 662 | console.assert(occurrences.in_array(new Date(1999, 5, 24, 9).getTime())); 663 | console.assert(occurrences.in_array(new Date(1999, 6, 1, 9).getTime())); 664 | console.assert(occurrences.in_array(new Date(1999, 6, 8, 9).getTime())); 665 | // ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;August 7,14,21,28 666 | // (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;August 6,13,20,27 667 | // (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;August 5,12,19,26 668 | // ... 669 | // ...modified... 670 | 671 | 672 | 673 | console.log("--- Every Friday the 13th, forever (occurrences between) ---"); 674 | d = new Date(1997, 8, 2, 9); 675 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13", true); 676 | start_at = new Date(1997, 8, 15); 677 | end_at = new Date(1999, 11, 31); 678 | occurrences = scheduler.occurrences_between(start_at, end_at); 679 | console.assert(occurrences.length == 4); 680 | console.assert(occurrences.in_array(new Date(1998, 1, 13, 9).getTime())); 681 | console.assert(occurrences.in_array(new Date(1998, 2, 13, 9).getTime())); 682 | console.assert(occurrences.in_array(new Date(1998, 10, 13, 9).getTime())); 683 | console.assert(occurrences.in_array(new Date(1999, 7, 13, 9).getTime())); 684 | // ==> (1998 9:00 AM EST)February 13;March 13;November 13 685 | // (1999 9:00 AM EDT)August 13 686 | // (2000 9:00 AM EDT)October 13 687 | // ... 688 | // ...modified... 689 | 690 | 691 | console.log("--- Every Friday the 13th, forever (exdate) ---"); 692 | d = new Date(1997, 8, 2, 9); 693 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;UNTIL=19991231T090000Z;BYDAY=FR;BYMONTHDAY=13", true); 694 | scheduler.add_exception_dates([ new Date(1997, 8, 2, 9) ]); 695 | occurrences = scheduler.all_occurrences(); 696 | console.assert(occurrences.length == 4); 697 | //console.assert(occurrences.in_array(new Date(1997, 8, 2, 9).getTime())); <-- exdate not matched ! 698 | console.assert(occurrences.in_array(new Date(1998, 1, 13, 9).getTime())); 699 | console.assert(occurrences.in_array(new Date(1998, 2, 13, 9).getTime())); 700 | console.assert(occurrences.in_array(new Date(1998, 10, 13, 9).getTime())); 701 | console.assert(occurrences.in_array(new Date(1999, 7, 13, 9).getTime())); 702 | // ==> (1998 9:00 AM EST)February 13;March 13;November 13 703 | // (1999 9:00 AM EDT)August 13 704 | // (2000 9:00 AM EDT)October 13 705 | // ... 706 | // ...added... 707 | 708 | 709 | 710 | console.log("--- Every four years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day) ---") 711 | d = new Date(1996, 10, 5, 9); 712 | scheduler = new Scheduler(d, "RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8", true); 713 | start_at = new Date(1996, 0, 1); 714 | end_at = new Date(2004, 11, 31); 715 | occurrences = scheduler.occurrences_between(start_at, end_at); 716 | console.assert(occurrences.length == 3); 717 | console.assert(occurrences.in_array(new Date(1996, 10, 5, 9).getTime())); 718 | console.assert(occurrences.in_array(new Date(2000, 10, 7, 9).getTime())); 719 | console.assert(occurrences.in_array(new Date(2004, 10, 2, 9).getTime())); 720 | 721 | // ==> (1996 9:00 AM EST)November 5 722 | // (2000 9:00 AM EST)November 7 723 | // (2004 9:00 AM EST)November 2 724 | 725 | 726 | console.log("--- The 3rd instance into the month of one of Tuesday, Wednesday or Thursday, for the next 3 months ---"); 727 | d = new Date(1997, 8, 4, 9); 728 | 729 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3", true); 730 | occurrences = scheduler.all_occurrences(); 731 | console.assert(occurrences.length == 3); 732 | console.assert(occurrences.in_array(new Date(1997, 8, 4, 9).getTime())); 733 | console.assert(occurrences.in_array(new Date(1997, 9, 7, 9).getTime())); 734 | console.assert(occurrences.in_array(new Date(1997, 10, 6, 9).getTime())); 735 | // ==> (1997 9:00 AM EDT)September 4;October 7;November 6 736 | 737 | 738 | 739 | console.log("--- The 2nd to last weekday of the month ---"); 740 | d = new Date(1997, 8, 29, 9); 741 | scheduler = new Scheduler(d, "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2", true); 742 | start_at = new Date(1996, 0, 1); 743 | end_at = new Date(1998, 1, 1); 744 | occurrences = scheduler.occurrences_between(start_at, end_at); 745 | console.assert(occurrences.length == 5); 746 | console.assert(occurrences.in_array(new Date(1997, 8, 29, 9).getTime())); 747 | console.assert(occurrences.in_array(new Date(1997, 9, 30, 9).getTime())); 748 | console.assert(occurrences.in_array(new Date(1997, 10, 27, 9).getTime())); 749 | console.assert(occurrences.in_array(new Date(1997, 11, 30, 9).getTime())); 750 | console.assert(occurrences.in_array(new Date(1998, 0, 29, 9).getTime())); 751 | // ==> (1997 9:00 AM EDT)September 29 752 | // (1997 9:00 AM EST)October 30;November 27;December 30 753 | // (1998 9:00 AM EST)January 29;February 26;March 30 754 | // ... 755 | 756 | 757 | 758 | console.log("--- Every 20 minutes from 9:00 AM to 4:40 PM every day ---"); 759 | d = new Date(1997, 8, 2, 9); 760 | scheduler = new Scheduler(d, "RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40", true); 761 | start_at = new Date(1996, 0, 1); 762 | end_at = new Date(2004, 11, 31); 763 | occurrences = scheduler.occurrences_between(start_at, end_at); 764 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 9, 0).getTime())); 765 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 9, 20).getTime())); 766 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 9, 40).getTime())); 767 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 10, 0).getTime())); 768 | // ... 769 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 16, 20).getTime())); 770 | console.assert(occurrences.in_array(new Date(1997, 8, 2, 16, 40).getTime())); 771 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 9, 0).getTime())); 772 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 9, 20).getTime())); 773 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 9, 40).getTime())); 774 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 10, 0).getTime())); 775 | // ... 776 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 16, 0).getTime())); 777 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 16, 20).getTime())); 778 | console.assert(occurrences.in_array(new Date(1997, 8, 3, 16, 40).getTime())); 779 | // ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20, 780 | // ... 16:00,16:20,16:40 781 | // (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20, 782 | // ...16:00,16:20,16:40 783 | 784 | 785 | console.log("--- An example where the days generated makes a difference because of WKST ---"); 786 | d = new Date(1997, 7, 5, 9); 787 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO", true); 788 | occurrences = scheduler.all_occurrences(); 789 | console.assert(occurrences.length == 4); 790 | console.assert(occurrences.in_array(new Date(1997, 7, 5, 9).getTime())); 791 | console.assert(occurrences.in_array(new Date(1997, 7, 10, 9).getTime())); 792 | console.assert(occurrences.in_array(new Date(1997, 7, 19, 9).getTime())); 793 | console.assert(occurrences.in_array(new Date(1997, 7, 24, 9).getTime())); 794 | // ==> (1997 EDT)Aug 5,10,19,24 795 | 796 | 797 | 798 | scheduler = new Scheduler(d, "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU", true); 799 | occurrences = scheduler.all_occurrences(); 800 | console.assert(occurrences.length == 4); 801 | console.assert(occurrences.in_array(new Date(1997, 7, 5, 9).getTime())); 802 | console.assert(occurrences.in_array(new Date(1997, 7, 17, 9).getTime())); 803 | console.assert(occurrences.in_array(new Date(1997, 7, 19, 9).getTime())); 804 | console.assert(occurrences.in_array(new Date(1997, 7, 31, 9).getTime())); 805 | // ==> (1997 EDT)August 5,17,19,31 806 | 807 | 808 | console.log("--- Birthday ---"); 809 | d = new Date(2011, 0, 1); 810 | start_at = new Date(2011, 0, 1); 811 | end_at = new Date(2014, 0, 1); 812 | scheduler = new Scheduler(d, "FREQ=YEARLY;INTERVAL=1", true); 813 | occurrences = scheduler.occurrences_between(start_at, end_at); 814 | console.assert(occurrences.length == 4); 815 | console.assert(occurrences[0] == new Date(2011, 0, 1).getTime()); 816 | console.assert(occurrences[1] == new Date(2012, 0, 1).getTime()); 817 | console.assert(occurrences[2] == new Date(2013, 0, 1).getTime()); 818 | console.assert(occurrences[3] == new Date(2014, 0, 1).getTime()); -------------------------------------------------------------------------------- /rrecur-parser.js: -------------------------------------------------------------------------------- 1 | // used for IE < 9 2 | // adapted from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf 3 | if (!Array.prototype.indexOf) 4 | { 5 | Array.prototype.indexOf = function(elt /*, from*/) 6 | { 7 | var len = this.length >>> 0; 8 | 9 | var from = Number(arguments[1]) || 0; 10 | from = (from < 0) 11 | ? Math.ceil(from) 12 | : Math.floor(from); 13 | if (from < 0) 14 | from += len; 15 | 16 | for (; from < len; from++) 17 | { 18 | if (from in this && 19 | this[from] === elt) 20 | return from; 21 | } 22 | return -1; 23 | }; 24 | } 25 | 26 | Array.prototype.in_array = function(test_var) { // useful method for "class" Array 27 | return this.indexOf(test_var, 0) != -1; 28 | } 29 | 30 | Date.countMonthDays = function(y, m) { // m : 0->11 31 | return (new Date(y, m + 1, 0)).getDate(); 32 | } 33 | 34 | Date.findNthWeekDays = function(y, m, nth, week_day) { // m : false or 0->11, week_day : 0->SU, 1->MO, ..., nth : N (Nth from start of month), -N (Nth from end of month), 0 (all) 35 | var dates = []; 36 | if (nth >= 0) { 37 | if (m === false) { // from start of year 38 | var date = new Date(y, 0, 1); 39 | var end_year_ts = new Date(y + 1, 0, 1).getTime(); 40 | var count = 0; 41 | while (date.getTime() < end_year_ts) { 42 | if (date.getDay() == week_day) { 43 | count++; 44 | if (nth == 0 || count == nth) { 45 | dates.push(date); 46 | } 47 | } 48 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate()+1); 49 | } 50 | } else { // from start of month 51 | date = new Date(y, m, 1); 52 | var end_month_ts = new Date(y, m + 1, 1).getTime(); 53 | count = 0; 54 | while (date.getTime() < end_month_ts) { 55 | if (date.getDay() == week_day) { 56 | count++; 57 | if (nth == 0 || count == nth) { 58 | dates.push(date); 59 | } 60 | } 61 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate()+1); 62 | } 63 | } 64 | 65 | } else if (nth < 0) { 66 | if (m === false) { // from end of year 67 | nth = Math.abs(nth); 68 | date = new Date(y + 1, 0, 0); 69 | var begin_year_ts = new Date(y, 0, 1).getTime(); 70 | count = 0; 71 | while (date.getTime() >= begin_year_ts) { 72 | if (date.getDay() == week_day) { 73 | count++; 74 | if (nth == 0 || count == nth) { 75 | dates.push(date); 76 | } 77 | } 78 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate()-1); 79 | } 80 | } else { // from end of month 81 | nth = Math.abs(nth); 82 | date = new Date(y, m + 1, 0); 83 | var begin_month_ts = new Date(y, m, 1).getTime(); 84 | count = 0; 85 | while (date.getTime() >= begin_month_ts) { 86 | if (date.getDay() == week_day) { 87 | count++; 88 | if (nth == 0 || count == nth) { 89 | dates.push(date); 90 | } 91 | } 92 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate()-1); 93 | } 94 | } 95 | 96 | } 97 | return dates; 98 | } 99 | 100 | Date.prototype.isSameDate = function(other_date) { 101 | return other_date instanceof Date && 102 | this.getFullYear() == other_date.getFullYear() && 103 | this.getMonth() == other_date.getMonth() && 104 | this.getDate() == other_date.getDate(); 105 | 106 | } 107 | 108 | Date.fromWeek = function(nth, y, wkday){ // nth : nth week (1 : first week) of the year y, wkday : day of the week to retrieve (0->SU, 1->MO, ...) 109 | nth = nth - 1; 110 | y = y || new Date().getFullYear(); 111 | var d1= new Date(y, 0, 4); 112 | if (wkday == undefined) wkday = 1; 113 | return d1.nextWeek(wkday, nth); 114 | } 115 | 116 | Date.prototype.nextWeek = function(wd, nth){ // wd : 0->SU, 1->MO, ... (default : same day), nth : how many week to add (default 1) 117 | if(nth== undefined) nth= 1; 118 | var incr= nth < 0? 1: -1, 119 | D= new Date(this), dd= D.getDay(); 120 | if(wd== undefined) wd= dd; 121 | while(D.getDay()!= wd) D.setDate(D.getDate()+ incr); 122 | D.setDate(D.getDate()+ 7*nth); 123 | return D; 124 | } 125 | 126 | /** 127 | * Returns the week number for this date. dowOffset is the day of week the week 128 | * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), 129 | * the week returned is the ISO 8601 week number. 130 | * @param dowOffset 131 | * @return int 132 | */ 133 | Date.prototype.getWeekNo = function (dowOffset) { // 0 -> wkst SU, 1 -> wkst MO 134 | /*getWeek() was developed by Nick Baicoianu at MeanFreePath: http://www.meanfreepath.com */ 135 | 136 | dowOffset = dowOffset !== undefined ? dowOffset : 0; //default dowOffset to zero 137 | var newYear = new Date(this.getFullYear(),0,1); 138 | var day = newYear.getDay() - dowOffset; //the day of week the year begins on 139 | day = (day >= 0 ? day : day + 7); 140 | var daynum = Math.floor((this.getTime() - newYear.getTime() - 141 | (this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1; 142 | var weeknum; 143 | //if the year starts before the middle of a week 144 | if(day < 4) { 145 | weeknum = Math.floor((daynum+day-1)/7) + 1; 146 | if(weeknum > 52) { 147 | var nYear = new Date(this.getFullYear() + 1,0,1); 148 | var nday = nYear.getDay() - dowOffset; 149 | nday = nday >= 0 ? nday : nday + 7; 150 | /*if the next year starts before the middle of 151 | the week, it is week #1 of that year*/ 152 | weeknum = nday < 4 ? 1 : 53; 153 | } 154 | } else { 155 | weeknum = Math.floor((daynum+day-1)/7); 156 | } 157 | return weeknum; 158 | }; 159 | 160 | Scheduler = function(start_date, rfc_rrule, test_mode) { // Scheduler "class" (global visibility) 161 | this.init_recurrence_rules = function() { 162 | // mandatory 163 | this.rrule_freq = false; 164 | 165 | // both count & until are forbidden 166 | this.rrule_count = false; 167 | this.rrule_until = false; 168 | 169 | // facultative 170 | this.rrule_interval = 1; 171 | this.rrule_bysecond = false; 172 | this.rrule_byminute = false; 173 | this.rrule_byhour = false; 174 | this.rrule_byday = false; // +1, -2, etc. only for monthly or yearly 175 | this.rrule_bymonthday = false; 176 | this.rrule_byyearday = false; 177 | this.rrule_byweekno = false; // only for yearly 178 | this.rrule_bymonth = false; 179 | this.rrule_bysetpos = false; // only in conjonction with others BYxxx rules 180 | this.rrule_wkst = "MO"; // significant where weekly interval > 1 & where yearly byweekno is specified 181 | } 182 | 183 | this.test_mode = test_mode === true ? true : false; 184 | this.start_date = start_date; 185 | this.start_ts = start_date.getTime(); 186 | 187 | this.init_recurrence_rules(); 188 | this.exception_dates = []; 189 | this.rdates = []; 190 | 191 | this.dayFromDayNo = [ "SU", "MO", "TU", "WE", "TH", "FR", "SA" ]; 192 | this.dayNoFromDay = { 193 | SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6 194 | }; 195 | 196 | if (rfc_rrule) { 197 | this.add_recurrence_rules(rfc_rrule); 198 | } 199 | 200 | }; 201 | 202 | // adds at least one RRULE 203 | Scheduler.prototype.add_recurrence_rules = function(rfc_rrule) { 204 | if (rfc_rrule.indexOf("RRULE:") == 0) { // removes "RRULE:" if needed 205 | rfc_rrule = rfc_rrule.slice(6); 206 | } 207 | 208 | var rules = rfc_rrule.split(";"); 209 | var nb_rules = rules.length; 210 | 211 | for (var i = 0; i < nb_rules; i++) { 212 | var rule = rules[i].split("="); 213 | var rule_value = rule[1]; 214 | switch (rule[0]) { // rule name 215 | case "FREQ": 216 | this.rrule_freq = rule_value; 217 | break; 218 | case "UNTIL": 219 | var until = rule_value; 220 | var y = until.substr(0, 4); 221 | var m = until.substr(4, 2) - 1; // js Date month -> 0 to 11 222 | var d = until.substr(6, 2); 223 | if (until.length > 8) { 224 | var h = until.substr(9, 2); 225 | var min = until.substr(11, 2); 226 | var s = until.substr(13, 2); 227 | this.rrule_until = new Date(y, m, d, h, min, s); 228 | //this.rrule_until = this.rrule_until.getTime() - this.rrule_until.getTimezoneOffset() * 60000; 229 | } else { 230 | this.rrule_until = new Date(y, m, d).getTime(); 231 | } 232 | 233 | break; 234 | case "COUNT": 235 | this.rrule_count = rule_value; 236 | break; 237 | case "INTERVAL": 238 | this.rrule_interval = rule_value; 239 | break; 240 | case "BYSECOND": 241 | this.rrule_bysecond = rule_value.split(","); 242 | break; 243 | case "BYMINUTE": 244 | this.rrule_byminute = rule_value.split(","); 245 | break; 246 | case "BYHOUR": 247 | this.rrule_byhour = rule_value.split(","); 248 | break; 249 | case "BYDAY": 250 | this.rrule_byday = rule_value.split(","); 251 | break; 252 | case "BYMONTHDAY": 253 | if (this.rrule_freq != "WEEKLY") { 254 | this.rrule_bymonthday = rule_value.split(","); 255 | } 256 | break; 257 | case "BYYEARDAY": 258 | if (this.rrule_freq == "YEARLY") { 259 | this.rrule_byyearday = rule_value.split(","); 260 | } 261 | break; 262 | case "BYWEEKNO": 263 | if (this.rrule_freq == "YEARLY") { 264 | this.rrule_byweekno = rule_value.split(","); 265 | } 266 | break; 267 | case "BYMONTH": 268 | this.rrule_bymonth = rule_value.split(","); 269 | break; 270 | case "BYSETPOS": 271 | this.rrule_bysetpos = rule_value.split(","); 272 | this.rrule_bysetpos.sort(function(a,b) { 273 | return parseInt(a, 10) - parseInt(b, 10); 274 | }); 275 | break; 276 | case "WKST": 277 | this.rrule_wkst = rule_value; 278 | break; 279 | } 280 | } 281 | 282 | //if BYSECOND, BYMINUTE, BYHOUR, BYDAY, BYMONTHDAY or BYMONTH unspecified, fetch values from start date 283 | if (!this.rrule_bysecond) { 284 | this.rrule_bysecond = [ this.start_date.getSeconds().toString() ]; 285 | } 286 | if (!this.rrule_byminute) { 287 | this.rrule_byminute = [ this.start_date.getMinutes().toString() ]; 288 | } 289 | if (!this.rrule_byhour) { 290 | this.rrule_byhour = [ this.start_date.getHours().toString() ]; 291 | } 292 | if (!this.rrule_byday && this.rrule_freq == "WEEKLY") {// auto value only when freq=weekly i guess... 293 | this.rrule_byday = [ this.dayFromDayNo[this.start_date.getDay()] ]; 294 | } 295 | if (!this.rrule_byday && !this.rrule_bymonthday && !this.rrule_byyearday && (this.rrule_freq == "MONTHLY" || this.rrule_freq == "YEARLY")) { 296 | this.rrule_bymonthday = [ this.start_date.getDate().toString() ]; 297 | } 298 | if (!this.rrule_byday && !this.rrule_byyearday && !this.rrule_bymonth && this.rrule_freq == "YEARLY") { 299 | this.rrule_bymonth = [ (this.start_date.getMonth() + 1).toString() ]; 300 | } 301 | } 302 | 303 | // removes all RRULEs 304 | Scheduler.prototype.remove_recurrence_rules = function() { 305 | this.init_recurrence_rules(); 306 | } 307 | 308 | // adds at least one EXDATE (optional) 309 | Scheduler.prototype.add_exception_dates = function(dates) { 310 | if(dates) { 311 | var nb_date = dates.length; 312 | for (var i = 0; i < nb_date; i++) { 313 | if(typeof dates[i] == 'string') { 314 | var date = dates[i] 315 | dates[i] = parseISO8601(date); 316 | if(isNaN(dates[i])) { 317 | dates[i] = new Date(date); 318 | } 319 | } 320 | if(typeof dates[i] == "number") { 321 | this.exception_dates.push(dates[i]); 322 | } 323 | else { 324 | this.exception_dates.push(dates[i].getTime()); 325 | } 326 | } 327 | this.exception_dates.sort(); 328 | } 329 | } 330 | 331 | // removes all EXDATEs 332 | Scheduler.prototype.remove_exception_dates = function() { 333 | this.exception_dates = []; 334 | } 335 | 336 | // adds at least one RDATE (optional) 337 | Scheduler.prototype.add_rdates = function(dates) { 338 | if(dates) { 339 | var nb_date = dates.length; 340 | for (var i = 0; i < nb_date; i++) { 341 | if(typeof dates[i] == 'string') { 342 | var date = dates[i] 343 | dates[i] = parseISO8601(date); 344 | if(isNaN(dates[i])) { 345 | dates[i] = new Date(date); 346 | } 347 | } 348 | if(typeof dates[i] == "number") { 349 | this.rdates.push(dates[i]); 350 | } 351 | else { 352 | this.rdates.push(dates[i].getTime()); 353 | } 354 | } 355 | this.rdates.sort(); 356 | } 357 | } 358 | 359 | // removes all RDATEs 360 | Scheduler.prototype.remove_rdates = function() { 361 | this.rdates = []; 362 | } 363 | 364 | // retourns all occurrences as Date array (test mode => timestamp array) 365 | Scheduler.prototype.all_occurrences = function(filter_begin_ts, filter_end_ts) { 366 | var occurrences = []; 367 | if ((filter_begin_ts === undefined || filter_end_ts === undefined) && 368 | this.rrule_count === false && this.rrule_until === false) { 369 | return null; // infinity of results => must be processed with filter_begin_ts & filter_end_ts 370 | } 371 | 372 | var current_date = this.start_date; 373 | var count = 0; // used to filter by rrule_count 374 | var count_period = 0; // used to process intervals 375 | 376 | period_loop: 377 | while ((this.rrule_count === false || count < this.rrule_count) 378 | && (this.rrule_until === false || current_date.getTime() <= this.rrule_until) 379 | && (filter_end_ts === undefined || current_date.getTime() <= filter_end_ts)) { 380 | 381 | var day = this.dayFromDayNo[current_date.getDay()]; 382 | var d = current_date.getDate(); 383 | var m = current_date.getMonth() + 1; 384 | var y = current_date.getFullYear(); 385 | var week_no = current_date.getWeekNo(this.rrule_wkst == "MO" ? 1 : 0); // 1 to 53 386 | var h = current_date.getHours(); 387 | var min = current_date.getMinutes(); 388 | var s = current_date.getSeconds(); 389 | 390 | this.current_pos = 1; // used in rrule_bysetpos 391 | this.old_pos = []; // used when bysetpos is a negative number 392 | 393 | if (count_period % this.rrule_interval == 0 && this.check_rules(day, d, m, week_no, h, min, s)) { // current date matches interval AND rules byday, bymonthday, bymonth 394 | if (this.rrule_freq == "DAILY") { 395 | for (var h_it = 0; h_it < this.rrule_byhour.length; h_it++) { 396 | for (var min_it = 0; min_it < this.rrule_byminute.length; min_it++) { 397 | for (var s_it = 0; s_it < this.rrule_bysecond.length; s_it++) { 398 | var date_to_push = new Date(y, (m-1), d, this.rrule_byhour[h_it], this.rrule_byminute[min_it], this.rrule_bysecond[s_it]); 399 | var ts_to_push = date_to_push.getTime(); 400 | if (this.rrule_bysetpos !== false && !this.rrule_bysetpos.in_array(this.current_pos.toString())) { 401 | this.current_pos++; 402 | this.old_pos.push(date_to_push); 403 | continue; 404 | } 405 | if ((this.rrule_until !== false && ts_to_push > this.rrule_until) || 406 | (filter_end_ts !== undefined && ts_to_push > filter_end_ts)) { 407 | break period_loop; 408 | } 409 | if (ts_to_push >= this.start_ts) { 410 | if (filter_begin_ts === undefined || ts_to_push >= filter_begin_ts) { 411 | occurrences.push(date_to_push); 412 | } 413 | count++; 414 | } 415 | 416 | this.current_pos++; 417 | this.old_pos.push(date_to_push); 418 | 419 | if (this.rrule_count !== false && count >= this.rrule_count) { 420 | break period_loop; 421 | } 422 | } 423 | } 424 | } 425 | } else if (["WEEKLY", "MONTHLY", "YEARLY"].in_array(this.rrule_freq)) { 426 | switch (this.rrule_freq) { 427 | case "WEEKLY": 428 | var period_begin = Date.fromWeek(week_no, y, this.rrule_wkst == "MO" ? 1 : 0); 429 | var until = Date.fromWeek(week_no + 1, y, this.rrule_wkst == "MO" ? 1 : 0); 430 | break; 431 | case "MONTHLY": 432 | period_begin = new Date(y, m - 1, 1) 433 | until = new Date(y, m, 1); 434 | break; 435 | case "YEARLY": 436 | period_begin = new Date(y, 0, 1); 437 | until = new Date(y + 1, 0, 1); 438 | break; 439 | } 440 | 441 | var it_date = period_begin; 442 | 443 | while (it_date.getTime() < until.getTime()) { 444 | var it_date_ts = it_date.getTime(); 445 | if ((this.rrule_until !== false && it_date_ts > this.rrule_until) || 446 | (filter_end_ts !== undefined && it_date_ts > filter_end_ts)) { 447 | break period_loop; 448 | } 449 | 450 | if (this.check_day(it_date)) { 451 | 452 | for (h_it = 0; h_it < this.rrule_byhour.length; h_it++) { 453 | for (min_it = 0; min_it < this.rrule_byminute.length; min_it++) { 454 | for (s_it = 0; s_it < this.rrule_bysecond.length; s_it++) { 455 | date_to_push = new Date(it_date.getFullYear(), it_date.getMonth(), it_date.getDate(), this.rrule_byhour[h_it], this.rrule_byminute[min_it], this.rrule_bysecond[s_it]); 456 | ts_to_push = date_to_push.getTime(); 457 | if (this.rrule_bysetpos !== false && !this.rrule_bysetpos.in_array(this.current_pos.toString())) { 458 | this.current_pos++; 459 | this.old_pos.push(date_to_push); 460 | continue; 461 | } 462 | if ((this.rrule_until !== false && ts_to_push > this.rrule_until) || 463 | (filter_end_ts !== undefined && ts_to_push > filter_end_ts)) { 464 | break period_loop; 465 | } 466 | if (ts_to_push >= this.start_ts) { 467 | if (filter_begin_ts === undefined || ts_to_push >= filter_begin_ts) { 468 | occurrences.push(date_to_push); 469 | } 470 | count++; 471 | } 472 | 473 | this.current_pos++; 474 | this.old_pos.push(date_to_push); 475 | 476 | if (this.rrule_count !== false && count >= this.rrule_count) { 477 | break period_loop; 478 | } 479 | } 480 | } 481 | } 482 | } 483 | it_date = new Date(it_date); 484 | it_date.setDate(it_date.getDate() + 1); 485 | } 486 | // process negative values of rrule_bysetpos 487 | if (this.rrule_bysetpos instanceof Array) { 488 | for (var it_pos = 0; it_pos < this.rrule_bysetpos.length; it_pos++) { 489 | var pos = parseInt(this.rrule_bysetpos[it_pos], 10); 490 | if (pos < 0) { 491 | pos = Math.abs(pos); 492 | var last_matching_dates = this.old_pos.reverse(); 493 | var matching_date = last_matching_dates[pos - 1]; 494 | if (matching_date && matching_date >= this.start_ts) { 495 | occurrences.push(matching_date); 496 | count++; 497 | } 498 | if (this.rrule_count !== false && count >= this.rrule_count) { 499 | break period_loop; 500 | } 501 | } 502 | } 503 | } 504 | } 505 | } 506 | 507 | count_period++; 508 | current_date = this.next_period(current_date); 509 | } 510 | 511 | // removes exdates 512 | var nb_occurrences = occurrences.length; 513 | var occurrences_without_exdates = []; 514 | for (var i = 0; i < nb_occurrences; i++) { 515 | var occurrence = occurrences[i]; 516 | var ts = occurrence.getTime(); 517 | if (!(this.exception_dates.in_array(ts))) { 518 | occurrences_without_exdates.push(this.test_mode ? ts : occurrence); 519 | } 520 | } 521 | 522 | // add rdates 523 | var nb_rdates = this.rdates.length; 524 | for (var i = 0; i < nb_rdates; i++) { 525 | var occurrence = new Date(this.rdates[i]); 526 | if((filter_begin_ts !== undefined && occurrence < filter_begin_ts) || (filter_end_ts !== undefined && occurrence > filter_end_ts)) { 527 | continue; 528 | } 529 | else { 530 | occurrences_without_exdates.push(this.test_mode ? occurrence.getTime() : occurrence); 531 | } 532 | } 533 | 534 | return occurrences_without_exdates; 535 | } 536 | 537 | Scheduler.prototype.next_period = function(date) { 538 | switch (this.rrule_freq) { 539 | case "DAILY": 540 | var new_date = new Date(date); 541 | new_date.setDate(date.getDate() + 1); 542 | return new_date; 543 | case "WEEKLY": 544 | return date.nextWeek(); 545 | case "MONTHLY": 546 | new_date = new Date(date); 547 | new_date.setMonth(date.getMonth() + 1, 1); 548 | return new_date; 549 | case "YEARLY": 550 | new_date = new Date(date); 551 | new_date.setFullYear(date.getFullYear() + 1); 552 | return new_date; 553 | } 554 | 555 | } 556 | 557 | // check validity of selected rules 558 | Scheduler.prototype.check_rules = function(day, d, m, week_no, h, min, s) { 559 | switch (this.rrule_freq) { 560 | case "DAILY": 561 | return ((m === undefined || this.rrule_bymonth === false || this.rrule_bymonth.in_array(m.toString())) && 562 | (d === undefined || this.rrule_bymonthday === false || this.rrule_bymonthday.in_array(d.toString())) && 563 | (day === undefined || this.rrule_byday === false || this.rrule_byday.in_array(day.toString()))); 564 | case "WEEKLY": 565 | return ((m === undefined || this.rrule_bymonth === false || this.rrule_bymonth.in_array(m.toString())) && 566 | (d === undefined || this.rrule_bymonthday === false || this.rrule_bymonthday.in_array(d.toString()))); 567 | case "MONTHLY": 568 | return (m === undefined || this.rrule_bymonth === false || this.rrule_bymonth.in_array(m.toString())); 569 | case "YEARLY": 570 | return true; 571 | } 572 | } 573 | 574 | // retourns occurrences in the range [ begin_date, end_date ] as Date array (test mode => timestamp array) 575 | Scheduler.prototype.occurrences_between = function(begin_date, end_date) { 576 | var begin_ts = begin_date.getTime(); 577 | var end_ts = end_date.getTime(); 578 | 579 | return this.all_occurrences(begin_ts, end_ts); 580 | } 581 | 582 | Scheduler.prototype.check_day = function(date) { 583 | var is_yearly = (this.rrule_freq == "YEARLY"); 584 | var is_weekly = (this.rrule_freq == "WEEKLY"); 585 | 586 | var day = this.dayFromDayNo[date.getDay()]; 587 | var d = date.getDate(); 588 | var m = date.getMonth() + 1; 589 | var y = date.getFullYear(); 590 | if (is_yearly) { 591 | var week_no = date.getWeekNo(this.rrule_wkst == "MO" ? 1 : 0); // 1 to 53 592 | } 593 | 594 | // check rrule_bymonth 595 | if (this.rrule_bymonth !== false) { 596 | if (!this.rrule_bymonth.in_array(m.toString())) { 597 | return false; 598 | } 599 | } 600 | 601 | // check rrule_byday 602 | if (this.rrule_byday !== false) { 603 | if (is_weekly) { 604 | if (!this.rrule_byday.in_array(day)) { 605 | return false; 606 | } 607 | } else { 608 | found = false; 609 | for (var it_wd = 0; it_wd < this.rrule_byday.length; it_wd++) { 610 | var rule_byday = /([+0-9-]*)([A-Z]+)/.exec(this.rrule_byday[it_wd]); 611 | var matching_dates = Date.findNthWeekDays(y, is_yearly ? false : m - 1, rule_byday[1] ? rule_byday[1] : 0, this.dayNoFromDay[rule_byday[2]]); 612 | 613 | for (var it = 0; it < matching_dates.length; it++) { 614 | if (date.isSameDate(matching_dates[it])) { 615 | found = true; 616 | break; 617 | } 618 | } 619 | } 620 | if (!found) { 621 | return false; 622 | } 623 | } 624 | } 625 | 626 | if (!is_weekly) { 627 | // check rrule_bymonthday 628 | if (this.rrule_bymonthday !== false) { 629 | var month_days_count = Date.countMonthDays(y, m - 1); 630 | var d_neg = d - 1 - month_days_count; 631 | var found = false; 632 | for (var it_md = 0; it_md < this.rrule_bymonthday.length; it_md++) { 633 | var md = parseInt(this.rrule_bymonthday[it_md], 10); 634 | if (d == md || d_neg == md) { 635 | found = true; 636 | break; 637 | } 638 | } 639 | if (!found) { 640 | return false; 641 | } 642 | } 643 | } 644 | 645 | if (is_yearly) { 646 | 647 | // check rrule_byyearday 648 | if (this.rrule_byyearday !== false) { 649 | found = false; 650 | for (var it_yd = 0; it_yd < this.rrule_byyearday.length; it_yd++) { 651 | var year_day = this.rrule_byyearday[it_yd]; 652 | if (year_day > 0) { 653 | var year_date = new Date(y, 0, parseInt(year_day, 10)); 654 | if (date.isSameDate(year_date)) { 655 | found = true; 656 | break; 657 | } 658 | } else if (year_day < 0) { 659 | year_date = new Date(y + 1, 0, 1 + parseInt(year_day, 10)); 660 | if (date.isSameDate(year_date)) { 661 | found = true; 662 | break; 663 | } 664 | } else { 665 | continue; 666 | } 667 | } 668 | if (!found) { 669 | return false; 670 | } 671 | } 672 | 673 | // check rrule_byweekno 674 | if (this.rrule_byweekno !== false) { 675 | found = false; 676 | for (var it_wkno = 0; it_wkno < this.rrule_byweekno.length; it_wkno++) { 677 | var a_week_no = this.rrule_byweekno[it_wkno]; 678 | if (a_week_no) 679 | if (a_week_no > 0) { 680 | if (a_week_no == week_no) { 681 | found = true; 682 | break; 683 | } 684 | } else if (a_week_no < 0) { 685 | var year_week_count = new Date(y, 11, 31).getWeekNo(); 686 | if (week_no == year_week_count + 1 + a_week_no) { 687 | found = true; 688 | break; 689 | } 690 | } else { 691 | continue; 692 | } 693 | } 694 | if (!found) { 695 | return false; 696 | } 697 | } 698 | } 699 | 700 | // check rrule_bysetpos processed in period_loop 701 | return true; 702 | } 703 | 704 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |