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 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
- // How many days left to christmas eve?
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 isn't good enough. They have serious bugs, use bloated dependencies, does not work in all environments and/or just doesn't work as expected.
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
- #### Expressions
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
- - *?*: 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.
217
- - *L*: L can be used in the day of month field, to specify the last day of the month.
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 in Europe/Stockholm.');
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 second 15. And quit on second 20.');
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 {date} date - Input date
19
+ * @param {Date} date - Input date
20
20
  * @param {string} tzString - Timezone string in Europe/Stockholm format
21
- * @returns {date}
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, fromLocal) {
57
+ CronDate.prototype.fromDate = function (date) {
59
58
 
60
- if (this.timezone && !fromLocal) {
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 = date.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, true);
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
- if( pattern[target][i] ) {
162
- this[target] = i-offset;
163
- return true;
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
- return new Date(year, month-1, day, hour, minute, second);
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
- /** @type {CronPattern} */
753
- this.pattern = new CronPattern(pattern, this.options.timezone);
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
- previous = this.next(previous);
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 = new CronDate(this.options.startAt, this.options.timezone);
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 ((nextRun === null) ||
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
- // Update function if passed
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 > _maxDelay ) {
960
- waitMs = _maxDelay;
986
+ if( waitMs > maxDelay ) {
987
+ waitMs = maxDelay;
961
988
  }
962
-
989
+
963
990
  // Ok, go!
964
- const go = () => {
965
-
966
- if( waitMs !== _maxDelay && !this.options.paused ) {
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;
@@ -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});