node-schedule 1.2.1 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +11 -10
- package/.travis.yml +9 -2
- package/README.md +29 -8
- package/lib/schedule.js +86 -25
- package/package.json +4 -4
- package/test/convenience-method-test.js +0 -713
- package/test/date-convenience-methods-test.js +0 -59
- package/test/es6/job-test.js +0 -33
- package/test/job-test.js +0 -494
- package/test/range-test.js +0 -77
- package/test/recurrence-rule-test.js +0 -294
- package/test/schedule-cron-jobs.js +0 -136
- package/test/start-end-test.js +0 -312
package/.eslintrc
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"env": {
|
|
3
|
-
"node": true
|
|
3
|
+
"node": true,
|
|
4
|
+
"es6": true
|
|
4
5
|
},
|
|
5
6
|
"rules": {
|
|
6
|
-
"eqeqeq": [
|
|
7
|
-
"indent": [2,
|
|
8
|
-
"no-constant-condition":
|
|
9
|
-
"no-redeclare":
|
|
10
|
-
"no-underscore-dangle":
|
|
11
|
-
"no-use-before-define": [
|
|
12
|
-
"quotes": [
|
|
13
|
-
"space-before-blocks":
|
|
14
|
-
"strict":
|
|
7
|
+
"eqeqeq": ["error", "smart"],
|
|
8
|
+
"indent": ["error", 2, { "SwitchCase": 1 }],
|
|
9
|
+
"no-constant-condition": "off",
|
|
10
|
+
"no-redeclare": "warn",
|
|
11
|
+
"no-underscore-dangle": "off",
|
|
12
|
+
"no-use-before-define": ["warn","nofunc"],
|
|
13
|
+
"quotes": ["error", "single"],
|
|
14
|
+
"space-before-blocks": "error",
|
|
15
|
+
"strict": "error"
|
|
15
16
|
}
|
|
16
17
|
}
|
package/.travis.yml
CHANGED
|
@@ -3,6 +3,13 @@ node_js:
|
|
|
3
3
|
- "0.12"
|
|
4
4
|
- "0.10"
|
|
5
5
|
- "4"
|
|
6
|
-
- "
|
|
7
|
-
|
|
6
|
+
- "6"
|
|
7
|
+
- "7"
|
|
8
|
+
before_script: >
|
|
9
|
+
if nvm ls-remote --lts | grep "$(nvm current)"; then
|
|
10
|
+
echo "running on a LTS node version, linting"
|
|
11
|
+
npm test
|
|
12
|
+
else
|
|
13
|
+
echo "running on a non-LTS node version, skipping linting"
|
|
14
|
+
fi
|
|
8
15
|
script: ./node_modules/.bin/istanbul cover ./node_modules/.bin/nodeunit test && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage
|
package/README.md
CHANGED
|
@@ -48,8 +48,9 @@ They also emit a `scheduled` event each time they're scheduled to run, and a
|
|
|
48
48
|
`canceled` event when an invocation is canceled before it's executed (both events
|
|
49
49
|
receive a JavaScript date object as a parameter). Note that jobs are scheduled the
|
|
50
50
|
first time immediately, so if you create a job using the `scheduleJob()`
|
|
51
|
-
convenience method, you'll miss the first `scheduled` event
|
|
52
|
-
`canceled` is the single-L American
|
|
51
|
+
convenience method, you'll miss the first `scheduled` event, but you can query the
|
|
52
|
+
invocation manually (see below). Also note that `canceled` is the single-L American
|
|
53
|
+
spelling.
|
|
53
54
|
|
|
54
55
|
### Cron-style Scheduling
|
|
55
56
|
|
|
@@ -110,12 +111,6 @@ var j = schedule.scheduleJob(date, function(){
|
|
|
110
111
|
});
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
You can invalidate the job with the `cancel()` method:
|
|
114
|
-
|
|
115
|
-
```js
|
|
116
|
-
j.cancel();
|
|
117
|
-
```
|
|
118
|
-
|
|
119
114
|
To use current data in the future you can use binding:
|
|
120
115
|
|
|
121
116
|
```js
|
|
@@ -200,6 +195,32 @@ var j = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * *
|
|
|
200
195
|
});
|
|
201
196
|
```
|
|
202
197
|
|
|
198
|
+
### Handle Jobs and Job Invocations
|
|
199
|
+
|
|
200
|
+
There are some function to get informations for a Job and to handle the Job and
|
|
201
|
+
Invocations.
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
#### job.cancel(reshedule)
|
|
205
|
+
You can invalidate any job with the `cancel()` method:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
j.cancel();
|
|
209
|
+
```
|
|
210
|
+
All planned invocations will be canceled. When you set the parameter ***reschedule***
|
|
211
|
+
to true then the Job is newly scheduled afterwards.
|
|
212
|
+
|
|
213
|
+
#### job.cancelNext(reshedule)
|
|
214
|
+
This method invalidates the next planned invocation or the job.
|
|
215
|
+
When you set the parameter ***reschedule*** to true then the Job is newly scheduled
|
|
216
|
+
afterwards.
|
|
217
|
+
|
|
218
|
+
#### job.reschedule(spec)
|
|
219
|
+
This method cancels all pending invocation and registers the Job completely new again using the given specification.
|
|
220
|
+
Return true/false on success/failure.
|
|
221
|
+
|
|
222
|
+
#### job.nextInvocation()
|
|
223
|
+
This method returns a Date object for the planned next Invocation for this Job. If no invocation is planned the method returns null.
|
|
203
224
|
|
|
204
225
|
## Contributing
|
|
205
226
|
|
package/lib/schedule.js
CHANGED
|
@@ -15,6 +15,7 @@ var events = require('events'),
|
|
|
15
15
|
|
|
16
16
|
/* Job object */
|
|
17
17
|
var anonJobCounter = 0;
|
|
18
|
+
var scheduledJobs = {};
|
|
18
19
|
|
|
19
20
|
function isValidDate(date) {
|
|
20
21
|
// Taken from http://stackoverflow.com/a/12372720/1562178
|
|
@@ -185,33 +186,40 @@ Job.prototype.schedule = function(spec) {
|
|
|
185
186
|
var inv;
|
|
186
187
|
var start;
|
|
187
188
|
var end;
|
|
189
|
+
var tz;
|
|
188
190
|
|
|
189
191
|
if (typeof spec === 'object' && spec.rule) {
|
|
190
|
-
start = spec.start ||
|
|
191
|
-
end = spec.end ||
|
|
192
|
+
start = spec.start || undefined;
|
|
193
|
+
end = spec.end || undefined;
|
|
194
|
+
tz = spec.tz;
|
|
192
195
|
spec = spec.rule;
|
|
193
196
|
|
|
194
|
-
if (start
|
|
197
|
+
if (start) {
|
|
195
198
|
if (!(start instanceof Date)) {
|
|
196
199
|
start = new Date(start);
|
|
197
200
|
}
|
|
201
|
+
|
|
202
|
+
start = new CronDate(start, tz);
|
|
198
203
|
if (!isValidDate(start) || start.getTime() < Date.now()) {
|
|
199
|
-
start =
|
|
204
|
+
start = undefined;
|
|
200
205
|
}
|
|
201
206
|
}
|
|
202
207
|
|
|
203
|
-
if (end
|
|
204
|
-
end =
|
|
208
|
+
if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
|
|
209
|
+
end = undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (end) {
|
|
213
|
+
end = new CronDate(end, tz);
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
try {
|
|
209
|
-
var res = cronParser.parseExpression(spec, { currentDate: start });
|
|
218
|
+
var res = cronParser.parseExpression(spec, { currentDate: start, tz: tz });
|
|
210
219
|
inv = scheduleNextRecurrence(res, self, start, end);
|
|
211
220
|
if (inv !== null) {
|
|
212
221
|
success = self.trackInvocation(inv);
|
|
213
222
|
}
|
|
214
|
-
|
|
215
223
|
} catch (err) {
|
|
216
224
|
var type = typeof spec;
|
|
217
225
|
if ((type === 'string') || (type === 'number')) {
|
|
@@ -219,6 +227,7 @@ Job.prototype.schedule = function(spec) {
|
|
|
219
227
|
}
|
|
220
228
|
|
|
221
229
|
if ((spec instanceof Date) && (isValidDate(spec))) {
|
|
230
|
+
spec = new CronDate(spec);
|
|
222
231
|
if (spec.getTime() >= Date.now()) {
|
|
223
232
|
inv = new Invocation(self, spec);
|
|
224
233
|
scheduleInvocation(inv);
|
|
@@ -252,6 +261,7 @@ Job.prototype.schedule = function(spec) {
|
|
|
252
261
|
spec = r;
|
|
253
262
|
}
|
|
254
263
|
|
|
264
|
+
spec.tz = tz;
|
|
255
265
|
inv = scheduleNextRecurrence(spec, self, start, end);
|
|
256
266
|
if (inv !== null) {
|
|
257
267
|
success = self.trackInvocation(inv);
|
|
@@ -336,13 +346,68 @@ function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
|
|
|
336
346
|
this.second = (second == null) ? 0 : second;
|
|
337
347
|
}
|
|
338
348
|
|
|
349
|
+
RecurrenceRule.prototype.isValid = function() {
|
|
350
|
+
function isValidType(num) {
|
|
351
|
+
if (Array.isArray(num) || (num instanceof Array)) {
|
|
352
|
+
return num.every(function(e) {
|
|
353
|
+
return isValidType(e);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return !(Number.isNaN(Number(num)) && !(num instanceof Range));
|
|
357
|
+
}
|
|
358
|
+
if (this.month !== null && (this.month < 0 || this.month > 11 || !isValidType(this.month))) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
if (this.dayOfWeek !== null && (this.dayOfWeek < 0 || this.dayOfWeek > 6 || !isValidType(this.dayOfWeek))) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
if (this.hour !== null && (this.hour < 0 || this.hour > 23 || !isValidType(this.hour))) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
if (this.minute !== null && (this.minute < 0 || this.minute > 59 || !isValidType(this.minute))) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
if (this.second !== null && (this.second < 0 || this.second > 59 || !isValidType(this.second))) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
if (this.date !== null) {
|
|
374
|
+
if(!isValidType(this.date)) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
switch (this.month) {
|
|
378
|
+
case 3:
|
|
379
|
+
case 5:
|
|
380
|
+
case 8:
|
|
381
|
+
case 10:
|
|
382
|
+
if (this.date < 1 || this. date > 30) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
case 1:
|
|
387
|
+
if (this.date < 1 || this. date > 29) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
default:
|
|
392
|
+
if (this.date < 1 || this. date > 31) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
};
|
|
399
|
+
|
|
339
400
|
RecurrenceRule.prototype.nextInvocationDate = function(base) {
|
|
340
|
-
base = (base instanceof Date) ? base : (new Date());
|
|
401
|
+
base = ((base instanceof CronDate) || (base instanceof Date)) ? base : (new Date());
|
|
341
402
|
if (!this.recurs) {
|
|
342
403
|
return null;
|
|
343
404
|
}
|
|
344
405
|
|
|
345
|
-
|
|
406
|
+
if(!this.isValid()) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
var now = new CronDate(Date.now(), this.tz);
|
|
346
411
|
var fullYear = now.getFullYear();
|
|
347
412
|
if ((this.year !== null) &&
|
|
348
413
|
(typeof this.year == 'number') &&
|
|
@@ -350,7 +415,7 @@ RecurrenceRule.prototype.nextInvocationDate = function(base) {
|
|
|
350
415
|
return null;
|
|
351
416
|
}
|
|
352
417
|
|
|
353
|
-
var next = new CronDate(base.getTime());
|
|
418
|
+
var next = new CronDate(base.getTime(), this.tz);
|
|
354
419
|
next.addSecond();
|
|
355
420
|
|
|
356
421
|
while (true) {
|
|
@@ -399,7 +464,7 @@ RecurrenceRule.prototype.nextInvocationDate = function(base) {
|
|
|
399
464
|
break;
|
|
400
465
|
}
|
|
401
466
|
|
|
402
|
-
return next;
|
|
467
|
+
return next ? next.toDate() : null;
|
|
403
468
|
};
|
|
404
469
|
|
|
405
470
|
function recurMatch(val, matcher) {
|
|
@@ -407,8 +472,10 @@ function recurMatch(val, matcher) {
|
|
|
407
472
|
return true;
|
|
408
473
|
}
|
|
409
474
|
|
|
410
|
-
if (typeof matcher === 'number'
|
|
475
|
+
if (typeof matcher === 'number') {
|
|
411
476
|
return (val === matcher);
|
|
477
|
+
} else if(typeof matcher === 'string') {
|
|
478
|
+
return (val === Number(matcher));
|
|
412
479
|
} else if (matcher instanceof Range) {
|
|
413
480
|
return matcher.contains(val);
|
|
414
481
|
} else if (Array.isArray(matcher) || (matcher instanceof Array)) {
|
|
@@ -426,13 +493,8 @@ function recurMatch(val, matcher) {
|
|
|
426
493
|
function runOnDate(date, job) {
|
|
427
494
|
var now = (new Date()).getTime();
|
|
428
495
|
var then = date.getTime();
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
setImmediate(job);
|
|
432
|
-
return null;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return lt.setTimeout(job, (then - now));
|
|
496
|
+
|
|
497
|
+
return lt.setTimeout(job, (then < now ? 0 : then - now));
|
|
436
498
|
}
|
|
437
499
|
|
|
438
500
|
var invocations = [];
|
|
@@ -441,7 +503,8 @@ var currentInvocation = null;
|
|
|
441
503
|
function scheduleInvocation(invocation) {
|
|
442
504
|
sorted.add(invocations, invocation, sorter);
|
|
443
505
|
prepareNextInvocation();
|
|
444
|
-
invocation.
|
|
506
|
+
var date = invocation.fireDate instanceof CronDate ? invocation.fireDate.toDate() : invocation.fireDate;
|
|
507
|
+
invocation.job.emit('scheduled', date);
|
|
445
508
|
}
|
|
446
509
|
|
|
447
510
|
function prepareNextInvocation() {
|
|
@@ -504,14 +567,14 @@ function cancelInvocation(invocation) {
|
|
|
504
567
|
/* Recurrence scheduler */
|
|
505
568
|
function scheduleNextRecurrence(rule, job, prevDate, endDate) {
|
|
506
569
|
|
|
507
|
-
prevDate = (prevDate instanceof
|
|
570
|
+
prevDate = (prevDate instanceof CronDate) ? prevDate : new CronDate();
|
|
508
571
|
|
|
509
572
|
var date = (rule instanceof RecurrenceRule) ? rule.nextInvocationDate(prevDate) : rule.next();
|
|
510
573
|
if (date === null) {
|
|
511
574
|
return null;
|
|
512
575
|
}
|
|
513
576
|
|
|
514
|
-
if ((endDate instanceof
|
|
577
|
+
if ((endDate instanceof CronDate) && date.getTime() > endDate.getTime()) {
|
|
515
578
|
return null;
|
|
516
579
|
}
|
|
517
580
|
|
|
@@ -522,8 +585,6 @@ function scheduleNextRecurrence(rule, job, prevDate, endDate) {
|
|
|
522
585
|
}
|
|
523
586
|
|
|
524
587
|
/* Convenience methods */
|
|
525
|
-
var scheduledJobs = {};
|
|
526
|
-
|
|
527
588
|
function scheduleJob() {
|
|
528
589
|
if (arguments.length < 2) {
|
|
529
590
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-schedule",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "A cron-like and not-cron-like job scheduler for Node.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"schedule",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"main": "./lib/schedule.js",
|
|
13
13
|
"scripts": {
|
|
14
14
|
"test": "nodeunit",
|
|
15
|
-
"lint": "eslint lib"
|
|
15
|
+
"lint": "eslint lib test"
|
|
16
16
|
},
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Matt Patenaude",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"url": "https://github.com/node-schedule/node-schedule.git"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"cron-parser": "
|
|
27
|
+
"cron-parser": "^2.4.0",
|
|
28
28
|
"long-timeout": "0.1.1",
|
|
29
29
|
"sorted-array-functions": "^1.0.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"coveralls": "^2.11.2",
|
|
33
|
-
"eslint": "^
|
|
33
|
+
"eslint": "^3.19.0",
|
|
34
34
|
"istanbul": "^0.4.5",
|
|
35
35
|
"nodeunit": "^0.10.2",
|
|
36
36
|
"sinon": "^1.14.1"
|