croner 4.1.97 → 4.2.2
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 +22 -18
- package/dist/croner.cjs +161 -128
- package/dist/croner.min.js +1 -1
- package/dist/croner.min.js.map +1 -1
- package/dist/croner.min.mjs +1 -1
- package/dist/croner.min.mjs.map +1 -1
- package/package.json +6 -2
- package/src/croner.js +46 -86
- package/src/date.js +49 -37
- package/src/options.js +49 -0
- package/src/pattern.js +21 -4
- package/src/{timezone.js → tz.js} +3 -3
- package/types/croner.d.ts +2 -40
- package/types/date.d.ts +3 -1
- package/types/options.d.ts +61 -0
- package/types/pattern.d.ts +2 -0
- package/types/{timezone.d.ts → tz.d.ts} +2 -3
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Trigger functions and/or evaluate cron expressions in JavaScript. No dependencie
|
|
|
6
6
|
# Croner
|
|
7
7
|
|
|
8
8
|
 [](https://badge.fury.io/js/croner) [](https://www.codacy.com/gh/Hexagon/croner/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Hexagon/croner&utm_campaign=Badge_Grade)
|
|
9
|
-
[](https://github.com/Hexagon/croner/blob/master/LICENSE) [](https://github.com/Hexagon/croner/blob/master/LICENSE) [](https://www.npmjs.org/package/croner)
|
|
10
10
|

|
|
11
11
|
|
|
12
12
|
* Trigger functions in JavaScript using [Cron](https://en.wikipedia.org/wiki/Cron#CRON_expression) syntax.
|
|
@@ -46,17 +46,19 @@ More [examples](#examples)...
|
|
|
46
46
|
|
|
47
47
|
Because the existing ones aren't good enough. They have serious bugs, use bloated dependencies, do not work in all environments and/or simply don't work as expected.
|
|
48
48
|
|
|
49
|
-
Benchmark at 2022-02-
|
|
49
|
+
Benchmark at 2022-02-20:
|
|
50
50
|
|
|
51
51
|
```
|
|
52
52
|
> node cron-implementation-test.js
|
|
53
53
|
|
|
54
|
-
Test: When is next
|
|
54
|
+
Test: When is 23:00 next 31st march, pattern '0 0 23 31 3 *'
|
|
55
55
|
|
|
56
|
-
node-schedule:
|
|
57
|
-
node-cron:
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
node-schedule: 2022-03-31 23:00:00 in 15.379ms
|
|
57
|
+
node-cron: ??? in 0.339ms
|
|
58
|
+
Month '3' is limited to '30' days.
|
|
59
|
+
cron: 2022-04-01 23:00:00 in 7.785ms
|
|
60
|
+
croner (legacy): 2022-03-31 23:00:00 in 2.142ms
|
|
61
|
+
croner (default): 2022-03-31 23:00:00 in 0.672ms
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
<details>
|
|
@@ -65,18 +67,19 @@ croner: 2022-10-03 00:00:00 in 1.774ms
|
|
|
65
67
|
```
|
|
66
68
|
Test: When is next 15th of february, pattern '0 0 0 15 2 *'
|
|
67
69
|
|
|
68
|
-
node-schedule:
|
|
69
|
-
node-cron:
|
|
70
|
-
cron:
|
|
71
|
-
croner:
|
|
70
|
+
node-schedule: 2023-02-15 00:00:00 in 43.875ms
|
|
71
|
+
node-cron: ??? in 2.217ms
|
|
72
|
+
cron: 2022-03-15 00:00:00 in 4.147ms
|
|
73
|
+
croner (legacy): 2023-02-15 00:00:00 in 1.72ms
|
|
74
|
+
croner (default): 2023-02-15 00:00:00 in 0.946ms
|
|
72
75
|
|
|
73
|
-
Test: When is
|
|
76
|
+
Test: When is next monday in october, pattern '0 0 0 * 10 1'
|
|
74
77
|
|
|
75
|
-
node-schedule:
|
|
76
|
-
node-cron:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
croner:
|
|
78
|
+
node-schedule: 2022-10-03 00:00:00 in 5.594ms
|
|
79
|
+
node-cron: ??? in 3.62ms
|
|
80
|
+
cron: 2022-11-07 00:00:00 in 1.658ms
|
|
81
|
+
croner (legacy): 2022-10-03 00:00:00 in 0.697ms
|
|
82
|
+
croner (default): 2022-10-03 00:00:00 in 0.546ms
|
|
80
83
|
```
|
|
81
84
|
|
|
82
85
|
</details>
|
|
@@ -203,12 +206,13 @@ job.stop();
|
|
|
203
206
|
| stopAt | undefined | String | ISO 8601 formatted datetime (2021-10-17T23:43:00)<br>in local or specified timezone |
|
|
204
207
|
| paused | false | Boolean | If the job should be paused from start. |
|
|
205
208
|
| context | undefined | Any | Passed as the second parameter to triggered function |
|
|
209
|
+
| legacyMode | false | boolean | Combine day-of-month and day-of-week using OR, default is AND |
|
|
206
210
|
|
|
207
211
|
#### Pattern
|
|
208
212
|
|
|
209
213
|
The expressions of Croner are very similar to the ones of Vixie Cron, with a few additions and changes listed below.
|
|
210
214
|
|
|
211
|
-
* In croner, a combination of day-of-week and day-of-month will only trigger when both conditions match. An example: ```0 20 1 * MON``` will only trigger when monday occur the first day of any month. In Vixie Cron, it would trigger every monday AND the first day of every month. See issue [#53](https://github.com/Hexagon/croner/issues/53).
|
|
215
|
+
* In croner, a combination of day-of-week and day-of-month will only trigger when both conditions match. An example: ```0 20 1 * MON``` will only trigger when monday occur the first day of any month. In Vixie Cron, it would trigger every monday AND the first day of every month. Vixie style can be enabled with `legacyMode: true` from version `4.2.0`. See issue [#53](https://github.com/Hexagon/croner/issues/53) for more information.
|
|
212
216
|
|
|
213
217
|
* Croner expressions support the following additional modifiers
|
|
214
218
|
- *?* A question mark is substituted with croner initialization time, as an example - `? ? * * * *` would be substituted with `25 8 * * * *` if time is `<any hour>:08:25` at the time of `new Cron('? ? * * * *', <...>)`. The question mark can be used in any field.
|
package/dist/croner.cjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Example:
|
|
14
14
|
* let normalDate = new Date(); // d is a normal Date instance, with local timezone and correct utc representation
|
|
15
|
-
* tzDate =
|
|
15
|
+
* tzDate = CronTZ(d, 'America/New_York') // d is a tainted Date instance, where getHours()
|
|
16
16
|
* (for example) will return local time in new york, but getUTCHours()
|
|
17
17
|
* will return something irrelevant.
|
|
18
18
|
*
|
|
@@ -20,10 +20,56 @@
|
|
|
20
20
|
* @param {string} tzString - Timezone string in Europe/Stockholm format
|
|
21
21
|
* @returns {Date}
|
|
22
22
|
*/
|
|
23
|
-
function
|
|
23
|
+
function CronTZ(date, tzString) {
|
|
24
24
|
return new Date(date.toLocaleString("en-US", {timeZone: tzString}));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} CronOptions - Cron scheduler options
|
|
29
|
+
* @property {boolean} [paused] - Job is paused
|
|
30
|
+
* @property {boolean} [kill] - Job is about to be killed or killed
|
|
31
|
+
* @property {boolean} [catch] - Continue exection even if a unhandled error is thrown by triggered function
|
|
32
|
+
* @property {number} [maxRuns] - Maximum nuber of executions
|
|
33
|
+
* @property {string | Date} [startAt] - When to start running
|
|
34
|
+
* @property {string | Date} [stopAt] - When to stop running
|
|
35
|
+
* @property {string} [timezone] - Time zone in Europe/Stockholm format
|
|
36
|
+
* @property {boolean} [legacyMode] - Combine day-of-month and day-of-week using OR. Default is AND.
|
|
37
|
+
* @property {?} [context] - Used to pass any object to scheduled function
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Internal function that validates options, and sets defaults
|
|
42
|
+
* @private
|
|
43
|
+
*
|
|
44
|
+
* @param {CronOptions} options
|
|
45
|
+
* @returns {CronOptions}
|
|
46
|
+
*/
|
|
47
|
+
function CronOptions(options) {
|
|
48
|
+
|
|
49
|
+
// If no options are passed, create empty object
|
|
50
|
+
if (options === void 0) {
|
|
51
|
+
options = {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Keep options, or set defaults
|
|
55
|
+
options.legacyMode = (options.legacyMode === void 0) ? false : options.legacyMode;
|
|
56
|
+
options.paused = (options.paused === void 0) ? false : options.paused;
|
|
57
|
+
options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns;
|
|
58
|
+
options.catch = (options.catch === void 0) ? false : options.catch;
|
|
59
|
+
options.kill = false;
|
|
60
|
+
|
|
61
|
+
// startAt is set, validate it
|
|
62
|
+
if( options.startAt ) {
|
|
63
|
+
options.startAt = new CronDate(options.startAt, options.timezone);
|
|
64
|
+
}
|
|
65
|
+
if( options.stopAt ) {
|
|
66
|
+
options.stopAt = new CronDate(options.stopAt, options.timezone);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return options;
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
27
73
|
/**
|
|
28
74
|
* Converts date to CronDate
|
|
29
75
|
* @constructor
|
|
@@ -57,7 +103,7 @@
|
|
|
57
103
|
CronDate.prototype.fromDate = function (date) {
|
|
58
104
|
|
|
59
105
|
if (this.timezone) {
|
|
60
|
-
date =
|
|
106
|
+
date = CronTZ(date, this.timezone);
|
|
61
107
|
}
|
|
62
108
|
|
|
63
109
|
this.milliseconds = date.getMilliseconds();
|
|
@@ -128,10 +174,11 @@
|
|
|
128
174
|
* @public
|
|
129
175
|
*
|
|
130
176
|
* @param {string} pattern - The pattern used to increment current state
|
|
177
|
+
* @param {CronOptions} options - Cron options used for incrementing
|
|
131
178
|
* @param {boolean} [rerun=false] - If this is an internal incremental run
|
|
132
179
|
* @return {CronDate|null} - Returns itself for chaining, or null if increment wasnt possible
|
|
133
180
|
*/
|
|
134
|
-
CronDate.prototype.increment = function (pattern, rerun) {
|
|
181
|
+
CronDate.prototype.increment = function (pattern, options, rerun) {
|
|
135
182
|
|
|
136
183
|
if (!rerun) {
|
|
137
184
|
this.seconds += 1;
|
|
@@ -140,8 +187,7 @@
|
|
|
140
187
|
this.milliseconds = 0;
|
|
141
188
|
|
|
142
189
|
const
|
|
143
|
-
|
|
144
|
-
|
|
190
|
+
|
|
145
191
|
/**
|
|
146
192
|
* Find next
|
|
147
193
|
*
|
|
@@ -159,27 +205,53 @@
|
|
|
159
205
|
|
|
160
206
|
for( let i = startPos; i < pattern[target].length; i++ ) {
|
|
161
207
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
208
|
+
// This applies to all "levels"
|
|
209
|
+
let match = pattern[target][i];
|
|
210
|
+
|
|
211
|
+
// Days has a couple of special cases
|
|
212
|
+
if (target === "days") {
|
|
213
|
+
|
|
214
|
+
// Create a date object for the target date
|
|
215
|
+
let targetDate = this.getDate(true);
|
|
216
|
+
targetDate.setDate(i-offset);
|
|
217
|
+
|
|
165
218
|
// Special handling for L (last day of month), when we are searching for days
|
|
166
|
-
if (
|
|
167
|
-
let baseDate = this.getDate(true);
|
|
219
|
+
if (pattern.lastDayOfMonth) {
|
|
168
220
|
|
|
221
|
+
// Create a copy of targetDate
|
|
169
222
|
// Set days to one day after today, if month changes, then we are at the last day of the month
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
223
|
+
let targetDateCopy = new Date(targetDate);
|
|
224
|
+
targetDateCopy.setDate(i-offset+1);
|
|
225
|
+
|
|
226
|
+
// Overwrite match if last day of month is matching
|
|
227
|
+
if (targetDateCopy.getMonth() !== this.months) {
|
|
228
|
+
match = true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Weekdays must also match when incrementing days
|
|
234
|
+
// If running in legacy mode, it is sufficient that only weekday match.
|
|
235
|
+
const dowMatch = pattern.daysOfWeek[targetDate.getDay()];
|
|
236
|
+
if (options.legacyMode) {
|
|
237
|
+
if (!pattern.starDayOfWeek && pattern.starDayOfMonth) {
|
|
238
|
+
match = dowMatch;
|
|
239
|
+
} else if (!pattern.starDayOfWeek && !pattern.starDayOfMonth) {
|
|
240
|
+
match = match || dowMatch;
|
|
174
241
|
}
|
|
175
|
-
|
|
176
|
-
// Normal handling
|
|
177
242
|
} else {
|
|
178
|
-
|
|
179
|
-
|
|
243
|
+
// dom AND dow
|
|
244
|
+
match = match && dowMatch;
|
|
180
245
|
}
|
|
181
246
|
|
|
182
247
|
}
|
|
248
|
+
|
|
249
|
+
if (match) {
|
|
250
|
+
this[target] = i-offset;
|
|
251
|
+
this.apply();
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
183
255
|
}
|
|
184
256
|
return false;
|
|
185
257
|
|
|
@@ -233,6 +305,7 @@
|
|
|
233
305
|
|
|
234
306
|
// If pattern didn't provide a match, increment next value (e.g. minues)
|
|
235
307
|
if(!findNext(toDo[doing][0], pattern, toDo[doing][2])) {
|
|
308
|
+
|
|
236
309
|
this[toDo[doing][1]]++;
|
|
237
310
|
|
|
238
311
|
// Reset current level and previous levels
|
|
@@ -240,6 +313,7 @@
|
|
|
240
313
|
|
|
241
314
|
// If pattern provided a match, but changed current value ...
|
|
242
315
|
} else if (currentValue !== this[toDo[doing][0]]) {
|
|
316
|
+
|
|
243
317
|
// Reset previous levels
|
|
244
318
|
resetPrevious(-1);
|
|
245
319
|
|
|
@@ -253,25 +327,8 @@
|
|
|
253
327
|
// Gp down, seconds -> minutes -> hours -> days -> months -> year
|
|
254
328
|
doing++;
|
|
255
329
|
}
|
|
256
|
-
|
|
257
|
-
// This is a special case for weekday, as the user isn't able to combine date/month patterns
|
|
258
|
-
// with weekday patterns, it's just to increment days until we get a match.
|
|
259
|
-
while (!pattern.daysOfWeek[this.getDate(true).getDay()]) {
|
|
260
|
-
this.days += 1;
|
|
261
|
-
|
|
262
|
-
// Reset everything before days
|
|
263
|
-
doing = 2;
|
|
264
|
-
resetPrevious();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// If anything changed, recreate this CronDate and run again without incrementing
|
|
268
|
-
if (origTime != this.getTime()) {
|
|
269
|
-
this.apply();
|
|
270
|
-
return this.increment(pattern, true);
|
|
271
|
-
} else {
|
|
272
|
-
return this;
|
|
273
|
-
}
|
|
274
330
|
|
|
331
|
+
return this;
|
|
275
332
|
};
|
|
276
333
|
|
|
277
334
|
/**
|
|
@@ -286,7 +343,7 @@
|
|
|
286
343
|
if (internal || !this.timezone) {
|
|
287
344
|
return targetDate;
|
|
288
345
|
} else {
|
|
289
|
-
const offset =
|
|
346
|
+
const offset = CronTZ(targetDate, this.timezone).getTime()-targetDate.getTime();
|
|
290
347
|
return new Date(targetDate.getTime()-offset);
|
|
291
348
|
}
|
|
292
349
|
};
|
|
@@ -402,6 +459,8 @@
|
|
|
402
459
|
this.daysOfWeek = Array(8).fill(0); // 0-7 Where 0 = Sunday and 7=Sunday;
|
|
403
460
|
|
|
404
461
|
this.lastDayOfMonth = false;
|
|
462
|
+
this.starDayOfMonth = false;
|
|
463
|
+
this.starDayOfWeek = false;
|
|
405
464
|
|
|
406
465
|
this.parse();
|
|
407
466
|
|
|
@@ -431,17 +490,27 @@
|
|
|
431
490
|
parts.unshift("0");
|
|
432
491
|
}
|
|
433
492
|
|
|
434
|
-
// Convert 'L' to
|
|
493
|
+
// Convert 'L' to lastDayOfMonth flag,
|
|
435
494
|
// and set days to 28,29,30,31 as those are the only days that can be the last day of month
|
|
436
|
-
if(parts[3].
|
|
437
|
-
parts[3] = "
|
|
495
|
+
if(parts[3].indexOf("L") >= 0) {
|
|
496
|
+
parts[3] = parts[3].replace("L","");
|
|
438
497
|
this.lastDayOfMonth = true;
|
|
439
498
|
}
|
|
440
499
|
|
|
500
|
+
// Check for starDayOfMonth
|
|
501
|
+
if(parts[3].toUpperCase() == "*") {
|
|
502
|
+
this.starDayOfMonth = true;
|
|
503
|
+
}
|
|
504
|
+
|
|
441
505
|
// Replace alpha representations
|
|
442
506
|
parts[4] = this.replaceAlphaMonths(parts[4]);
|
|
443
507
|
parts[5] = this.replaceAlphaDays(parts[5]);
|
|
444
508
|
|
|
509
|
+
// Check for starDayOfWeek
|
|
510
|
+
if(parts[5].toUpperCase() == "*") {
|
|
511
|
+
this.starDayOfWeek = true;
|
|
512
|
+
}
|
|
513
|
+
|
|
445
514
|
// Implement '?' in the simplest possible way - replace ? with current value, before further processing
|
|
446
515
|
let initDate = new CronDate(new Date(),this.timezone).getDate(true);
|
|
447
516
|
|
|
@@ -516,7 +585,8 @@
|
|
|
516
585
|
|
|
517
586
|
this.handleStepping(conf, type, valueIndexOffset);
|
|
518
587
|
|
|
519
|
-
|
|
588
|
+
// Anything left should be a number
|
|
589
|
+
} else if( conf !== "" ) {
|
|
520
590
|
this.handleNumber(conf, type, valueIndexOffset);
|
|
521
591
|
}
|
|
522
592
|
|
|
@@ -548,6 +618,10 @@
|
|
|
548
618
|
CronPattern.prototype.handleNumber = function (conf, type, valueIndexOffset) {
|
|
549
619
|
const i = (parseInt(conf, 10) + valueIndexOffset);
|
|
550
620
|
|
|
621
|
+
if( isNaN(i) ) {
|
|
622
|
+
throw new TypeError("CronPattern: " + type + " is not a number: '" + conf + "'");
|
|
623
|
+
}
|
|
624
|
+
|
|
551
625
|
if( i < 0 || i >= this[type].length ) {
|
|
552
626
|
throw new TypeError("CronPattern: " + type + " value out of range: '" + conf + "'");
|
|
553
627
|
}
|
|
@@ -731,18 +805,6 @@
|
|
|
731
805
|
THE SOFTWARE.
|
|
732
806
|
|
|
733
807
|
------------------------------------------------------------------------------------ */
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* @typedef {Object} CronOptions - Cron scheduler options
|
|
737
|
-
* @property {boolean} [paused] - Job is paused
|
|
738
|
-
* @property {boolean} [kill] - Job is about to be killed or killed
|
|
739
|
-
* @property {boolean} [catch] - Continue exection even if a unhandled error is thrown by triggered function
|
|
740
|
-
* @property {number} [maxRuns] - Maximum nuber of executions
|
|
741
|
-
* @property {string | Date} [startAt] - When to start running
|
|
742
|
-
* @property {string | Date} [stopAt] - When to stop running
|
|
743
|
-
* @property {string} [timezone] - Time zone in Europe/Stockholm format
|
|
744
|
-
* @property {?} [context] - Used to pass any object to scheduled function
|
|
745
|
-
*/
|
|
746
808
|
|
|
747
809
|
/**
|
|
748
810
|
* Many JS engines stores the delay as a 32-bit signed integer internally.
|
|
@@ -779,22 +841,23 @@
|
|
|
779
841
|
}
|
|
780
842
|
|
|
781
843
|
/** @type {CronOptions} */
|
|
782
|
-
this.options =
|
|
844
|
+
this.options = CronOptions(options);
|
|
845
|
+
|
|
846
|
+
/** @type {CronDate|undefined} */
|
|
847
|
+
this.once = void 0;
|
|
848
|
+
|
|
849
|
+
/** @type {CronPattern|undefined} */
|
|
850
|
+
this.pattern = void 0;
|
|
783
851
|
|
|
784
852
|
// Check if we got a date, or a pattern supplied as first argument
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
} else if (pattern && (typeof pattern === "string") && pattern.indexOf(":") > 0) {
|
|
788
|
-
/** @type {CronDate} */
|
|
853
|
+
// Then set either this.once or this.pattern
|
|
854
|
+
if (pattern && (pattern instanceof Date || ((typeof pattern === "string") && pattern.indexOf(":") > 0))) {
|
|
789
855
|
this.once = new CronDate(pattern, this.options.timezone);
|
|
790
856
|
} else {
|
|
791
|
-
/** @type {CronPattern} */
|
|
792
857
|
this.pattern = new CronPattern(pattern, this.options.timezone);
|
|
793
858
|
}
|
|
794
859
|
|
|
795
|
-
|
|
796
|
-
* Allow shorthand scheduling
|
|
797
|
-
*/
|
|
860
|
+
// Allow shorthand scheduling
|
|
798
861
|
if( func !== void 0 ) {
|
|
799
862
|
this.fn = func;
|
|
800
863
|
this.schedule();
|
|
@@ -804,37 +867,6 @@
|
|
|
804
867
|
|
|
805
868
|
}
|
|
806
869
|
|
|
807
|
-
/**
|
|
808
|
-
* Internal function that validates options, and sets defaults
|
|
809
|
-
* @private
|
|
810
|
-
*
|
|
811
|
-
* @param {CronOptions} options
|
|
812
|
-
* @returns {CronOptions}
|
|
813
|
-
*/
|
|
814
|
-
Cron.prototype.processOptions = function (options) {
|
|
815
|
-
|
|
816
|
-
// If no options are passed, create empty object
|
|
817
|
-
if (options === void 0) {
|
|
818
|
-
options = {};
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// Keep options, or set defaults
|
|
822
|
-
options.paused = (options.paused === void 0) ? false : options.paused;
|
|
823
|
-
options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns;
|
|
824
|
-
options.catch = (options.catch === void 0) ? false : options.catch;
|
|
825
|
-
options.kill = false;
|
|
826
|
-
|
|
827
|
-
// startAt is set, validate it
|
|
828
|
-
if( options.startAt ) {
|
|
829
|
-
options.startAt = new CronDate(options.startAt, options.timezone);
|
|
830
|
-
}
|
|
831
|
-
if( options.stopAt ) {
|
|
832
|
-
options.stopAt = new CronDate(options.stopAt, options.timezone);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
return options;
|
|
836
|
-
};
|
|
837
|
-
|
|
838
870
|
/**
|
|
839
871
|
* Find next runtime, based on supplied date. Strips milliseconds.
|
|
840
872
|
*
|
|
@@ -886,40 +918,6 @@
|
|
|
886
918
|
return this.previousrun ? this.previousrun.getDate() : null;
|
|
887
919
|
};
|
|
888
920
|
|
|
889
|
-
/**
|
|
890
|
-
* Internal version of next. Cron needs millseconds internally, hence _next.
|
|
891
|
-
* @private
|
|
892
|
-
*
|
|
893
|
-
* @param {CronDate} prev - Input pattern
|
|
894
|
-
* @returns {CronDate | null} - Next run time
|
|
895
|
-
*/
|
|
896
|
-
Cron.prototype._next = function (prev) {
|
|
897
|
-
|
|
898
|
-
// Previous run should never be before startAt
|
|
899
|
-
if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) {
|
|
900
|
-
prev = this.options.startAt;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Calculate next run according to pattern or one-off timestamp
|
|
904
|
-
const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern);
|
|
905
|
-
|
|
906
|
-
if (this.once && this.once.getTime(true) <= prev.getTime(true)) {
|
|
907
|
-
return null;
|
|
908
|
-
|
|
909
|
-
} else if ((nextRun === null) ||
|
|
910
|
-
(this.options.maxRuns <= 0) ||
|
|
911
|
-
(this.options.kill) ||
|
|
912
|
-
(this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) {
|
|
913
|
-
return null;
|
|
914
|
-
|
|
915
|
-
} else {
|
|
916
|
-
// All seem good, return next run
|
|
917
|
-
return nextRun;
|
|
918
|
-
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
};
|
|
922
|
-
|
|
923
921
|
/**
|
|
924
922
|
* Returns number of milliseconds to next run
|
|
925
923
|
* @public
|
|
@@ -1027,6 +1025,41 @@
|
|
|
1027
1025
|
|
|
1028
1026
|
};
|
|
1029
1027
|
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Internal version of next. Cron needs millseconds internally, hence _next.
|
|
1031
|
+
* @private
|
|
1032
|
+
*
|
|
1033
|
+
* @param {CronDate} prev - Input pattern
|
|
1034
|
+
* @returns {CronDate | null} - Next run time
|
|
1035
|
+
*/
|
|
1036
|
+
Cron.prototype._next = function (prev) {
|
|
1037
|
+
|
|
1038
|
+
// Previous run should never be before startAt
|
|
1039
|
+
if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) {
|
|
1040
|
+
prev = this.options.startAt;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Calculate next run according to pattern or one-off timestamp
|
|
1044
|
+
const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern, this.options);
|
|
1045
|
+
|
|
1046
|
+
if (this.once && this.once.getTime(true) <= prev.getTime(true)) {
|
|
1047
|
+
return null;
|
|
1048
|
+
|
|
1049
|
+
} else if ((nextRun === null) ||
|
|
1050
|
+
(this.options.maxRuns <= 0) ||
|
|
1051
|
+
(this.options.kill) ||
|
|
1052
|
+
(this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) {
|
|
1053
|
+
return null;
|
|
1054
|
+
|
|
1055
|
+
} else {
|
|
1056
|
+
// All seem good, return next run
|
|
1057
|
+
return nextRun;
|
|
1058
|
+
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1030
1063
|
return Cron;
|
|
1031
1064
|
|
|
1032
1065
|
}));
|
package/dist/croner.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.Cron=factory())})(this,function(){"use strict";function convertTZ(date,tzString){return new Date(date.toLocaleString("en-US",{timeZone:tzString}))}function CronDate(date,timezone){this.timezone=timezone;if(date&&date instanceof Date){this.fromDate(date)}else if(date===void 0){this.fromDate(new Date)}else if(date&&typeof date==="string"){this.fromString(date)}else if(date instanceof CronDate){this.fromCronDate(date)}else{throw new TypeError("CronDate: Invalid type ("+typeof date+") passed as parameter to CronDate constructor")}}CronDate.prototype.fromDate=function(date){if(this.timezone){date=convertTZ(date,this.timezone)}this.milliseconds=date.getMilliseconds();this.seconds=date.getSeconds();this.minutes=date.getMinutes();this.hours=date.getHours();this.days=date.getDate();this.months=date.getMonth();this.years=date.getFullYear()};CronDate.prototype.fromCronDate=function(date){this.timezone=date.timezone;this.milliseconds=date.milliseconds;this.seconds=date.seconds;this.minutes=date.minutes;this.hours=date.hours;this.days=date.days;this.months=date.months;this.years=date.years};CronDate.prototype.apply=function(){const newDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);this.milliseconds=newDate.getMilliseconds();this.seconds=newDate.getSeconds();this.minutes=newDate.getMinutes();this.hours=newDate.getHours();this.days=newDate.getDate();this.months=newDate.getMonth();this.years=newDate.getFullYear()};CronDate.prototype.fromString=function(str){const parsedDate=this.parseISOLocal(str);if(isNaN(parsedDate)){throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date.")}this.fromDate(parsedDate)};CronDate.prototype.increment=function(pattern,rerun){if(!rerun){this.seconds+=1}this.milliseconds=0;const origTime=this.getTime(),findNext=(target,pattern,offset,override)=>{const startPos=override===void 0?this[target]+offset:0+offset;for(let i=startPos;i<pattern[target].length;i++){if(pattern[target][i]){if(target==="days"&&pattern.lastDayOfMonth){let baseDate=this.getDate(true);baseDate.setDate(i-offset+1);if(baseDate.getMonth()!==this["months"]){this[target]=i-offset;return true}}else{this[target]=i-offset;return true}}}return false},resetPrevious=offset=>{while(doing+offset>=0){findNext(toDo[doing+offset][0],pattern,toDo[doing+offset][2],0);doing--}};const toDo=[["seconds","minutes",0],["minutes","hours",0],["hours","days",0],["days","months",-1],["months","years",0]];let doing=0;while(doing<5){let currentValue=this[toDo[doing][0]];if(!findNext(toDo[doing][0],pattern,toDo[doing][2])){this[toDo[doing][1]]++;resetPrevious(0)}else if(currentValue!==this[toDo[doing][0]]){resetPrevious(-1)}if(this.years>=4e3){return null}doing++}while(!pattern.daysOfWeek[this.getDate(true).getDay()]){this.days+=1;doing=2;resetPrevious()}if(origTime!=this.getTime()){this.apply();return this.increment(pattern,true)}else{return this}};CronDate.prototype.getDate=function(internal){const targetDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);if(internal||!this.timezone){return targetDate}else{const offset=convertTZ(targetDate,this.timezone).getTime()-targetDate.getTime();return new Date(targetDate.getTime()-offset)}};CronDate.prototype.getTime=function(internal){return this.getDate(internal).getTime()};CronDate.prototype.parseISOLocal=function(dateTimeString){const dateTimeStringSplit=dateTimeString.split(/\D/);if(dateTimeStringSplit.length<6){return NaN}const year=parseInt(dateTimeStringSplit[0],10),month=parseInt(dateTimeStringSplit[1],10),day=parseInt(dateTimeStringSplit[2],10),hour=parseInt(dateTimeStringSplit[3],10),minute=parseInt(dateTimeStringSplit[4],10),second=parseInt(dateTimeStringSplit[5],10);if(isNaN(year)||isNaN(month)||isNaN(day)||isNaN(hour)||isNaN(minute)||isNaN(second)){return NaN}else{let generatedDate;if(dateTimeString.indexOf("Z")>0){generatedDate=new Date(Date.UTC(year,month-1,day,hour,minute,second));if(year==generatedDate.getUTCFullYear()&&month==generatedDate.getUTCMonth()+1&&day==generatedDate.getUTCDate()&&hour==generatedDate.getUTCHours()&&minute==generatedDate.getUTCMinutes()&&second==generatedDate.getUTCSeconds()){return generatedDate}else{return NaN}}else{generatedDate=new Date(year,month-1,day,hour,minute,second);if(year==generatedDate.getFullYear()&&month==generatedDate.getMonth()+1&&day==generatedDate.getDate()&&hour==generatedDate.getHours()&&minute==generatedDate.getMinutes()&&second==generatedDate.getSeconds()){return generatedDate}else{return NaN}}}};function CronPattern(pattern,timezone){this.pattern=pattern;this.timezone=timezone;this.seconds=Array(60).fill(0);this.minutes=Array(60).fill(0);this.hours=Array(24).fill(0);this.days=Array(31).fill(0);this.months=Array(12).fill(0);this.daysOfWeek=Array(8).fill(0);this.lastDayOfMonth=false;this.parse()}CronPattern.prototype.parse=function(){if(!(typeof this.pattern==="string"||this.pattern.constructor===String)){throw new TypeError("CronPattern: Pattern has to be of type string.")}const parts=this.pattern.trim().replace(/\s+/g," ").split(" ");if(parts.length<5||parts.length>6){throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exacly five or six space separated parts required.")}if(parts.length===5){parts.unshift("0")}if(parts[3].toUpperCase()=="L"){parts[3]="28,29,30,31";this.lastDayOfMonth=true}parts[4]=this.replaceAlphaMonths(parts[4]);parts[5]=this.replaceAlphaDays(parts[5]);let initDate=new CronDate(new Date,this.timezone).getDate(true);parts[0]=parts[0].replace("?",initDate.getSeconds());parts[1]=parts[1].replace("?",initDate.getMinutes());parts[2]=parts[2].replace("?",initDate.getHours());parts[3]=parts[3].replace("?",initDate.getDate());parts[4]=parts[4].replace("?",initDate.getMonth()+1);parts[5]=parts[5].replace("?",initDate.getDay());this.throwAtIllegalCharacters(parts);this.partToArray("seconds",parts[0],0);this.partToArray("minutes",parts[1],0);this.partToArray("hours",parts[2],0);this.partToArray("days",parts[3],-1);this.partToArray("months",parts[4],-1);this.partToArray("daysOfWeek",parts[5],0);if(this.daysOfWeek[7]){this.daysOfWeek[0]=1}};CronPattern.prototype.partToArray=function(type,conf,valueIndexOffset,recursed){const arr=this[type];if(conf==="*"){for(let i=0;i<arr.length;i++){arr[i]=1}return}const split=conf.split(",");if(split.length>1){for(let i=0;i<split.length;i++){this.partToArray(type,split[i],valueIndexOffset,true)}}else if(conf.indexOf("-")!==-1&&conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRangeWithStepping(conf,type,valueIndexOffset)}else if(conf.indexOf("-")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRange(conf,type,valueIndexOffset)}else if(conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleStepping(conf,type,valueIndexOffset)}else{this.handleNumber(conf,type,valueIndexOffset)}};CronPattern.prototype.throwAtIllegalCharacters=function(parts){const reValidCron=/[^/*0-9,-]+/;for(let i=0;i<parts.length;i++){if(reValidCron.test(parts[i])){throw new TypeError("CronPattern: configuration entry "+i+" ("+parts[i]+") contains illegal characters.")}}};CronPattern.prototype.handleNumber=function(conf,type,valueIndexOffset){const i=parseInt(conf,10)+valueIndexOffset;if(i<0||i>=this[type].length){throw new TypeError("CronPattern: "+type+" value out of range: '"+conf+"'")}this[type][i]=1};CronPattern.prototype.handleRangeWithStepping=function(conf,type,valueIndexOffset){const matches=conf.match(/^(\d+)-(\d+)\/(\d+)$/);if(matches===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+conf+"'");let[,lower,upper,steps]=matches;lower=parseInt(lower,10)+valueIndexOffset;upper=parseInt(upper,10)+valueIndexOffset;steps=parseInt(steps,10);if(isNaN(lower))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(upper))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");if(lower<0||upper>=this[type].length)throw new TypeError("CronPattern: Value out of range: '"+conf+"'");if(lower>upper)throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'");for(let i=lower;i<=upper;i+=steps){this[type][i]=1}};CronPattern.prototype.handleRange=function(conf,type,valueIndexOffset){const split=conf.split("-");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal range: '"+conf+"'")}const lower=parseInt(split[0],10)+valueIndexOffset,upper=parseInt(split[1],10)+valueIndexOffset;if(isNaN(lower)){throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)")}else if(isNaN(upper)){throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)")}if(lower<0||upper>=this[type].length){throw new TypeError("CronPattern: Value out of range: '"+conf+"'")}if(lower>upper){throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'")}for(let i=lower;i<=upper;i++){this[type][i]=1}};CronPattern.prototype.handleStepping=function(conf,type){const split=conf.split("/");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+conf+"'")}let start=0;if(split[0]!=="*"){start=parseInt(split[0],10)}const steps=parseInt(split[1],10);if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");for(let i=start;i<this[type].length;i+=steps){this[type][i]=1}};CronPattern.prototype.replaceAlphaDays=function(conf){return conf.replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};CronPattern.prototype.replaceAlphaMonths=function(conf){return conf.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};const maxDelay=Math.pow(2,32-1)-1;function Cron(pattern,options,func){if(!(this instanceof Cron)){return new Cron(pattern,options,func)}if(typeof options==="function"){func=options;options=void 0}this.options=this.processOptions(options);if(pattern&&pattern instanceof Date){this.once=new CronDate(pattern,this.options.timezone)}else if(pattern&&typeof pattern==="string"&&pattern.indexOf(":")>0){this.once=new CronDate(pattern,this.options.timezone)}else{this.pattern=new CronPattern(pattern,this.options.timezone)}if(func!==void 0){this.fn=func;this.schedule()}return this}Cron.prototype.processOptions=function(options){if(options===void 0){options={}}options.paused=options.paused===void 0?false:options.paused;options.maxRuns=options.maxRuns===void 0?Infinity:options.maxRuns;options.catch=options.catch===void 0?false:options.catch;options.kill=false;if(options.startAt){options.startAt=new CronDate(options.startAt,options.timezone)}if(options.stopAt){options.stopAt=new CronDate(options.stopAt,options.timezone)}return options};Cron.prototype.next=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);return next?next.getDate():null};Cron.prototype.enumerate=function(n,previous){let enumeration=[];while(n--&&(previous=this.next(previous))){enumeration.push(previous)}return enumeration};Cron.prototype.running=function(){const msLeft=this.msToNext(this.previousrun);const running=!this.options.paused&&this.fn!==void 0;return msLeft!==null&&running};Cron.prototype.previous=function(){return this.previousrun?this.previousrun.getDate():null};Cron.prototype._next=function(prev){if(this.options.startAt&&prev&&prev.getTime(true)<this.options.startAt.getTime(true)){prev=this.options.startAt}const nextRun=this.once||new CronDate(prev,this.options.timezone).increment(this.pattern);if(this.once&&this.once.getTime(true)<=prev.getTime(true)){return null}else if(nextRun===null||this.options.maxRuns<=0||this.options.kill||this.options.stopAt&&nextRun.getTime(true)>=this.options.stopAt.getTime(true)){return null}else{return nextRun}};Cron.prototype.msToNext=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);if(next){return next.getTime(true)-prev.getTime(true)}else{return null}};Cron.prototype.stop=function(){this.options.kill=true;if(this.currentTimeout){clearTimeout(this.currentTimeout)}};Cron.prototype.pause=function(){return(this.options.paused=true)&&!this.options.kill};Cron.prototype.resume=function(){return!(this.options.paused=false)&&!this.options.kill};Cron.prototype.schedule=function(func){if(func&&this.fn){throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.")}else if(func){this.fn=func}let waitMs=this.msToNext(this.previousrun);if(waitMs===null)return this;if(waitMs>maxDelay){waitMs=maxDelay}this.currentTimeout=setTimeout(()=>{if(waitMs!==maxDelay&&!this.options.paused){this.options.maxRuns--;if(this.options.catch){try{this.fn(this,this.options.context)}catch(_e){}}else{this.fn(this,this.options.context)}this.previousrun=new CronDate(void 0,this.options.timezone)}this.schedule()},waitMs);return this};return Cron});
|
|
1
|
+
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.Cron=factory())})(this,function(){"use strict";function CronTZ(date,tzString){return new Date(date.toLocaleString("en-US",{timeZone:tzString}))}function CronOptions(options){if(options===void 0){options={}}options.legacyMode=options.legacyMode===void 0?false:options.legacyMode;options.paused=options.paused===void 0?false:options.paused;options.maxRuns=options.maxRuns===void 0?Infinity:options.maxRuns;options.catch=options.catch===void 0?false:options.catch;options.kill=false;if(options.startAt){options.startAt=new CronDate(options.startAt,options.timezone)}if(options.stopAt){options.stopAt=new CronDate(options.stopAt,options.timezone)}return options}function CronDate(date,timezone){this.timezone=timezone;if(date&&date instanceof Date){this.fromDate(date)}else if(date===void 0){this.fromDate(new Date)}else if(date&&typeof date==="string"){this.fromString(date)}else if(date instanceof CronDate){this.fromCronDate(date)}else{throw new TypeError("CronDate: Invalid type ("+typeof date+") passed as parameter to CronDate constructor")}}CronDate.prototype.fromDate=function(date){if(this.timezone){date=CronTZ(date,this.timezone)}this.milliseconds=date.getMilliseconds();this.seconds=date.getSeconds();this.minutes=date.getMinutes();this.hours=date.getHours();this.days=date.getDate();this.months=date.getMonth();this.years=date.getFullYear()};CronDate.prototype.fromCronDate=function(date){this.timezone=date.timezone;this.milliseconds=date.milliseconds;this.seconds=date.seconds;this.minutes=date.minutes;this.hours=date.hours;this.days=date.days;this.months=date.months;this.years=date.years};CronDate.prototype.apply=function(){const newDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);this.milliseconds=newDate.getMilliseconds();this.seconds=newDate.getSeconds();this.minutes=newDate.getMinutes();this.hours=newDate.getHours();this.days=newDate.getDate();this.months=newDate.getMonth();this.years=newDate.getFullYear()};CronDate.prototype.fromString=function(str){const parsedDate=this.parseISOLocal(str);if(isNaN(parsedDate)){throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date.")}this.fromDate(parsedDate)};CronDate.prototype.increment=function(pattern,options,rerun){if(!rerun){this.seconds+=1}this.milliseconds=0;const findNext=(target,pattern,offset,override)=>{const startPos=override===void 0?this[target]+offset:0+offset;for(let i=startPos;i<pattern[target].length;i++){let match=pattern[target][i];if(target==="days"){let targetDate=this.getDate(true);targetDate.setDate(i-offset);if(pattern.lastDayOfMonth){let targetDateCopy=new Date(targetDate);targetDateCopy.setDate(i-offset+1);if(targetDateCopy.getMonth()!==this.months){match=true}}const dowMatch=pattern.daysOfWeek[targetDate.getDay()];if(options.legacyMode){if(!pattern.starDayOfWeek&&pattern.starDayOfMonth){match=dowMatch}else if(!pattern.starDayOfWeek&&!pattern.starDayOfMonth){match=match||dowMatch}}else{match=match&&dowMatch}}if(match){this[target]=i-offset;this.apply();return true}}return false},resetPrevious=offset=>{while(doing+offset>=0){findNext(toDo[doing+offset][0],pattern,toDo[doing+offset][2],0);doing--}};const toDo=[["seconds","minutes",0],["minutes","hours",0],["hours","days",0],["days","months",-1],["months","years",0]];let doing=0;while(doing<5){let currentValue=this[toDo[doing][0]];if(!findNext(toDo[doing][0],pattern,toDo[doing][2])){this[toDo[doing][1]]++;resetPrevious(0)}else if(currentValue!==this[toDo[doing][0]]){resetPrevious(-1)}if(this.years>=4e3){return null}doing++}return this};CronDate.prototype.getDate=function(internal){const targetDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);if(internal||!this.timezone){return targetDate}else{const offset=CronTZ(targetDate,this.timezone).getTime()-targetDate.getTime();return new Date(targetDate.getTime()-offset)}};CronDate.prototype.getTime=function(internal){return this.getDate(internal).getTime()};CronDate.prototype.parseISOLocal=function(dateTimeString){const dateTimeStringSplit=dateTimeString.split(/\D/);if(dateTimeStringSplit.length<6){return NaN}const year=parseInt(dateTimeStringSplit[0],10),month=parseInt(dateTimeStringSplit[1],10),day=parseInt(dateTimeStringSplit[2],10),hour=parseInt(dateTimeStringSplit[3],10),minute=parseInt(dateTimeStringSplit[4],10),second=parseInt(dateTimeStringSplit[5],10);if(isNaN(year)||isNaN(month)||isNaN(day)||isNaN(hour)||isNaN(minute)||isNaN(second)){return NaN}else{let generatedDate;if(dateTimeString.indexOf("Z")>0){generatedDate=new Date(Date.UTC(year,month-1,day,hour,minute,second));if(year==generatedDate.getUTCFullYear()&&month==generatedDate.getUTCMonth()+1&&day==generatedDate.getUTCDate()&&hour==generatedDate.getUTCHours()&&minute==generatedDate.getUTCMinutes()&&second==generatedDate.getUTCSeconds()){return generatedDate}else{return NaN}}else{generatedDate=new Date(year,month-1,day,hour,minute,second);if(year==generatedDate.getFullYear()&&month==generatedDate.getMonth()+1&&day==generatedDate.getDate()&&hour==generatedDate.getHours()&&minute==generatedDate.getMinutes()&&second==generatedDate.getSeconds()){return generatedDate}else{return NaN}}}};function CronPattern(pattern,timezone){this.pattern=pattern;this.timezone=timezone;this.seconds=Array(60).fill(0);this.minutes=Array(60).fill(0);this.hours=Array(24).fill(0);this.days=Array(31).fill(0);this.months=Array(12).fill(0);this.daysOfWeek=Array(8).fill(0);this.lastDayOfMonth=false;this.starDayOfMonth=false;this.starDayOfWeek=false;this.parse()}CronPattern.prototype.parse=function(){if(!(typeof this.pattern==="string"||this.pattern.constructor===String)){throw new TypeError("CronPattern: Pattern has to be of type string.")}const parts=this.pattern.trim().replace(/\s+/g," ").split(" ");if(parts.length<5||parts.length>6){throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exacly five or six space separated parts required.")}if(parts.length===5){parts.unshift("0")}if(parts[3].indexOf("L")>=0){parts[3]=parts[3].replace("L","");this.lastDayOfMonth=true}if(parts[3].toUpperCase()=="*"){this.starDayOfMonth=true}parts[4]=this.replaceAlphaMonths(parts[4]);parts[5]=this.replaceAlphaDays(parts[5]);if(parts[5].toUpperCase()=="*"){this.starDayOfWeek=true}let initDate=new CronDate(new Date,this.timezone).getDate(true);parts[0]=parts[0].replace("?",initDate.getSeconds());parts[1]=parts[1].replace("?",initDate.getMinutes());parts[2]=parts[2].replace("?",initDate.getHours());parts[3]=parts[3].replace("?",initDate.getDate());parts[4]=parts[4].replace("?",initDate.getMonth()+1);parts[5]=parts[5].replace("?",initDate.getDay());this.throwAtIllegalCharacters(parts);this.partToArray("seconds",parts[0],0);this.partToArray("minutes",parts[1],0);this.partToArray("hours",parts[2],0);this.partToArray("days",parts[3],-1);this.partToArray("months",parts[4],-1);this.partToArray("daysOfWeek",parts[5],0);if(this.daysOfWeek[7]){this.daysOfWeek[0]=1}};CronPattern.prototype.partToArray=function(type,conf,valueIndexOffset,recursed){const arr=this[type];if(conf==="*"){for(let i=0;i<arr.length;i++){arr[i]=1}return}const split=conf.split(",");if(split.length>1){for(let i=0;i<split.length;i++){this.partToArray(type,split[i],valueIndexOffset,true)}}else if(conf.indexOf("-")!==-1&&conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRangeWithStepping(conf,type,valueIndexOffset)}else if(conf.indexOf("-")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRange(conf,type,valueIndexOffset)}else if(conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleStepping(conf,type,valueIndexOffset)}else if(conf!==""){this.handleNumber(conf,type,valueIndexOffset)}};CronPattern.prototype.throwAtIllegalCharacters=function(parts){const reValidCron=/[^/*0-9,-]+/;for(let i=0;i<parts.length;i++){if(reValidCron.test(parts[i])){throw new TypeError("CronPattern: configuration entry "+i+" ("+parts[i]+") contains illegal characters.")}}};CronPattern.prototype.handleNumber=function(conf,type,valueIndexOffset){const i=parseInt(conf,10)+valueIndexOffset;if(isNaN(i)){throw new TypeError("CronPattern: "+type+" is not a number: '"+conf+"'")}if(i<0||i>=this[type].length){throw new TypeError("CronPattern: "+type+" value out of range: '"+conf+"'")}this[type][i]=1};CronPattern.prototype.handleRangeWithStepping=function(conf,type,valueIndexOffset){const matches=conf.match(/^(\d+)-(\d+)\/(\d+)$/);if(matches===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+conf+"'");let[,lower,upper,steps]=matches;lower=parseInt(lower,10)+valueIndexOffset;upper=parseInt(upper,10)+valueIndexOffset;steps=parseInt(steps,10);if(isNaN(lower))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(upper))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");if(lower<0||upper>=this[type].length)throw new TypeError("CronPattern: Value out of range: '"+conf+"'");if(lower>upper)throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'");for(let i=lower;i<=upper;i+=steps){this[type][i]=1}};CronPattern.prototype.handleRange=function(conf,type,valueIndexOffset){const split=conf.split("-");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal range: '"+conf+"'")}const lower=parseInt(split[0],10)+valueIndexOffset,upper=parseInt(split[1],10)+valueIndexOffset;if(isNaN(lower)){throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)")}else if(isNaN(upper)){throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)")}if(lower<0||upper>=this[type].length){throw new TypeError("CronPattern: Value out of range: '"+conf+"'")}if(lower>upper){throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'")}for(let i=lower;i<=upper;i++){this[type][i]=1}};CronPattern.prototype.handleStepping=function(conf,type){const split=conf.split("/");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+conf+"'")}let start=0;if(split[0]!=="*"){start=parseInt(split[0],10)}const steps=parseInt(split[1],10);if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");for(let i=start;i<this[type].length;i+=steps){this[type][i]=1}};CronPattern.prototype.replaceAlphaDays=function(conf){return conf.replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};CronPattern.prototype.replaceAlphaMonths=function(conf){return conf.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};const maxDelay=Math.pow(2,32-1)-1;function Cron(pattern,options,func){if(!(this instanceof Cron)){return new Cron(pattern,options,func)}if(typeof options==="function"){func=options;options=void 0}this.options=CronOptions(options);this.once=void 0;this.pattern=void 0;if(pattern&&(pattern instanceof Date||typeof pattern==="string"&&pattern.indexOf(":")>0)){this.once=new CronDate(pattern,this.options.timezone)}else{this.pattern=new CronPattern(pattern,this.options.timezone)}if(func!==void 0){this.fn=func;this.schedule()}return this}Cron.prototype.next=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);return next?next.getDate():null};Cron.prototype.enumerate=function(n,previous){let enumeration=[];while(n--&&(previous=this.next(previous))){enumeration.push(previous)}return enumeration};Cron.prototype.running=function(){const msLeft=this.msToNext(this.previousrun);const running=!this.options.paused&&this.fn!==void 0;return msLeft!==null&&running};Cron.prototype.previous=function(){return this.previousrun?this.previousrun.getDate():null};Cron.prototype.msToNext=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);if(next){return next.getTime(true)-prev.getTime(true)}else{return null}};Cron.prototype.stop=function(){this.options.kill=true;if(this.currentTimeout){clearTimeout(this.currentTimeout)}};Cron.prototype.pause=function(){return(this.options.paused=true)&&!this.options.kill};Cron.prototype.resume=function(){return!(this.options.paused=false)&&!this.options.kill};Cron.prototype.schedule=function(func){if(func&&this.fn){throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.")}else if(func){this.fn=func}let waitMs=this.msToNext(this.previousrun);if(waitMs===null)return this;if(waitMs>maxDelay){waitMs=maxDelay}this.currentTimeout=setTimeout(()=>{if(waitMs!==maxDelay&&!this.options.paused){this.options.maxRuns--;if(this.options.catch){try{this.fn(this,this.options.context)}catch(_e){}}else{this.fn(this,this.options.context)}this.previousrun=new CronDate(void 0,this.options.timezone)}this.schedule()},waitMs);return this};Cron.prototype._next=function(prev){if(this.options.startAt&&prev&&prev.getTime(true)<this.options.startAt.getTime(true)){prev=this.options.startAt}const nextRun=this.once||new CronDate(prev,this.options.timezone).increment(this.pattern,this.options);if(this.once&&this.once.getTime(true)<=prev.getTime(true)){return null}else if(nextRun===null||this.options.maxRuns<=0||this.options.kill||this.options.stopAt&&nextRun.getTime(true)>=this.options.stopAt.getTime(true)){return null}else{return nextRun}};return Cron});
|