croner 4.0.90 → 4.1.94
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 +36 -13
- package/dist/croner.cjs +138 -113
- 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 +1 -1
- package/src/croner.js +74 -72
- package/src/date.js +63 -40
- package/src/timezone.js +2 -2
- package/types/croner.d.ts +9 -8
- package/types/timezone.d.ts +3 -3
package/README.md
CHANGED
|
@@ -21,25 +21,30 @@ Trigger functions and/or evaluate cron expressions in JavaScript. No dependencie
|
|
|
21
21
|
Quick examples:
|
|
22
22
|
|
|
23
23
|
```javascript
|
|
24
|
-
// Run a function at the interval defined by a cron expression
|
|
25
|
-
const job = Cron('
|
|
26
|
-
console.log('This will run every second');
|
|
24
|
+
// Basic: Run a function at the interval defined by a cron expression
|
|
25
|
+
const job = Cron('*/5 * * * * *', () => {
|
|
26
|
+
console.log('This will run every fifth second');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
// What dates do the next 100 sundays occur at?
|
|
29
|
+
// Enumeration: What dates do the next 100 sundays occur at?
|
|
30
30
|
const nextSundays = Cron('0 0 0 * * 7').enumerate(100);
|
|
31
31
|
console.log(nextSundays);
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Days left to a specific date
|
|
34
34
|
const msLeft = Cron('59 59 23 24 DEC *').next() - new Date();
|
|
35
35
|
console.log(Math.floor(msLeft/1000/3600/24) + " days left to next christmas eve");
|
|
36
|
+
|
|
37
|
+
// Run a function at a specific date/time using a non-local timezone (time is ISO 8601 local time)
|
|
38
|
+
// This will run 2023-01-23 00:00:00 according to the time in Asia/Kolkata
|
|
39
|
+
Cron('2023-01-23T00:00:00', { timezone: 'Asia/Kolkata' }, () => { console.log('Yay') });
|
|
40
|
+
|
|
36
41
|
```
|
|
37
42
|
|
|
38
43
|
More [examples](#examples)...
|
|
39
44
|
|
|
40
45
|
## Why another javascript cron implementation
|
|
41
46
|
|
|
42
|
-
Because the existing ones
|
|
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.
|
|
43
48
|
|
|
44
49
|
Benchmark at 2022-02-01:
|
|
45
50
|
|
|
@@ -175,7 +180,7 @@ Cron takes three arguments
|
|
|
175
180
|
* scheduled function (optional)
|
|
176
181
|
|
|
177
182
|
```javascript
|
|
178
|
-
const job = Cron("* * * * * *" , /*optional*/ { maxRuns: 1 } , /*optional*/ () => {} );
|
|
183
|
+
const job = Cron("* * * * * *" /* Or a date object, or ISO 8601 local time */ , /*optional*/ { maxRuns: 1 } , /*optional*/ () => {} );
|
|
179
184
|
|
|
180
185
|
// If function is omitted in constructor, it can be scheduled later
|
|
181
186
|
job.schedule((/* optional */ job, /* optional */ context) => {});
|
|
@@ -206,15 +211,17 @@ job.stop();
|
|
|
206
211
|
| paused | false | Boolean | If the job should be paused from start. |
|
|
207
212
|
| context | undefined | Any | Passed as the second parameter to triggered function |
|
|
208
213
|
|
|
209
|
-
####
|
|
214
|
+
#### Pattern
|
|
210
215
|
|
|
211
|
-
The expressions of Croner are very similar to the ones of Vixie Cron, with a few additions and changes listed below.
|
|
216
|
+
The expressions of Croner are very similar to the ones of Vixie Cron, with a few additions and changes listed below.
|
|
212
217
|
|
|
213
218
|
* 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.
|
|
214
219
|
|
|
215
220
|
* Croner expressions support the following additional modifiers
|
|
216
|
-
-
|
|
217
|
-
- *L
|
|
221
|
+
- *?* 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.
|
|
222
|
+
- *L* L can be used in the day of month field, to specify the last day of the month.
|
|
223
|
+
|
|
224
|
+
* Croner also allow you to pass a javascript Date object, or a ISO 8601 formatted string, as a pattern. The scheduled function will trigger once at the specified date/time. If you use a timezone different from local, you pass ISO 8601 local time in target location, and specify timezone using the options (2nd parameter).
|
|
218
225
|
|
|
219
226
|
```javascript
|
|
220
227
|
// ┌──────────────── (optional) second (0 - 59)
|
|
@@ -277,7 +284,7 @@ const job = Cron(
|
|
|
277
284
|
timezone: "Europe/Stockholm"
|
|
278
285
|
},
|
|
279
286
|
function() {
|
|
280
|
-
console.log('This will run every minute, from 2021-11-01 to 2021-12-01 00:00:00
|
|
287
|
+
console.log('This will run every minute, from 2021-11-01 to 2021-12-01 00:00:00');
|
|
281
288
|
}
|
|
282
289
|
);
|
|
283
290
|
```
|
|
@@ -285,7 +292,7 @@ const job = Cron(
|
|
|
285
292
|
#### Job controls
|
|
286
293
|
```javascript
|
|
287
294
|
const job = Cron('* * * * * *', (self) => {
|
|
288
|
-
console.log('This will run every second. Pause on second 10. Resume on
|
|
295
|
+
console.log('This will run every second. Pause on second 10. Resume on 15. And quit on 20.');
|
|
289
296
|
console.log('Current second: ', new Date().getSeconds());
|
|
290
297
|
console.log('Previous run: ' + self.previous());
|
|
291
298
|
console.log('Next run: ' + self.next());
|
|
@@ -313,6 +320,22 @@ Cron('*/5 * * * * *', { context: data }, (self, context) => {
|
|
|
313
320
|
});
|
|
314
321
|
```
|
|
315
322
|
|
|
323
|
+
|
|
324
|
+
#### Fire on a specific date/time
|
|
325
|
+
```javascript
|
|
326
|
+
// A javascript date, or a ISO 8601 local time string can be passed, to fire a function once.
|
|
327
|
+
// Always specify which timezone the ISO 8601 time string has with the timezone option.
|
|
328
|
+
let job = Cron("2025-01-01T23:00:00",{timezone: "Europe/Stockholm"},() => {
|
|
329
|
+
console.log('This will run at 2025-01-01 23:00:00 in timezone Europe/Stockholm');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (job.next() === null) {
|
|
333
|
+
// The job will not fire for some reason
|
|
334
|
+
} else {
|
|
335
|
+
console.log("Job will fire at " + job.next());
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
316
339
|
## Contributing
|
|
317
340
|
|
|
318
341
|
See [Contribution Guide](/CONTRIBUTING.md)
|
package/dist/croner.cjs
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
* (for example) will return local time in new york, but getUTCHours()
|
|
17
17
|
* will return something irrelevant.
|
|
18
18
|
*
|
|
19
|
-
* @param {
|
|
19
|
+
* @param {Date} date - Input date
|
|
20
20
|
* @param {string} tzString - Timezone string in Europe/Stockholm format
|
|
21
|
-
* @returns {
|
|
21
|
+
* @returns {Date}
|
|
22
22
|
*/
|
|
23
23
|
function convertTZ(date, tzString) {
|
|
24
24
|
return new Date(date.toLocaleString("en-US", {timeZone: tzString}));
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
function CronDate (date, timezone) {
|
|
35
35
|
|
|
36
36
|
this.timezone = timezone;
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
if (date && date instanceof Date) {
|
|
39
39
|
this.fromDate(date);
|
|
40
40
|
} else if (date === void 0) {
|
|
@@ -53,11 +53,10 @@
|
|
|
53
53
|
* @private
|
|
54
54
|
*
|
|
55
55
|
* @param {Date} date - Input date
|
|
56
|
-
* @param {boolean} fromLocal - Target already in local time
|
|
57
56
|
*/
|
|
58
|
-
CronDate.prototype.fromDate = function (date
|
|
57
|
+
CronDate.prototype.fromDate = function (date) {
|
|
59
58
|
|
|
60
|
-
if (this.timezone
|
|
59
|
+
if (this.timezone) {
|
|
61
60
|
date = convertTZ(date, this.timezone);
|
|
62
61
|
}
|
|
63
62
|
|
|
@@ -68,6 +67,7 @@
|
|
|
68
67
|
this.days = date.getDate();
|
|
69
68
|
this.months = date.getMonth();
|
|
70
69
|
this.years = date.getFullYear();
|
|
70
|
+
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
this.minutes = date.minutes;
|
|
84
84
|
this.hours = date.hours;
|
|
85
85
|
this.days = date.days;
|
|
86
|
-
this.months
|
|
86
|
+
this.months = date.months;
|
|
87
87
|
this.years = date.years;
|
|
88
88
|
};
|
|
89
89
|
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date.");
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
this.fromDate(parsedDate
|
|
123
|
+
this.fromDate(parsedDate);
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
/**
|
|
@@ -158,9 +158,27 @@
|
|
|
158
158
|
const startPos = (override === void 0) ? this[target] + offset : 0 + offset;
|
|
159
159
|
|
|
160
160
|
for( let i = startPos; i < pattern[target].length; i++ ) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
|
|
162
|
+
// If pattern matches and, in case of days, weekday matches, go on
|
|
163
|
+
if( pattern[target][i] && (target !== "days" || (pattern.daysOfWeek[this.getDate(true).getDay()])) ) {
|
|
164
|
+
|
|
165
|
+
// Special handling for L (last day of month), when we are searching for days
|
|
166
|
+
if (target === "days" && pattern.lastDayOfMonth) {
|
|
167
|
+
let baseDate = this.getDate(true);
|
|
168
|
+
|
|
169
|
+
// Set days to one day after today, if month changes, then we are at the last day of the month
|
|
170
|
+
baseDate.setDate(i-offset+1);
|
|
171
|
+
if (baseDate.getMonth() !== this["months"]) {
|
|
172
|
+
this[target] = i-offset;
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Normal handling
|
|
177
|
+
} else {
|
|
178
|
+
this[target] = i-offset;
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
164
182
|
}
|
|
165
183
|
}
|
|
166
184
|
return false;
|
|
@@ -227,7 +245,6 @@
|
|
|
227
245
|
|
|
228
246
|
}
|
|
229
247
|
|
|
230
|
-
|
|
231
248
|
// Bail out if an impossible pattern is used
|
|
232
249
|
if (this.years >= 4000) {
|
|
233
250
|
return null;
|
|
@@ -236,36 +253,7 @@
|
|
|
236
253
|
// Gp down, seconds -> minutes -> hours -> days -> months -> year
|
|
237
254
|
doing++;
|
|
238
255
|
}
|
|
239
|
-
|
|
240
|
-
// This is a special case for last day of month, increase days until days+1 changes month, stop, and re-evaluate
|
|
241
|
-
if (pattern.lastDayOfMonth) {
|
|
242
|
-
let baseDate = this.getDate(true),
|
|
243
|
-
originalDays = this.days;
|
|
244
|
-
|
|
245
|
-
// Set days to one day before the first of next month
|
|
246
|
-
baseDate.setMonth(baseDate.getMonth()+1);
|
|
247
|
-
baseDate.setDate(0);
|
|
248
|
-
if (this.days < baseDate.getDate()) {
|
|
249
|
-
this.days = baseDate.getDate();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// If day has changed, reset everything before days
|
|
253
|
-
if (this.days !== originalDays) {
|
|
254
|
-
doing = 2;
|
|
255
|
-
resetPrevious();
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
256
|
|
|
259
|
-
// This is a special case for weekday, as the user isn't able to combine date/month patterns
|
|
260
|
-
// with weekday patterns, it's just to increment days until we get a match.
|
|
261
|
-
while (!pattern.daysOfWeek[this.getDate(true).getDay()]) {
|
|
262
|
-
this.days += 1;
|
|
263
|
-
|
|
264
|
-
// Reset everything before days
|
|
265
|
-
doing = 2;
|
|
266
|
-
resetPrevious();
|
|
267
|
-
}
|
|
268
|
-
|
|
269
257
|
// If anything changed, recreate this CronDate and run again without incrementing
|
|
270
258
|
if (origTime != this.getTime()) {
|
|
271
259
|
this.apply();
|
|
@@ -332,7 +320,42 @@
|
|
|
332
320
|
if( isNaN(year) || isNaN(month) || isNaN(day) || isNaN(hour) || isNaN(minute) || isNaN(second) ) {
|
|
333
321
|
return NaN;
|
|
334
322
|
} else {
|
|
335
|
-
|
|
323
|
+
let generatedDate;
|
|
324
|
+
|
|
325
|
+
// Check for UTC flag
|
|
326
|
+
if ((dateTimeString.indexOf("Z") > 0)) {
|
|
327
|
+
|
|
328
|
+
// Handle date as UTC
|
|
329
|
+
generatedDate = new Date(Date.UTC(year, month-1, day, hour, minute, second));
|
|
330
|
+
|
|
331
|
+
// Check generated date
|
|
332
|
+
if (year == generatedDate.getUTCFullYear()
|
|
333
|
+
&& month == generatedDate.getUTCMonth()+1
|
|
334
|
+
&& day == generatedDate.getUTCDate()
|
|
335
|
+
&& hour == generatedDate.getUTCHours()
|
|
336
|
+
&& minute == generatedDate.getUTCMinutes()
|
|
337
|
+
&& second == generatedDate.getUTCSeconds()) {
|
|
338
|
+
return generatedDate;
|
|
339
|
+
} else {
|
|
340
|
+
return NaN;
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
|
|
344
|
+
// Handle date as local time
|
|
345
|
+
generatedDate = new Date(year, month-1, day, hour, minute, second);
|
|
346
|
+
|
|
347
|
+
// Check generated date
|
|
348
|
+
if (year == generatedDate.getFullYear()
|
|
349
|
+
&& month == generatedDate.getMonth()+1
|
|
350
|
+
&& day == generatedDate.getDate()
|
|
351
|
+
&& hour == generatedDate.getHours()
|
|
352
|
+
&& minute == generatedDate.getMinutes()
|
|
353
|
+
&& second == generatedDate.getSeconds()) {
|
|
354
|
+
return generatedDate;
|
|
355
|
+
} else {
|
|
356
|
+
return NaN;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
336
359
|
}
|
|
337
360
|
};
|
|
338
361
|
|
|
@@ -699,7 +722,7 @@
|
|
|
699
722
|
THE SOFTWARE.
|
|
700
723
|
|
|
701
724
|
------------------------------------------------------------------------------------ */
|
|
702
|
-
|
|
725
|
+
|
|
703
726
|
/**
|
|
704
727
|
* @typedef {Object} CronOptions - Cron scheduler options
|
|
705
728
|
* @property {boolean} [paused] - Job is paused
|
|
@@ -723,35 +746,43 @@
|
|
|
723
746
|
* @type {number}
|
|
724
747
|
*/
|
|
725
748
|
const maxDelay = Math.pow(2, 32 - 1) - 1;
|
|
726
|
-
|
|
749
|
+
|
|
727
750
|
/**
|
|
728
751
|
* Cron entrypoint
|
|
729
752
|
*
|
|
730
753
|
* @constructor
|
|
731
|
-
* @param {string} pattern - Input pattern
|
|
754
|
+
* @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string
|
|
732
755
|
* @param {CronOptions|Function} [options] - Options
|
|
733
756
|
* @param {Function} [func] - Function to be run each iteration of pattern
|
|
734
757
|
* @returns {Cron}
|
|
735
758
|
*/
|
|
736
759
|
function Cron (pattern, options, func) {
|
|
737
|
-
|
|
760
|
+
|
|
738
761
|
// Optional "new" keyword
|
|
739
762
|
if( !(this instanceof Cron) ) {
|
|
740
763
|
return new Cron(pattern, options, func);
|
|
741
764
|
}
|
|
742
|
-
|
|
765
|
+
|
|
743
766
|
// Make options optional
|
|
744
767
|
if( typeof options === "function" ) {
|
|
745
768
|
func = options;
|
|
746
769
|
options = void 0;
|
|
747
770
|
}
|
|
748
|
-
|
|
771
|
+
|
|
749
772
|
/** @type {CronOptions} */
|
|
750
773
|
this.options = this.processOptions(options);
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
774
|
+
|
|
775
|
+
// Check if we got a date, or a pattern supplied as first argument
|
|
776
|
+
if (pattern && (pattern instanceof Date)) {
|
|
777
|
+
this.once = new CronDate(pattern, this.options.timezone);
|
|
778
|
+
} else if (pattern && (typeof pattern === "string") && pattern.indexOf(":") > 0) {
|
|
779
|
+
/** @type {CronPattern} */
|
|
780
|
+
this.once = new CronDate(pattern, this.options.timezone);
|
|
781
|
+
} else {
|
|
782
|
+
/** @type {CronPattern} */
|
|
783
|
+
this.pattern = new CronPattern(pattern, this.options.timezone);
|
|
784
|
+
}
|
|
785
|
+
|
|
755
786
|
/**
|
|
756
787
|
* Allow shorthand scheduling
|
|
757
788
|
*/
|
|
@@ -759,11 +790,11 @@
|
|
|
759
790
|
this.fn = func;
|
|
760
791
|
this.schedule();
|
|
761
792
|
}
|
|
762
|
-
|
|
793
|
+
|
|
763
794
|
return this;
|
|
764
|
-
|
|
795
|
+
|
|
765
796
|
}
|
|
766
|
-
|
|
797
|
+
|
|
767
798
|
/**
|
|
768
799
|
* Internal function that validates options, and sets defaults
|
|
769
800
|
* @private
|
|
@@ -772,18 +803,18 @@
|
|
|
772
803
|
* @returns {CronOptions}
|
|
773
804
|
*/
|
|
774
805
|
Cron.prototype.processOptions = function (options) {
|
|
775
|
-
|
|
806
|
+
|
|
776
807
|
// If no options are passed, create empty object
|
|
777
808
|
if (options === void 0) {
|
|
778
809
|
options = {};
|
|
779
810
|
}
|
|
780
|
-
|
|
811
|
+
|
|
781
812
|
// Keep options, or set defaults
|
|
782
813
|
options.paused = (options.paused === void 0) ? false : options.paused;
|
|
783
814
|
options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns;
|
|
784
815
|
options.catch = (options.catch === void 0) ? false : options.catch;
|
|
785
816
|
options.kill = false;
|
|
786
|
-
|
|
817
|
+
|
|
787
818
|
// startAt is set, validate it
|
|
788
819
|
if( options.startAt ) {
|
|
789
820
|
options.startAt = new CronDate(options.startAt, options.timezone);
|
|
@@ -791,14 +822,14 @@
|
|
|
791
822
|
if( options.stopAt ) {
|
|
792
823
|
options.stopAt = new CronDate(options.stopAt, options.timezone);
|
|
793
824
|
}
|
|
794
|
-
|
|
825
|
+
|
|
795
826
|
return options;
|
|
796
827
|
};
|
|
797
|
-
|
|
828
|
+
|
|
798
829
|
/**
|
|
799
830
|
* Find next runtime, based on supplied date. Strips milliseconds.
|
|
800
831
|
*
|
|
801
|
-
* @param {Date} [prev] - Date to start from
|
|
832
|
+
* @param {Date|string} [prev] - Date to start from
|
|
802
833
|
* @returns {Date | null} - Next run time
|
|
803
834
|
*/
|
|
804
835
|
Cron.prototype.next = function (prev) {
|
|
@@ -806,29 +837,24 @@
|
|
|
806
837
|
const next = this._next(prev);
|
|
807
838
|
return next ? next.getDate() : null;
|
|
808
839
|
};
|
|
809
|
-
|
|
840
|
+
|
|
810
841
|
/**
|
|
811
842
|
* Find next n runs, based on supplied date. Strips milliseconds.
|
|
812
843
|
*
|
|
813
844
|
* @param {number} n - Number of runs to enumerate
|
|
814
|
-
* @param {Date} [prev] - Date to start from
|
|
845
|
+
* @param {Date|string} [prev] - Date to start from
|
|
815
846
|
* @returns {Date[]} - Next n run times
|
|
816
847
|
*/
|
|
817
848
|
Cron.prototype.enumerate = function (n, previous) {
|
|
818
849
|
let enumeration = [];
|
|
819
|
-
|
|
820
|
-
while(n--) {
|
|
821
|
-
|
|
822
|
-
if (previous !== null) {
|
|
823
|
-
enumeration.push(previous);
|
|
824
|
-
} else {
|
|
825
|
-
break;
|
|
826
|
-
}
|
|
850
|
+
|
|
851
|
+
while(n-- && (previous = this.next(previous))) {
|
|
852
|
+
enumeration.push(previous);
|
|
827
853
|
}
|
|
828
|
-
|
|
854
|
+
|
|
829
855
|
return enumeration;
|
|
830
856
|
};
|
|
831
|
-
|
|
857
|
+
|
|
832
858
|
/**
|
|
833
859
|
* Is running?
|
|
834
860
|
* @public
|
|
@@ -840,7 +866,7 @@
|
|
|
840
866
|
const running = !this.options.paused && this.fn !== void 0;
|
|
841
867
|
return msLeft !== null && running;
|
|
842
868
|
};
|
|
843
|
-
|
|
869
|
+
|
|
844
870
|
/**
|
|
845
871
|
* Return previous run time
|
|
846
872
|
* @public
|
|
@@ -850,7 +876,7 @@
|
|
|
850
876
|
Cron.prototype.previous = function () {
|
|
851
877
|
return this.previousrun ? this.previousrun.getDate() : null;
|
|
852
878
|
};
|
|
853
|
-
|
|
879
|
+
|
|
854
880
|
/**
|
|
855
881
|
* Internal version of next. Cron needs millseconds internally, hence _next.
|
|
856
882
|
* @private
|
|
@@ -859,27 +885,32 @@
|
|
|
859
885
|
* @returns {CronDate | null} - Next run time
|
|
860
886
|
*/
|
|
861
887
|
Cron.prototype._next = function (prev) {
|
|
862
|
-
|
|
888
|
+
|
|
863
889
|
// Previous run should never be before startAt
|
|
864
890
|
if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) {
|
|
865
|
-
prev =
|
|
891
|
+
prev = this.options.startAt;
|
|
866
892
|
}
|
|
867
|
-
|
|
868
|
-
// Calculate next run
|
|
869
|
-
const nextRun = new CronDate(prev, this.options.timezone).increment(this.pattern);
|
|
870
|
-
|
|
871
|
-
if ((
|
|
893
|
+
|
|
894
|
+
// Calculate next run according to pattern or one-off timestamp
|
|
895
|
+
const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern);
|
|
896
|
+
|
|
897
|
+
if (this.once && this.once.getTime(true) <= prev.getTime(true)) {
|
|
898
|
+
return null;
|
|
899
|
+
|
|
900
|
+
} else if ((nextRun === null) ||
|
|
872
901
|
(this.options.maxRuns <= 0) ||
|
|
873
902
|
(this.options.kill) ||
|
|
874
903
|
(this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) {
|
|
875
904
|
return null;
|
|
905
|
+
|
|
876
906
|
} else {
|
|
877
907
|
// All seem good, return next run
|
|
878
908
|
return nextRun;
|
|
909
|
+
|
|
879
910
|
}
|
|
880
|
-
|
|
911
|
+
|
|
881
912
|
};
|
|
882
|
-
|
|
913
|
+
|
|
883
914
|
/**
|
|
884
915
|
* Returns number of milliseconds to next run
|
|
885
916
|
* @public
|
|
@@ -896,7 +927,7 @@
|
|
|
896
927
|
return null;
|
|
897
928
|
}
|
|
898
929
|
};
|
|
899
|
-
|
|
930
|
+
|
|
900
931
|
/**
|
|
901
932
|
* Stop execution
|
|
902
933
|
* @public
|
|
@@ -908,7 +939,7 @@
|
|
|
908
939
|
clearTimeout( this.currentTimeout );
|
|
909
940
|
}
|
|
910
941
|
};
|
|
911
|
-
|
|
942
|
+
|
|
912
943
|
/**
|
|
913
944
|
* Pause executionR
|
|
914
945
|
* @public
|
|
@@ -918,7 +949,7 @@
|
|
|
918
949
|
Cron.prototype.pause = function () {
|
|
919
950
|
return (this.options.paused = true) && !this.options.kill;
|
|
920
951
|
};
|
|
921
|
-
|
|
952
|
+
|
|
922
953
|
/**
|
|
923
954
|
* Pause execution
|
|
924
955
|
* @public
|
|
@@ -928,7 +959,7 @@
|
|
|
928
959
|
Cron.prototype.resume = function () {
|
|
929
960
|
return !(this.options.paused = false) && !this.options.kill;
|
|
930
961
|
};
|
|
931
|
-
|
|
962
|
+
|
|
932
963
|
/**
|
|
933
964
|
* Schedule a new job
|
|
934
965
|
* @public
|
|
@@ -937,36 +968,32 @@
|
|
|
937
968
|
* @returns {Cron}
|
|
938
969
|
*/
|
|
939
970
|
Cron.prototype.schedule = function (func) {
|
|
940
|
-
|
|
971
|
+
|
|
941
972
|
// If a function is already scheduled, bail out
|
|
942
973
|
if (func && this.fn) {
|
|
943
974
|
throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");
|
|
944
|
-
|
|
945
|
-
|
|
975
|
+
|
|
976
|
+
// Update function if passed
|
|
946
977
|
} else if (func) {
|
|
947
978
|
this.fn = func;
|
|
948
979
|
}
|
|
949
|
-
|
|
980
|
+
|
|
950
981
|
// Get ms to next run, bail out early if waitMs is null (no next run)
|
|
951
982
|
let waitMs = this.msToNext(this.previousrun);
|
|
952
983
|
if ( waitMs === null ) return this;
|
|
953
|
-
|
|
954
|
-
// Prioritize context before closure,
|
|
955
|
-
// to allow testing of maximum delay.
|
|
956
|
-
const _maxDelay = this.maxDelay || maxDelay;
|
|
957
|
-
|
|
984
|
+
|
|
958
985
|
// setTimeout cant handle more than Math.pow(2, 32 - 1) - 1 ms
|
|
959
|
-
if( waitMs >
|
|
960
|
-
waitMs =
|
|
986
|
+
if( waitMs > maxDelay ) {
|
|
987
|
+
waitMs = maxDelay;
|
|
961
988
|
}
|
|
962
|
-
|
|
989
|
+
|
|
963
990
|
// Ok, go!
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if( waitMs !==
|
|
967
|
-
|
|
991
|
+
this.currentTimeout = setTimeout(() => {
|
|
992
|
+
|
|
993
|
+
if( waitMs !== maxDelay && !this.options.paused ) {
|
|
994
|
+
|
|
968
995
|
this.options.maxRuns--;
|
|
969
|
-
|
|
996
|
+
|
|
970
997
|
// Always catch errors, but only re-throw if options.catch is not set
|
|
971
998
|
if (this.options.catch) {
|
|
972
999
|
try {
|
|
@@ -977,20 +1004,18 @@
|
|
|
977
1004
|
} else {
|
|
978
1005
|
this.fn(this, this.options.context);
|
|
979
1006
|
}
|
|
980
|
-
|
|
1007
|
+
|
|
981
1008
|
this.previousrun = new CronDate(void 0, this.options.timezone);
|
|
982
|
-
|
|
1009
|
+
|
|
983
1010
|
}
|
|
984
|
-
|
|
1011
|
+
|
|
985
1012
|
// Recurse
|
|
986
1013
|
this.schedule();
|
|
987
|
-
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
this.currentTimeout = setTimeout(go, waitMs );
|
|
991
1014
|
|
|
1015
|
+
}, waitMs );
|
|
1016
|
+
|
|
992
1017
|
return this;
|
|
993
|
-
|
|
1018
|
+
|
|
994
1019
|
};
|
|
995
1020
|
|
|
996
1021
|
return Cron;
|
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,fromLocal){if(this.timezone&&!fromLocal){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,true)};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]){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++}if(pattern.lastDayOfMonth){let baseDate=this.getDate(true),originalDays=this.days;baseDate.setMonth(baseDate.getMonth()+1);baseDate.setDate(0);if(this.days<baseDate.getDate()){this.days=baseDate.getDate()}if(this.days!==originalDays){doing=2;resetPrevious()}}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{return new Date(year,month-1,day,hour,minute,second)}};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)+valueIndexOffset;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+valueIndexOffset]=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+valueIndexOffset]=1}};CronPattern.prototype.handleStepping=function(conf,type,valueIndexOffset){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+valueIndexOffset]=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);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);if(previous!==null){enumeration.push(previous)}else{break}}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=new CronDate(this.options.startAt,this.options.timezone)}const nextRun=new CronDate(prev,this.options.timezone).increment(this.pattern);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;const _maxDelay=this.maxDelay||maxDelay;if(waitMs>_maxDelay){waitMs=_maxDelay}const go=()=>{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()};this.currentTimeout=setTimeout(go,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 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]&&(target!=="days"||pattern.daysOfWeek[this.getDate(true).getDay()])){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++}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)+valueIndexOffset;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+valueIndexOffset]=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+valueIndexOffset]=1}};CronPattern.prototype.handleStepping=function(conf,type,valueIndexOffset){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+valueIndexOffset]=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});
|