croner 3.0.40 → 3.0.44
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/README.md +211 -211
- package/dist/croner.min.js +1 -1
- package/dist/croner.min.mjs +1 -1
- package/dist-legacy/croner.cjs +1 -1
- package/package.json +1 -1
- package/src/croner.js +84 -455
- package/src/date.js +214 -0
- package/src/pattern.js +202 -0
- package/src/timezone.js +12 -0
- package/test/{suite.cjs → src/suite.cjs} +3 -1
- package/test/test.croner.js +30 -0
- package/test/test.dist.legacy.cjs +1 -1
- package/test/test.dist.module.js +1 -1
- package/types/croner.d.ts +28 -45
- package/types/date.d.ts +54 -0
- package/types/pattern.d.ts +43 -0
- package/types/timezone.d.ts +9 -0
package/src/croner.js
CHANGED
|
@@ -27,414 +27,49 @@
|
|
|
27
27
|
THE SOFTWARE.
|
|
28
28
|
|
|
29
29
|
------------------------------------------------------------------------------------ */
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {"seconds" | "minutes" | "hours" | "days" | "months" | "daysOfWeek"} CronPatternPart
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @typedef {0 | -1} CronIndexOffset
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @typedef {Date | undefined} CronNextResult
|
|
44
|
-
*/
|
|
30
|
+
import { CronDate } from "./date.js";
|
|
31
|
+
import { CronPattern } from "./pattern.js";
|
|
45
32
|
|
|
46
33
|
/**
|
|
34
|
+
* @typedef {CronDate | null} CronNextResult
|
|
35
|
+
*
|
|
47
36
|
* @typedef {Object} CronOptions - Cron scheduler options
|
|
48
37
|
* @property {boolean} [paused] - Job is paused
|
|
49
38
|
* @property {boolean} [kill] - Job is about to be killed
|
|
50
|
-
* @property {
|
|
39
|
+
* @property {number} [maxRuns] - Maximum nuber of executions
|
|
51
40
|
* @property {number} [currentTimeout] - Internal: setTimeout "id"
|
|
52
41
|
* @property {CronNextResult} [previous] - Previous run time
|
|
53
42
|
* @property {string | Date} [startAt] - When to start running
|
|
54
43
|
* @property {string | Date} [stopAt] - When to stop running
|
|
44
|
+
* @property {string} [timezone] - Time zone in Europe/Stockholm format
|
|
55
45
|
*/
|
|
56
46
|
|
|
57
47
|
/**
|
|
58
48
|
* @typedef {Function} CronJobStop - Stop current job
|
|
59
49
|
* @returns {boolean} - If pause was successful
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/**
|
|
50
|
+
*
|
|
63
51
|
* @typedef {Function} CronJobResume - Resume current job
|
|
64
52
|
* @returns {boolean} - If resume was successful
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/**
|
|
53
|
+
*
|
|
68
54
|
* @typedef {Object} CronJob - Cron job control functions
|
|
69
55
|
* @property {CronJobStop} stop
|
|
70
56
|
* @property {CronJobResume} pause
|
|
71
57
|
* @property {Function} resume
|
|
72
58
|
*/
|
|
73
59
|
|
|
74
|
-
// Many JS engines stores the delay as a 32-bit signed integer internally.
|
|
75
|
-
// This causes an integer overflow when using delays larger than 2147483647,
|
|
76
|
-
// resulting in the timeout being executed immediately.
|
|
77
|
-
//
|
|
78
|
-
// All JS engines implements an immediate execution of delays larger that a 32-bit
|
|
79
|
-
// int to keep the behaviour concistent.
|
|
80
|
-
const maxDelay = Math.pow(2, 32 - 1) - 1;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
// ---- Helper functions ------------------------------------------------------------
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
function raise (err) {
|
|
89
|
-
throw new TypeError("Cron parser: " + err);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function fill(arr, val) {
|
|
93
|
-
|
|
94
|
-
// Simple "Polyfill" for Array.fill on pre ES6 environments
|
|
95
|
-
for(let i = 0; i < arr.length; i++) {
|
|
96
|
-
arr[i] = val;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return arr;
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
// ---- CronDate ---------------------------------------------------------------------
|
|
107
|
-
//
|
|
108
60
|
|
|
109
61
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
function CronDate (date) {
|
|
115
|
-
this.milliseconds = date.getMilliseconds();
|
|
116
|
-
this.seconds = date.getSeconds();
|
|
117
|
-
this.minutes = date.getMinutes();
|
|
118
|
-
this.hours = date.getHours();
|
|
119
|
-
this.days = date.getDate();
|
|
120
|
-
this.months = date.getMonth();
|
|
121
|
-
this.years = date.getFullYear();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Increment to next run time
|
|
62
|
+
* Many JS engines stores the delay as a 32-bit signed integer internally.
|
|
63
|
+
* This causes an integer overflow when using delays larger than 2147483647,
|
|
64
|
+
* resulting in the timeout being executed immediately.
|
|
126
65
|
*
|
|
127
|
-
*
|
|
128
|
-
|
|
129
|
-
CronDate.prototype.increment = function (pattern) {
|
|
130
|
-
|
|
131
|
-
this.seconds += 1;
|
|
132
|
-
this.milliseconds = 0;
|
|
133
|
-
|
|
134
|
-
let self = this,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Find next
|
|
139
|
-
*
|
|
140
|
-
* @param {string} target
|
|
141
|
-
* @param {string} pattern
|
|
142
|
-
* @param {string} offset
|
|
143
|
-
* @param {string} override
|
|
144
|
-
*
|
|
145
|
-
* @returns {boolean}
|
|
146
|
-
*
|
|
147
|
-
*/
|
|
148
|
-
findNext = function (target, pattern, offset, override) {
|
|
149
|
-
|
|
150
|
-
let startPos = (override === void 0) ? self[target] + offset : 0 + offset;
|
|
151
|
-
|
|
152
|
-
for( let i = startPos; i < pattern[target].length; i++ ) {
|
|
153
|
-
if( pattern[target][i] ) {
|
|
154
|
-
self[target] = i-offset;
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return false;
|
|
160
|
-
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Array of work to be done, consisting of subarrays described below:
|
|
164
|
-
// [
|
|
165
|
-
// First item is which member to process,
|
|
166
|
-
// Second item is which member to increment if we didn't find a mathch in current item,
|
|
167
|
-
// Third item is an offset. if months is handled 0-11 in js date object, and we get 1-12
|
|
168
|
-
// from pattern. Offset should be -1
|
|
169
|
-
// ]
|
|
170
|
-
let toDo = [
|
|
171
|
-
["seconds", "minutes", 0],
|
|
172
|
-
["minutes", "hours", 0],
|
|
173
|
-
["hours", "days", 0],
|
|
174
|
-
["days", "months", -1],
|
|
175
|
-
["months", "years", 0]
|
|
176
|
-
],
|
|
177
|
-
doing = 0;
|
|
178
|
-
|
|
179
|
-
// Ok, we're working our way trough the toDo array, top to bottom
|
|
180
|
-
// If we reach 5, work is done
|
|
181
|
-
while(doing < 5) {
|
|
182
|
-
|
|
183
|
-
// findNext sets the current member to next match in pattern
|
|
184
|
-
// If time is 00:00:01 and pattern says *:*:05, seconds will
|
|
185
|
-
// be set to 5
|
|
186
|
-
if(!findNext(toDo[doing][0], pattern, toDo[doing][2])) {
|
|
187
|
-
|
|
188
|
-
// If pattern didn't provide a match, increment next vanlue (e.g. minues)
|
|
189
|
-
this[toDo[doing][1]]++;
|
|
190
|
-
|
|
191
|
-
// Now when we have gone to next minute, we have to set seconds to the first match
|
|
192
|
-
// Now we are at 00:01:05 following the same example.
|
|
193
|
-
//
|
|
194
|
-
// This goes all the way back to seconds, hence the reverse loop.
|
|
195
|
-
while(doing >= 0) {
|
|
196
|
-
|
|
197
|
-
// Ok, reset current member(e.g. seconds) to first match in pattern, using
|
|
198
|
-
// the same method as aerlier
|
|
199
|
-
//
|
|
200
|
-
// Note the fourth parameter, stating that we should start matching the pattern
|
|
201
|
-
// from zero, instead of current time.
|
|
202
|
-
findNext(toDo[doing][0], pattern, toDo[doing][2], 0);
|
|
203
|
-
|
|
204
|
-
// Go back up, days -> hours -> minutes -> seconds
|
|
205
|
-
doing--;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Gp down, seconds -> minutes -> hours -> days -> months -> year
|
|
210
|
-
doing++;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// This is a special case for weekday, as the user isn't able to combine date/month patterns
|
|
214
|
-
// with weekday patterns, it's just to increment days until we get a match.
|
|
215
|
-
while (!pattern.daysOfWeek[this.getDate().getDay()]) {
|
|
216
|
-
this.days += 1;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Convert current state back to a javascript Date()
|
|
223
|
-
*
|
|
224
|
-
* @returns {date}
|
|
66
|
+
* All JS engines implements an immediate execution of delays larger that a 32-bit
|
|
67
|
+
* int to keep the behaviour concistent.
|
|
225
68
|
*
|
|
69
|
+
* @type {number}
|
|
226
70
|
*/
|
|
227
|
-
|
|
228
|
-
return new Date(this.years, this.months, this.days, this.hours, this.minutes, this.seconds, this.milliseconds);
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
// ---- CronPattern ---------------------------------------------------------------------
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Create a CronPattern instance from pattern string ('* * * * * *')
|
|
239
|
-
* @constructor
|
|
240
|
-
* @param {string} pattern - Input pattern
|
|
241
|
-
*/
|
|
242
|
-
function CronPattern (pattern) {
|
|
243
|
-
|
|
244
|
-
this.pattern = pattern;
|
|
245
|
-
|
|
246
|
-
this.seconds = fill(Array(60),0); // 0-59
|
|
247
|
-
this.minutes = fill(Array(60),0); // 0-59
|
|
248
|
-
this.hours = fill(Array(24),0); // 0-23
|
|
249
|
-
this.days = fill(Array(31),0); // 0-30 in array, 1-31 in config
|
|
250
|
-
this.months = fill(Array(12),0); // 0-11 in array, 1-12 in config
|
|
251
|
-
this.daysOfWeek = fill(Array(8),0); // 0-7 Where 0 = Sunday and 7=Sunday;
|
|
252
|
-
|
|
253
|
-
this.parse();
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Parse current pattern, will raise an error on failure
|
|
259
|
-
*/
|
|
260
|
-
CronPattern.prototype.parse = function () {
|
|
261
|
-
|
|
262
|
-
// Sanity check
|
|
263
|
-
if( !(typeof this.pattern === "string" || this.pattern.constructor === String) ) {
|
|
264
|
-
raise("Pattern has to be of type string.");
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Split configuration on whitespace
|
|
268
|
-
let parts = this.pattern.trim().replace(/\s+/g, " ").split(" "),
|
|
269
|
-
part,
|
|
270
|
-
i,
|
|
271
|
-
reValidCron = /[^/*0-9,-]+/,
|
|
272
|
-
hasMonths,
|
|
273
|
-
hasDaysOfWeek,
|
|
274
|
-
hasDates;
|
|
275
|
-
|
|
276
|
-
// Validite number of configuration entries
|
|
277
|
-
if( parts.length < 5 || parts.length > 6 ) {
|
|
278
|
-
raise("invalid configuration format ('" + this.pattern + "'), exacly five or six space separated parts required.");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// If seconds is omitted, insert 0 for seconds
|
|
282
|
-
if( parts.length == 5) {
|
|
283
|
-
parts.unshift("0");
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Validate field content
|
|
287
|
-
for( i = 0; i < parts.length; i++ ) {
|
|
288
|
-
part = parts[i].trim();
|
|
289
|
-
|
|
290
|
-
// Check that part only contain legal characters ^[0-9-,]+$
|
|
291
|
-
if( reValidCron.test(part) ) {
|
|
292
|
-
raise("configuration entry " + (i + 1) + " (" + part + ") contains illegal characters.");
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Check that we dont have both months and daysofweek
|
|
297
|
-
hasMonths = (parts[4] !== "*");
|
|
298
|
-
hasDaysOfWeek = (parts[5] !== "*");
|
|
299
|
-
hasDates = (parts[3] !== "*");
|
|
300
|
-
|
|
301
|
-
// Month/Date and dayofweek is incompatible
|
|
302
|
-
if( hasDaysOfWeek && (hasMonths || hasDates) ) {
|
|
303
|
-
raise("configuration invalid, you can not combine month/date with day of week.");
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Parse parts into arrays, validates as we go
|
|
307
|
-
this.partToArray("seconds", parts[0], 0);
|
|
308
|
-
this.partToArray("minutes", parts[1], 0);
|
|
309
|
-
this.partToArray("hours", parts[2], 0);
|
|
310
|
-
this.partToArray("days", parts[3], -1);
|
|
311
|
-
this.partToArray("months", parts[4], -1);
|
|
312
|
-
this.partToArray("daysOfWeek", parts[5], 0);
|
|
313
|
-
|
|
314
|
-
// 0 = Sunday, 7 = Sunday
|
|
315
|
-
if( this.daysOfWeek[7] ) {
|
|
316
|
-
this.daysOfWeek[0] = 1;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Convert current part (seconds/minutes etc) to an array of 1 or 0 depending on if the part is about to trigger a run or not.
|
|
323
|
-
*
|
|
324
|
-
* @param {CronPatternPart} type - Seconds/minutes etc
|
|
325
|
-
* @param {string} conf - Current pattern part - *, 0-1 etc
|
|
326
|
-
* @param {CronIndexOffset} valueIndexOffset - 0 or -1. 0 for seconds,minutes, hours as they start on 1. -1 on days and months, as the start on 0
|
|
327
|
-
*/
|
|
328
|
-
CronPattern.prototype.partToArray = function (type, conf, valueIndexOffset) {
|
|
329
|
-
|
|
330
|
-
let i,
|
|
331
|
-
split,
|
|
332
|
-
lower,
|
|
333
|
-
upper,
|
|
334
|
-
steps,
|
|
335
|
-
arr = this[type];
|
|
336
|
-
|
|
337
|
-
// First off, handle wildcard
|
|
338
|
-
if( conf === "*" ) {
|
|
339
|
-
for( i = 0; i < arr.length; i++ ) {
|
|
340
|
-
arr[i] = 1;
|
|
341
|
-
}
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Recurse into comma separated entries
|
|
346
|
-
split = conf.split(",");
|
|
347
|
-
if( split.length > 1 ) {
|
|
348
|
-
for( i = 0; i < split.length; i++ ) {
|
|
349
|
-
this.partToArray(type, split[i], valueIndexOffset);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Didn't need to recurse, determine if this is a range, steps or a number
|
|
356
|
-
// - Got a range
|
|
357
|
-
if( conf.indexOf("-") !== -1 ) {
|
|
358
|
-
|
|
359
|
-
split = conf.split("-");
|
|
360
|
-
|
|
361
|
-
if( split.length !== 2 ) {
|
|
362
|
-
raise("Syntax error, illegal range: '" + conf + "'");
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
lower = parseInt(split[0], 10) + valueIndexOffset;
|
|
366
|
-
upper = parseInt(split[1], 10) + valueIndexOffset;
|
|
367
|
-
|
|
368
|
-
if( isNaN(lower) ) {
|
|
369
|
-
raise("Syntax error, illegal lower range (NaN)");
|
|
370
|
-
} else if( isNaN(upper) ) {
|
|
371
|
-
raise("Syntax error, illegal upper range (NaN)");
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Check that value is within range
|
|
375
|
-
if( lower < 0 || upper >= arr.length ) {
|
|
376
|
-
raise("Value out of range: '" + conf + "'");
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
//
|
|
380
|
-
if( lower > upper ) {
|
|
381
|
-
raise("From value is larger than to value: '" + conf + "'");
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
for( i = lower; i <= upper; i++ ) {
|
|
385
|
-
arr[(i + valueIndexOffset)] = 1;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// - Got stepping
|
|
389
|
-
} else if( conf.indexOf("/") !== -1 ) {
|
|
390
|
-
|
|
391
|
-
split = conf.split("/");
|
|
392
|
-
|
|
393
|
-
if( split.length !== 2 ) {
|
|
394
|
-
raise("Syntax error, illegal stepping: '" + conf + "'");
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if( split[0] !== "*" ) {
|
|
398
|
-
raise("Syntax error, left part of / needs to be * : '" + conf + "'");
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
steps = parseInt(split[1], 10);
|
|
402
|
-
|
|
403
|
-
if( isNaN(steps) ) {
|
|
404
|
-
raise("Syntax error, illegal stepping: (NaN)");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if( steps === 0 ) {
|
|
408
|
-
raise("Syntax error, illegal stepping: 0");
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if( steps > arr.length ) {
|
|
412
|
-
raise("Syntax error, steps cannot be greater than maximum value of part ("+arr.length+")");
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
for( i = 0; i < arr.length; i+= steps ) {
|
|
416
|
-
arr[(i + valueIndexOffset)] = 1;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// - Got a number
|
|
420
|
-
} else {
|
|
421
|
-
|
|
422
|
-
i = (parseInt(conf, 10) + valueIndexOffset);
|
|
423
|
-
|
|
424
|
-
if( i < 0 || i >= arr.length ) {
|
|
425
|
-
raise(type + " value out of range: '" + conf + "'");
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
arr[i] = 1;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
|
|
71
|
+
const maxDelay = Math.pow(2, 32 - 1) - 1;
|
|
434
72
|
|
|
435
|
-
//
|
|
436
|
-
// ---- Cron --------------------------------------------------------------------------
|
|
437
|
-
//
|
|
438
73
|
/**
|
|
439
74
|
* Cron entrypoint
|
|
440
75
|
*
|
|
@@ -457,7 +92,6 @@ function Cron (pattern, options, fn) {
|
|
|
457
92
|
|
|
458
93
|
/** @type {CronOptions} */
|
|
459
94
|
self.schedulerDefaults = {
|
|
460
|
-
stopAt: Infinity,
|
|
461
95
|
maxRuns: Infinity,
|
|
462
96
|
kill: false
|
|
463
97
|
};
|
|
@@ -468,8 +102,10 @@ function Cron (pattern, options, fn) {
|
|
|
468
102
|
options = {};
|
|
469
103
|
}
|
|
470
104
|
|
|
471
|
-
|
|
472
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Store and validate options
|
|
107
|
+
* @type {CronOptions}
|
|
108
|
+
*/
|
|
473
109
|
self.opts = self.validateOpts(options || {});
|
|
474
110
|
|
|
475
111
|
// Determine what to return, default is self
|
|
@@ -489,56 +125,50 @@ function Cron (pattern, options, fn) {
|
|
|
489
125
|
* Find next runtime, based on supplied date. Strips milliseconds.
|
|
490
126
|
*
|
|
491
127
|
* @param {Date} prev - Input pattern
|
|
492
|
-
* @returns {
|
|
128
|
+
* @returns {Date | null} - Next run time
|
|
493
129
|
*/
|
|
494
130
|
Cron.prototype.next = function (prev) {
|
|
495
|
-
let
|
|
496
|
-
|
|
497
|
-
return dirtyDate;
|
|
131
|
+
let next = this._next(prev);
|
|
132
|
+
return next ? next.getDate() : null;
|
|
498
133
|
};
|
|
499
134
|
|
|
500
135
|
/**
|
|
501
|
-
* Return
|
|
136
|
+
* Return previous run time
|
|
502
137
|
*
|
|
503
|
-
* @returns {Date
|
|
138
|
+
* @returns {Date | null} - Previous run time
|
|
504
139
|
*/
|
|
505
140
|
Cron.prototype.previous = function () {
|
|
506
|
-
return this.opts.previous;
|
|
141
|
+
return this.opts.previous ? this.opts.previous.getDate() : null;
|
|
507
142
|
};
|
|
508
143
|
|
|
509
144
|
/**
|
|
510
145
|
* Internal version of next. Cron needs millseconds internally, hence _next.
|
|
511
146
|
*
|
|
512
147
|
* @param {Date} prev - Input pattern
|
|
513
|
-
* @returns {CronNextResult} - Next run time
|
|
148
|
+
* @returns {CronNextResult | null} - Next run time
|
|
514
149
|
*/
|
|
515
150
|
Cron.prototype._next = function (prev) {
|
|
516
151
|
|
|
517
|
-
prev =
|
|
152
|
+
prev = new CronDate(prev, this.opts.timezone);
|
|
518
153
|
|
|
519
154
|
// Previous run should never be before startAt
|
|
520
|
-
if( this.opts.startAt && prev < this.opts.startAt ) {
|
|
521
|
-
prev = this.opts.startAt;
|
|
155
|
+
if( this.opts.startAt && prev && prev.getTime() < this.opts.startAt.getTime() ) {
|
|
156
|
+
prev = new CronDate(this.opts.startAt, this.opts.timezone);
|
|
522
157
|
}
|
|
523
158
|
|
|
159
|
+
// Calculate next run
|
|
160
|
+
let nextRun = new CronDate(prev, this.opts.timezone).increment(this.pattern);
|
|
161
|
+
|
|
524
162
|
// Check for stop condition
|
|
525
163
|
if ((this.opts.maxRuns <= 0) ||
|
|
526
|
-
(this.opts.kill)
|
|
527
|
-
|
|
164
|
+
(this.opts.kill) ||
|
|
165
|
+
(this.opts.stopAt && nextRun.getTime() >= this.opts.stopAt.getTime() )) {
|
|
166
|
+
return null;
|
|
167
|
+
} else {
|
|
168
|
+
// All seem good, return next run
|
|
169
|
+
return nextRun;
|
|
528
170
|
}
|
|
529
|
-
|
|
530
|
-
let
|
|
531
|
-
stopAt = this.opts.stopAt || this.schedulerDefaults.stopAt,
|
|
532
|
-
cronDate = new CronDate(prev),
|
|
533
|
-
nextRun;
|
|
534
|
-
|
|
535
|
-
cronDate.increment(this.pattern);
|
|
536
|
-
|
|
537
|
-
// Get next run
|
|
538
|
-
nextRun = cronDate.getDate();
|
|
539
|
-
|
|
540
|
-
// All seem good, return next run
|
|
541
|
-
return !(stopAt && nextRun >= stopAt ) ? nextRun : void 0;
|
|
171
|
+
|
|
542
172
|
};
|
|
543
173
|
|
|
544
174
|
/**
|
|
@@ -550,26 +180,10 @@ Cron.prototype._next = function (prev) {
|
|
|
550
180
|
Cron.prototype.validateOpts = function (opts) {
|
|
551
181
|
// startAt is set, validate it
|
|
552
182
|
if( opts.startAt ) {
|
|
553
|
-
|
|
554
|
-
opts.startAt = new Date(Date.parse(opts.startAt)-1);
|
|
555
|
-
} else {
|
|
556
|
-
opts.startAt = new Date(opts.startAt.getTime()-1);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Raise if we did get an invalid date
|
|
560
|
-
if( isNaN(opts.startAt) ) {
|
|
561
|
-
raise("Provided value for startAt could not be parsed as date.");
|
|
562
|
-
}
|
|
183
|
+
opts.startAt = new CronDate(opts.startAt, opts.timezone);
|
|
563
184
|
}
|
|
564
185
|
if( opts.stopAt ) {
|
|
565
|
-
|
|
566
|
-
opts.stopAt = new Date(Date.parse(opts.stopAt));
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Raise if we did get an invalid date
|
|
570
|
-
if( isNaN(opts.stopAt) ) {
|
|
571
|
-
raise("Provided value for stopAt could not be parsed as date.");
|
|
572
|
-
}
|
|
186
|
+
opts.stopAt = new CronDate(opts.stopAt, opts.timezone);
|
|
573
187
|
}
|
|
574
188
|
return opts;
|
|
575
189
|
};
|
|
@@ -577,16 +191,16 @@ Cron.prototype.validateOpts = function (opts) {
|
|
|
577
191
|
/**
|
|
578
192
|
* Returns number of milliseconds to next run
|
|
579
193
|
*
|
|
580
|
-
* @param {CronNextResult} [prev=new
|
|
581
|
-
* @returns {number |
|
|
194
|
+
* @param {CronNextResult} [prev=new CronDate()] - Starting date, defaults to now
|
|
195
|
+
* @returns {number | null}
|
|
582
196
|
*/
|
|
583
197
|
Cron.prototype.msToNext = function (prev) {
|
|
584
|
-
prev = prev || new
|
|
198
|
+
prev = prev || new CronDate(void 0, this.opts.timezone);
|
|
585
199
|
let next = this._next(prev);
|
|
586
200
|
if( next ) {
|
|
587
|
-
return (
|
|
201
|
+
return (next.getTime() - prev.getTime());
|
|
588
202
|
} else {
|
|
589
|
-
return
|
|
203
|
+
return null;
|
|
590
204
|
}
|
|
591
205
|
};
|
|
592
206
|
|
|
@@ -599,36 +213,50 @@ Cron.prototype.msToNext = function (prev) {
|
|
|
599
213
|
* @returns {CronJob}
|
|
600
214
|
*/
|
|
601
215
|
Cron.prototype.schedule = function (opts, func) {
|
|
602
|
-
|
|
603
|
-
let self = this,
|
|
604
|
-
waitMs,
|
|
605
|
-
|
|
606
|
-
// Prioritize context before closure,
|
|
607
|
-
// to allow testing of maximum delay.
|
|
608
|
-
_maxDelay = self.maxDelay || maxDelay;
|
|
609
|
-
|
|
216
|
+
|
|
610
217
|
// Make opts optional
|
|
611
218
|
if( !func ) {
|
|
612
219
|
func = opts;
|
|
613
|
-
|
|
220
|
+
|
|
221
|
+
// If options isn't passed to schedule, use stored options
|
|
222
|
+
opts = this.opts;
|
|
614
223
|
}
|
|
615
224
|
|
|
616
225
|
// Keep options, or set defaults
|
|
617
226
|
opts.paused = (opts.paused === void 0) ? false : opts.paused;
|
|
618
227
|
opts.kill = opts.kill || this.schedulerDefaults.kill;
|
|
619
|
-
opts.rest = opts.rest || 0;
|
|
620
228
|
if( !opts.maxRuns && opts.maxRuns !== 0 ) {
|
|
621
229
|
opts.maxRuns = this.schedulerDefaults.maxRuns;
|
|
622
230
|
}
|
|
623
231
|
|
|
624
232
|
// Store options
|
|
625
|
-
|
|
233
|
+
this.opts = this.validateOpts(opts || {});
|
|
234
|
+
|
|
235
|
+
this._schedule(opts, func);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Schedule a new job
|
|
240
|
+
*
|
|
241
|
+
* @constructor
|
|
242
|
+
* @param {CronOptions | Function} [options] - Options
|
|
243
|
+
* @param {Function} [func] - Function to be run each iteration of pattern
|
|
244
|
+
* @returns {CronJob}
|
|
245
|
+
*/
|
|
246
|
+
Cron.prototype._schedule = function (opts, func) {
|
|
247
|
+
|
|
248
|
+
let self = this,
|
|
249
|
+
waitMs,
|
|
250
|
+
|
|
251
|
+
// Prioritize context before closure,
|
|
252
|
+
// to allow testing of maximum delay.
|
|
253
|
+
_maxDelay = self.maxDelay || maxDelay;
|
|
626
254
|
|
|
627
255
|
// Get ms to next run
|
|
628
|
-
waitMs = this.msToNext(opts.previous);
|
|
256
|
+
waitMs = this.msToNext(self.opts.previous);
|
|
629
257
|
|
|
630
258
|
// Check for stop conditions
|
|
631
|
-
if ( waitMs ===
|
|
259
|
+
if ( waitMs === null ) {
|
|
632
260
|
return;
|
|
633
261
|
}
|
|
634
262
|
|
|
@@ -638,21 +266,22 @@ Cron.prototype.schedule = function (opts, func) {
|
|
|
638
266
|
}
|
|
639
267
|
|
|
640
268
|
// All ok, go go!
|
|
641
|
-
opts.currentTimeout = setTimeout(function () {
|
|
269
|
+
self.opts.currentTimeout = setTimeout(function () {
|
|
642
270
|
|
|
643
271
|
// Are we running? If waitMs is maxed out, this is a blank run
|
|
644
272
|
if( waitMs !== _maxDelay ) {
|
|
645
273
|
|
|
646
|
-
if ( !opts.paused ) {
|
|
647
|
-
opts.maxRuns--;
|
|
274
|
+
if ( !self.opts.paused ) {
|
|
275
|
+
self.opts.maxRuns--;
|
|
648
276
|
func();
|
|
649
277
|
}
|
|
650
278
|
|
|
651
|
-
opts.previous = new
|
|
279
|
+
self.opts.previous = new CronDate(self.opts.previous, self.opts.timezone);
|
|
280
|
+
|
|
652
281
|
}
|
|
653
282
|
|
|
654
283
|
// Recurse
|
|
655
|
-
self.
|
|
284
|
+
self._schedule(self.opts, func);
|
|
656
285
|
|
|
657
286
|
}, waitMs );
|
|
658
287
|
|
|
@@ -661,21 +290,21 @@ Cron.prototype.schedule = function (opts, func) {
|
|
|
661
290
|
|
|
662
291
|
// Return undefined
|
|
663
292
|
stop: function() {
|
|
664
|
-
opts.kill = true;
|
|
293
|
+
self.opts.kill = true;
|
|
665
294
|
// Stop any awaiting call
|
|
666
|
-
if( opts.currentTimeout ) {
|
|
667
|
-
clearTimeout( opts.currentTimeout );
|
|
295
|
+
if( self.opts.currentTimeout ) {
|
|
296
|
+
clearTimeout( self.opts.currentTimeout );
|
|
668
297
|
}
|
|
669
298
|
},
|
|
670
299
|
|
|
671
300
|
// Return bool wether pause were successful
|
|
672
301
|
pause: function() {
|
|
673
|
-
return (opts.paused = true) && !opts.kill;
|
|
302
|
+
return (self.opts.paused = true) && !self.opts.kill;
|
|
674
303
|
},
|
|
675
304
|
|
|
676
305
|
// Return bool wether resume were successful
|
|
677
306
|
resume: function () {
|
|
678
|
-
return !(opts.paused = false) && !opts.kill;
|
|
307
|
+
return !(self.opts.paused = false) && !self.opts.kill;
|
|
679
308
|
}
|
|
680
309
|
|
|
681
310
|
};
|