@tachybase/module-cron 1.3.18 → 1.3.20
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/dist/client/cron-jobs-table/CronJobsTable.schema.d.ts +2 -3
- package/dist/client/index.js +3 -3
- package/dist/externalVersion.js +7 -7
- package/dist/node_modules/cron-parser/LICENSE +1 -1
- package/dist/node_modules/cron-parser/lib/date.js +252 -0
- package/dist/node_modules/cron-parser/lib/expression.js +1002 -0
- package/dist/node_modules/cron-parser/lib/field_compactor.js +70 -0
- package/dist/node_modules/cron-parser/lib/field_stringify.js +58 -0
- package/dist/node_modules/cron-parser/lib/parser.js +1 -0
- package/dist/node_modules/cron-parser/package.json +1 -1
- package/dist/node_modules/cron-parser/types/common.d.ts +131 -0
- package/dist/node_modules/cron-parser/types/index.d.ts +45 -0
- package/dist/node_modules/cron-parser/types/ts3/index.d.ts +28 -0
- package/dist/server/service/StaticScheduleTrigger.d.ts +1 -1
- package/package.json +10 -10
- package/dist/node_modules/cron-parser/dist/CronDate.js +0 -497
- package/dist/node_modules/cron-parser/dist/CronExpression.js +0 -376
- package/dist/node_modules/cron-parser/dist/CronExpressionParser.js +0 -384
- package/dist/node_modules/cron-parser/dist/CronFieldCollection.js +0 -371
- package/dist/node_modules/cron-parser/dist/CronFileParser.js +0 -109
- package/dist/node_modules/cron-parser/dist/fields/CronDayOfMonth.js +0 -44
- package/dist/node_modules/cron-parser/dist/fields/CronDayOfWeek.js +0 -51
- package/dist/node_modules/cron-parser/dist/fields/CronField.js +0 -183
- package/dist/node_modules/cron-parser/dist/fields/CronHour.js +0 -40
- package/dist/node_modules/cron-parser/dist/fields/CronMinute.js +0 -40
- package/dist/node_modules/cron-parser/dist/fields/CronMonth.js +0 -44
- package/dist/node_modules/cron-parser/dist/fields/CronSecond.js +0 -40
- package/dist/node_modules/cron-parser/dist/fields/index.js +0 -24
- package/dist/node_modules/cron-parser/dist/fields/types.js +0 -2
- package/dist/node_modules/cron-parser/dist/index.js +0 -1
- package/dist/node_modules/cron-parser/dist/types/CronDate.d.ts +0 -273
- package/dist/node_modules/cron-parser/dist/types/CronExpression.d.ts +0 -110
- package/dist/node_modules/cron-parser/dist/types/CronExpressionParser.d.ts +0 -70
- package/dist/node_modules/cron-parser/dist/types/CronFieldCollection.d.ts +0 -153
- package/dist/node_modules/cron-parser/dist/types/CronFileParser.d.ts +0 -30
- package/dist/node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts +0 -25
- package/dist/node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts +0 -30
- package/dist/node_modules/cron-parser/dist/types/fields/CronField.d.ts +0 -114
- package/dist/node_modules/cron-parser/dist/types/fields/CronHour.d.ts +0 -23
- package/dist/node_modules/cron-parser/dist/types/fields/CronMinute.d.ts +0 -23
- package/dist/node_modules/cron-parser/dist/types/fields/CronMonth.d.ts +0 -24
- package/dist/node_modules/cron-parser/dist/types/fields/CronSecond.d.ts +0 -23
- package/dist/node_modules/cron-parser/dist/types/fields/index.d.ts +0 -8
- package/dist/node_modules/cron-parser/dist/types/fields/types.d.ts +0 -18
- package/dist/node_modules/cron-parser/dist/types/index.d.ts +0 -8
- package/dist/node_modules/cron-parser/dist/types/utils/random.d.ts +0 -10
- package/dist/node_modules/cron-parser/dist/utils/random.js +0 -38
|
@@ -0,0 +1,1002 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Load Date class extensions
|
|
4
|
+
var CronDate = require('./date');
|
|
5
|
+
|
|
6
|
+
var stringifyField = require('./field_stringify');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Cron iteration loop safety limit
|
|
10
|
+
*/
|
|
11
|
+
var LOOP_LIMIT = 10000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Construct a new expression parser
|
|
15
|
+
*
|
|
16
|
+
* Options:
|
|
17
|
+
* currentDate: iterator start date
|
|
18
|
+
* endDate: iterator end date
|
|
19
|
+
*
|
|
20
|
+
* @constructor
|
|
21
|
+
* @private
|
|
22
|
+
* @param {Object} fields Expression fields parsed values
|
|
23
|
+
* @param {Object} options Parser options
|
|
24
|
+
*/
|
|
25
|
+
function CronExpression (fields, options) {
|
|
26
|
+
this._options = options;
|
|
27
|
+
this._utc = options.utc || false;
|
|
28
|
+
this._tz = this._utc ? 'UTC' : options.tz;
|
|
29
|
+
this._currentDate = new CronDate(options.currentDate, this._tz);
|
|
30
|
+
this._startDate = options.startDate ? new CronDate(options.startDate, this._tz) : null;
|
|
31
|
+
this._endDate = options.endDate ? new CronDate(options.endDate, this._tz) : null;
|
|
32
|
+
this._isIterator = options.iterator || false;
|
|
33
|
+
this._hasIterated = false;
|
|
34
|
+
this._nthDayOfWeek = options.nthDayOfWeek || 0;
|
|
35
|
+
this.fields = CronExpression._freezeFields(fields);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Field mappings
|
|
40
|
+
* @type {Array}
|
|
41
|
+
*/
|
|
42
|
+
CronExpression.map = [ 'second', 'minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek' ];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Prefined intervals
|
|
46
|
+
* @type {Object}
|
|
47
|
+
*/
|
|
48
|
+
CronExpression.predefined = {
|
|
49
|
+
'@yearly': '0 0 1 1 *',
|
|
50
|
+
'@monthly': '0 0 1 * *',
|
|
51
|
+
'@weekly': '0 0 * * 0',
|
|
52
|
+
'@daily': '0 0 * * *',
|
|
53
|
+
'@hourly': '0 * * * *'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Fields constraints
|
|
58
|
+
* @type {Array}
|
|
59
|
+
*/
|
|
60
|
+
CronExpression.constraints = [
|
|
61
|
+
{ min: 0, max: 59, chars: [] }, // Second
|
|
62
|
+
{ min: 0, max: 59, chars: [] }, // Minute
|
|
63
|
+
{ min: 0, max: 23, chars: [] }, // Hour
|
|
64
|
+
{ min: 1, max: 31, chars: ['L'] }, // Day of month
|
|
65
|
+
{ min: 1, max: 12, chars: [] }, // Month
|
|
66
|
+
{ min: 0, max: 7, chars: ['L'] }, // Day of week
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Days in month
|
|
71
|
+
* @type {number[]}
|
|
72
|
+
*/
|
|
73
|
+
CronExpression.daysInMonth = [
|
|
74
|
+
31,
|
|
75
|
+
29,
|
|
76
|
+
31,
|
|
77
|
+
30,
|
|
78
|
+
31,
|
|
79
|
+
30,
|
|
80
|
+
31,
|
|
81
|
+
31,
|
|
82
|
+
30,
|
|
83
|
+
31,
|
|
84
|
+
30,
|
|
85
|
+
31
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Field aliases
|
|
90
|
+
* @type {Object}
|
|
91
|
+
*/
|
|
92
|
+
CronExpression.aliases = {
|
|
93
|
+
month: {
|
|
94
|
+
jan: 1,
|
|
95
|
+
feb: 2,
|
|
96
|
+
mar: 3,
|
|
97
|
+
apr: 4,
|
|
98
|
+
may: 5,
|
|
99
|
+
jun: 6,
|
|
100
|
+
jul: 7,
|
|
101
|
+
aug: 8,
|
|
102
|
+
sep: 9,
|
|
103
|
+
oct: 10,
|
|
104
|
+
nov: 11,
|
|
105
|
+
dec: 12
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
dayOfWeek: {
|
|
109
|
+
sun: 0,
|
|
110
|
+
mon: 1,
|
|
111
|
+
tue: 2,
|
|
112
|
+
wed: 3,
|
|
113
|
+
thu: 4,
|
|
114
|
+
fri: 5,
|
|
115
|
+
sat: 6
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Field defaults
|
|
121
|
+
* @type {Array}
|
|
122
|
+
*/
|
|
123
|
+
CronExpression.parseDefaults = [ '0', '*', '*', '*', '*', '*' ];
|
|
124
|
+
|
|
125
|
+
CronExpression.standardValidCharacters = /^[,*\d/-]+$/;
|
|
126
|
+
CronExpression.dayOfWeekValidCharacters = /^[?,*\dL#/-]+$/;
|
|
127
|
+
CronExpression.dayOfMonthValidCharacters = /^[?,*\dL/-]+$/;
|
|
128
|
+
CronExpression.validCharacters = {
|
|
129
|
+
second: CronExpression.standardValidCharacters,
|
|
130
|
+
minute: CronExpression.standardValidCharacters,
|
|
131
|
+
hour: CronExpression.standardValidCharacters,
|
|
132
|
+
dayOfMonth: CronExpression.dayOfMonthValidCharacters,
|
|
133
|
+
month: CronExpression.standardValidCharacters,
|
|
134
|
+
dayOfWeek: CronExpression.dayOfWeekValidCharacters,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
CronExpression._isValidConstraintChar = function _isValidConstraintChar(constraints, value) {
|
|
138
|
+
if (typeof value !== 'string') {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return constraints.chars.some(function(char) {
|
|
143
|
+
return value.indexOf(char) > -1;
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Parse input interval
|
|
149
|
+
*
|
|
150
|
+
* @param {String} field Field symbolic name
|
|
151
|
+
* @param {String} value Field value
|
|
152
|
+
* @param {Array} constraints Range upper and lower constraints
|
|
153
|
+
* @return {Array} Sequence of sorted values
|
|
154
|
+
* @private
|
|
155
|
+
*/
|
|
156
|
+
CronExpression._parseField = function _parseField (field, value, constraints) {
|
|
157
|
+
// Replace aliases
|
|
158
|
+
switch (field) {
|
|
159
|
+
case 'month':
|
|
160
|
+
case 'dayOfWeek':
|
|
161
|
+
var aliases = CronExpression.aliases[field];
|
|
162
|
+
|
|
163
|
+
value = value.replace(/[a-z]{3}/gi, function(match) {
|
|
164
|
+
match = match.toLowerCase();
|
|
165
|
+
|
|
166
|
+
if (typeof aliases[match] !== 'undefined') {
|
|
167
|
+
return aliases[match];
|
|
168
|
+
} else {
|
|
169
|
+
throw new Error('Validation error, cannot resolve alias "' + match + '"');
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check for valid characters.
|
|
176
|
+
if (!(CronExpression.validCharacters[field].test(value))) {
|
|
177
|
+
throw new Error('Invalid characters, got value: ' + value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Replace '*' and '?'
|
|
181
|
+
if (value.indexOf('*') !== -1) {
|
|
182
|
+
value = value.replace(/\*/g, constraints.min + '-' + constraints.max);
|
|
183
|
+
} else if (value.indexOf('?') !== -1) {
|
|
184
|
+
value = value.replace(/\?/g, constraints.min + '-' + constraints.max);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
//
|
|
188
|
+
// Inline parsing functions
|
|
189
|
+
//
|
|
190
|
+
// Parser path:
|
|
191
|
+
// - parseSequence
|
|
192
|
+
// - parseRepeat
|
|
193
|
+
// - parseRange
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parse sequence
|
|
197
|
+
*
|
|
198
|
+
* @param {String} val
|
|
199
|
+
* @return {Array}
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
function parseSequence (val) {
|
|
203
|
+
var stack = [];
|
|
204
|
+
|
|
205
|
+
function handleResult (result) {
|
|
206
|
+
if (result instanceof Array) { // Make sequence linear
|
|
207
|
+
for (var i = 0, c = result.length; i < c; i++) {
|
|
208
|
+
var value = result[i];
|
|
209
|
+
|
|
210
|
+
if (CronExpression._isValidConstraintChar(constraints, value)) {
|
|
211
|
+
stack.push(value);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
// Check constraints
|
|
215
|
+
if (typeof value !== 'number' || Number.isNaN(value) || value < constraints.min || value > constraints.max) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
'Constraint error, got value ' + value + ' expected range ' +
|
|
218
|
+
constraints.min + '-' + constraints.max
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
stack.push(value);
|
|
223
|
+
}
|
|
224
|
+
} else { // Scalar value
|
|
225
|
+
|
|
226
|
+
if (CronExpression._isValidConstraintChar(constraints, result)) {
|
|
227
|
+
stack.push(result);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
var numResult = +result;
|
|
232
|
+
|
|
233
|
+
// Check constraints
|
|
234
|
+
if (Number.isNaN(numResult) || numResult < constraints.min || numResult > constraints.max) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
'Constraint error, got value ' + result + ' expected range ' +
|
|
237
|
+
constraints.min + '-' + constraints.max
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (field === 'dayOfWeek') {
|
|
242
|
+
numResult = numResult % 7;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
stack.push(numResult);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var atoms = val.split(',');
|
|
250
|
+
if (!atoms.every(function (atom) {
|
|
251
|
+
return atom.length > 0;
|
|
252
|
+
})) {
|
|
253
|
+
throw new Error('Invalid list value format');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (atoms.length > 1) {
|
|
257
|
+
for (var i = 0, c = atoms.length; i < c; i++) {
|
|
258
|
+
handleResult(parseRepeat(atoms[i]));
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
handleResult(parseRepeat(val));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
stack.sort(CronExpression._sortCompareFn);
|
|
265
|
+
|
|
266
|
+
return stack;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Parse repetition interval
|
|
271
|
+
*
|
|
272
|
+
* @param {String} val
|
|
273
|
+
* @return {Array}
|
|
274
|
+
*/
|
|
275
|
+
function parseRepeat (val) {
|
|
276
|
+
var repeatInterval = 1;
|
|
277
|
+
var atoms = val.split('/');
|
|
278
|
+
|
|
279
|
+
if (atoms.length > 2) {
|
|
280
|
+
throw new Error('Invalid repeat: ' + val);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (atoms.length > 1) {
|
|
284
|
+
if (atoms[0] == +atoms[0]) {
|
|
285
|
+
atoms = [atoms[0] + '-' + constraints.max, atoms[1]];
|
|
286
|
+
}
|
|
287
|
+
return parseRange(atoms[0], atoms[atoms.length - 1]);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return parseRange(val, repeatInterval);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Parse range
|
|
295
|
+
*
|
|
296
|
+
* @param {String} val
|
|
297
|
+
* @param {Number} repeatInterval Repetition interval
|
|
298
|
+
* @return {Array}
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
function parseRange (val, repeatInterval) {
|
|
302
|
+
var stack = [];
|
|
303
|
+
var atoms = val.split('-');
|
|
304
|
+
|
|
305
|
+
if (atoms.length > 1 ) {
|
|
306
|
+
// Invalid range, return value
|
|
307
|
+
if (atoms.length < 2) {
|
|
308
|
+
return +val;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!atoms[0].length) {
|
|
312
|
+
if (!atoms[1].length) {
|
|
313
|
+
throw new Error('Invalid range: ' + val);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return +val;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Validate range
|
|
320
|
+
var min = +atoms[0];
|
|
321
|
+
var max = +atoms[1];
|
|
322
|
+
|
|
323
|
+
if (Number.isNaN(min) || Number.isNaN(max) ||
|
|
324
|
+
min < constraints.min || max > constraints.max) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
'Constraint error, got range ' +
|
|
327
|
+
min + '-' + max +
|
|
328
|
+
' expected range ' +
|
|
329
|
+
constraints.min + '-' + constraints.max
|
|
330
|
+
);
|
|
331
|
+
} else if (min > max) {
|
|
332
|
+
throw new Error('Invalid range: ' + val);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Create range
|
|
336
|
+
var repeatIndex = +repeatInterval;
|
|
337
|
+
|
|
338
|
+
if (Number.isNaN(repeatIndex) || repeatIndex <= 0) {
|
|
339
|
+
throw new Error('Constraint error, cannot repeat at every ' + repeatIndex + ' time.');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// JS DOW is in range of 0-6 (SUN-SAT) but we also support 7 in the expression
|
|
343
|
+
// Handle case when range contains 7 instead of 0 and translate this value to 0
|
|
344
|
+
if (field === 'dayOfWeek' && max % 7 === 0) {
|
|
345
|
+
stack.push(0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (var index = min, count = max; index <= count; index++) {
|
|
349
|
+
var exists = stack.indexOf(index) !== -1;
|
|
350
|
+
if (!exists && repeatIndex > 0 && (repeatIndex % repeatInterval) === 0) {
|
|
351
|
+
repeatIndex = 1;
|
|
352
|
+
stack.push(index);
|
|
353
|
+
} else {
|
|
354
|
+
repeatIndex++;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return stack;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return Number.isNaN(+val) ? val : +val;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return parseSequence(value);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
CronExpression._sortCompareFn = function(a, b) {
|
|
367
|
+
var aIsNumber = typeof a === 'number';
|
|
368
|
+
var bIsNumber = typeof b === 'number';
|
|
369
|
+
|
|
370
|
+
if (aIsNumber && bIsNumber) {
|
|
371
|
+
return a - b;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!aIsNumber && bIsNumber) {
|
|
375
|
+
return 1;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (aIsNumber && !bIsNumber) {
|
|
379
|
+
return -1;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return a.localeCompare(b);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
CronExpression._handleMaxDaysInMonth = function(mappedFields) {
|
|
386
|
+
// Filter out any day of month value that is larger than given month expects
|
|
387
|
+
if (mappedFields.month.length === 1) {
|
|
388
|
+
var daysInMonth = CronExpression.daysInMonth[mappedFields.month[0] - 1];
|
|
389
|
+
|
|
390
|
+
if (mappedFields.dayOfMonth[0] > daysInMonth) {
|
|
391
|
+
throw new Error('Invalid explicit day of month definition');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return mappedFields.dayOfMonth
|
|
395
|
+
.filter(function(dayOfMonth) {
|
|
396
|
+
return dayOfMonth === 'L' ? true : dayOfMonth <= daysInMonth;
|
|
397
|
+
})
|
|
398
|
+
.sort(CronExpression._sortCompareFn);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
CronExpression._freezeFields = function(fields) {
|
|
403
|
+
for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
|
|
404
|
+
var field = CronExpression.map[i]; // Field name
|
|
405
|
+
var value = fields[field];
|
|
406
|
+
fields[field] = Object.freeze(value);
|
|
407
|
+
}
|
|
408
|
+
return Object.freeze(fields);
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
CronExpression.prototype._applyTimezoneShift = function(currentDate, dateMathVerb, method) {
|
|
412
|
+
if ((method === 'Month') || (method === 'Day')) {
|
|
413
|
+
var prevTime = currentDate.getTime();
|
|
414
|
+
currentDate[dateMathVerb + method]();
|
|
415
|
+
var currTime = currentDate.getTime();
|
|
416
|
+
if (prevTime === currTime) {
|
|
417
|
+
// Jumped into a not existent date due to a DST transition
|
|
418
|
+
if ((currentDate.getMinutes() === 0) &&
|
|
419
|
+
(currentDate.getSeconds() === 0)) {
|
|
420
|
+
currentDate.addHour();
|
|
421
|
+
} else if ((currentDate.getMinutes() === 59) &&
|
|
422
|
+
(currentDate.getSeconds() === 59)) {
|
|
423
|
+
currentDate.subtractHour();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
var previousHour = currentDate.getHours();
|
|
428
|
+
currentDate[dateMathVerb + method]();
|
|
429
|
+
var currentHour = currentDate.getHours();
|
|
430
|
+
var diff = currentHour - previousHour;
|
|
431
|
+
if (diff === 2) {
|
|
432
|
+
// Starting DST
|
|
433
|
+
if (this.fields.hour.length !== 24) {
|
|
434
|
+
// Hour is specified
|
|
435
|
+
this._dstStart = currentHour;
|
|
436
|
+
}
|
|
437
|
+
} else if ((diff === 0) &&
|
|
438
|
+
(currentDate.getMinutes() === 0) &&
|
|
439
|
+
(currentDate.getSeconds() === 0)) {
|
|
440
|
+
// Ending DST
|
|
441
|
+
if (this.fields.hour.length !== 24) {
|
|
442
|
+
// Hour is specified
|
|
443
|
+
this._dstEnd = currentHour;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Find next or previous matching schedule date
|
|
452
|
+
*
|
|
453
|
+
* @return {CronDate}
|
|
454
|
+
* @private
|
|
455
|
+
*/
|
|
456
|
+
CronExpression.prototype._findSchedule = function _findSchedule (reverse) {
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Match field value
|
|
460
|
+
*
|
|
461
|
+
* @param {String} value
|
|
462
|
+
* @param {Array} sequence
|
|
463
|
+
* @return {Boolean}
|
|
464
|
+
* @private
|
|
465
|
+
*/
|
|
466
|
+
function matchSchedule (value, sequence) {
|
|
467
|
+
for (var i = 0, c = sequence.length; i < c; i++) {
|
|
468
|
+
if (sequence[i] >= value) {
|
|
469
|
+
return sequence[i] === value;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return sequence[0] === value;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Helps determine if the provided date is the correct nth occurence of the
|
|
478
|
+
* desired day of week.
|
|
479
|
+
*
|
|
480
|
+
* @param {CronDate} date
|
|
481
|
+
* @param {Number} nthDayOfWeek
|
|
482
|
+
* @return {Boolean}
|
|
483
|
+
* @private
|
|
484
|
+
*/
|
|
485
|
+
function isNthDayMatch(date, nthDayOfWeek) {
|
|
486
|
+
if (nthDayOfWeek < 6) {
|
|
487
|
+
if (
|
|
488
|
+
date.getDate() < 8 &&
|
|
489
|
+
nthDayOfWeek === 1 // First occurence has to happen in first 7 days of the month
|
|
490
|
+
) {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
var offset = date.getDate() % 7 ? 1 : 0; // Math is off by 1 when dayOfWeek isn't divisible by 7
|
|
495
|
+
var adjustedDate = date.getDate() - (date.getDate() % 7); // find the first occurance
|
|
496
|
+
var occurrence = Math.floor(adjustedDate / 7) + offset;
|
|
497
|
+
|
|
498
|
+
return occurrence === nthDayOfWeek;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Helper function that checks if 'L' is in the array
|
|
506
|
+
*
|
|
507
|
+
* @param {Array} expressions
|
|
508
|
+
*/
|
|
509
|
+
function isLInExpressions(expressions) {
|
|
510
|
+
return expressions.length > 0 && expressions.some(function(expression) {
|
|
511
|
+
return typeof expression === 'string' && expression.indexOf('L') >= 0;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
// Whether to use backwards directionality when searching
|
|
517
|
+
reverse = reverse || false;
|
|
518
|
+
var dateMathVerb = reverse ? 'subtract' : 'add';
|
|
519
|
+
|
|
520
|
+
var currentDate = new CronDate(this._currentDate, this._tz);
|
|
521
|
+
var startDate = this._startDate;
|
|
522
|
+
var endDate = this._endDate;
|
|
523
|
+
|
|
524
|
+
// Find matching schedule
|
|
525
|
+
var startTimestamp = currentDate.getTime();
|
|
526
|
+
var stepCount = 0;
|
|
527
|
+
|
|
528
|
+
function isLastWeekdayOfMonthMatch(expressions) {
|
|
529
|
+
return expressions.some(function(expression) {
|
|
530
|
+
// There might be multiple expressions and not all of them will contain
|
|
531
|
+
// the "L".
|
|
532
|
+
if (!isLInExpressions([expression])) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// The first character represents the weekday
|
|
537
|
+
var weekday = Number.parseInt(expression[0]) % 7;
|
|
538
|
+
|
|
539
|
+
if (Number.isNaN(weekday)) {
|
|
540
|
+
throw new Error('Invalid last weekday of the month expression: ' + expression);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return currentDate.getDay() === weekday && currentDate.isLastWeekdayOfMonth();
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
while (stepCount < LOOP_LIMIT) {
|
|
548
|
+
stepCount++;
|
|
549
|
+
|
|
550
|
+
// Validate timespan
|
|
551
|
+
if (reverse) {
|
|
552
|
+
if (startDate && (currentDate.getTime() - startDate.getTime() < 0)) {
|
|
553
|
+
throw new Error('Out of the timespan range');
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
if (endDate && (endDate.getTime() - currentDate.getTime()) < 0) {
|
|
557
|
+
throw new Error('Out of the timespan range');
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Day of month and week matching:
|
|
562
|
+
//
|
|
563
|
+
// "The day of a command's execution can be specified by two fields --
|
|
564
|
+
// day of month, and day of week. If both fields are restricted (ie,
|
|
565
|
+
// aren't *), the command will be run when either field matches the cur-
|
|
566
|
+
// rent time. For example, "30 4 1,15 * 5" would cause a command to be
|
|
567
|
+
// run at 4:30 am on the 1st and 15th of each month, plus every Friday."
|
|
568
|
+
//
|
|
569
|
+
// http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
|
|
570
|
+
//
|
|
571
|
+
|
|
572
|
+
var dayOfMonthMatch = matchSchedule(currentDate.getDate(), this.fields.dayOfMonth);
|
|
573
|
+
if (isLInExpressions(this.fields.dayOfMonth)) {
|
|
574
|
+
dayOfMonthMatch = dayOfMonthMatch || currentDate.isLastDayOfMonth();
|
|
575
|
+
}
|
|
576
|
+
var dayOfWeekMatch = matchSchedule(currentDate.getDay(), this.fields.dayOfWeek);
|
|
577
|
+
if (isLInExpressions(this.fields.dayOfWeek)) {
|
|
578
|
+
dayOfWeekMatch = dayOfWeekMatch || isLastWeekdayOfMonthMatch(this.fields.dayOfWeek);
|
|
579
|
+
}
|
|
580
|
+
var isDayOfMonthWildcardMatch = this.fields.dayOfMonth.length >= CronExpression.daysInMonth[currentDate.getMonth()];
|
|
581
|
+
var isDayOfWeekWildcardMatch = this.fields.dayOfWeek.length === CronExpression.constraints[5].max - CronExpression.constraints[5].min + 1;
|
|
582
|
+
var currentHour = currentDate.getHours();
|
|
583
|
+
|
|
584
|
+
// Add or subtract day if select day not match with month (according to calendar)
|
|
585
|
+
if (!dayOfMonthMatch && (!dayOfWeekMatch || isDayOfWeekWildcardMatch)) {
|
|
586
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Add or subtract day if not day of month is set (and no match) and day of week is wildcard
|
|
591
|
+
if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !dayOfMonthMatch) {
|
|
592
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Add or subtract day if not day of week is set (and no match) and day of month is wildcard
|
|
597
|
+
if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !dayOfWeekMatch) {
|
|
598
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Add or subtract day if day of week & nthDayOfWeek are set (and no match)
|
|
603
|
+
if (
|
|
604
|
+
this._nthDayOfWeek > 0 &&
|
|
605
|
+
!isNthDayMatch(currentDate, this._nthDayOfWeek)
|
|
606
|
+
) {
|
|
607
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Match month
|
|
612
|
+
if (!matchSchedule(currentDate.getMonth() + 1, this.fields.month)) {
|
|
613
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Month');
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Match hour
|
|
618
|
+
if (!matchSchedule(currentHour, this.fields.hour)) {
|
|
619
|
+
if (this._dstStart !== currentHour) {
|
|
620
|
+
this._dstStart = null;
|
|
621
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Hour');
|
|
622
|
+
continue;
|
|
623
|
+
} else if (!matchSchedule(currentHour - 1, this.fields.hour)) {
|
|
624
|
+
currentDate[dateMathVerb + 'Hour']();
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
} else if (this._dstEnd === currentHour) {
|
|
628
|
+
if (!reverse) {
|
|
629
|
+
this._dstEnd = null;
|
|
630
|
+
this._applyTimezoneShift(currentDate, 'add', 'Hour');
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Match minute
|
|
636
|
+
if (!matchSchedule(currentDate.getMinutes(), this.fields.minute)) {
|
|
637
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Minute');
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Match second
|
|
642
|
+
if (!matchSchedule(currentDate.getSeconds(), this.fields.second)) {
|
|
643
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Increase a second in case in the first iteration the currentDate was not
|
|
648
|
+
// modified
|
|
649
|
+
if (startTimestamp === currentDate.getTime()) {
|
|
650
|
+
if ((dateMathVerb === 'add') || (currentDate.getMilliseconds() === 0)) {
|
|
651
|
+
this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
|
|
652
|
+
} else {
|
|
653
|
+
currentDate.setMilliseconds(0);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (stepCount >= LOOP_LIMIT) {
|
|
663
|
+
throw new Error('Invalid expression, loop limit exceeded');
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
this._currentDate = new CronDate(currentDate, this._tz);
|
|
667
|
+
this._hasIterated = true;
|
|
668
|
+
|
|
669
|
+
return currentDate;
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Find next suitable date
|
|
674
|
+
*
|
|
675
|
+
* @public
|
|
676
|
+
* @return {CronDate|Object}
|
|
677
|
+
*/
|
|
678
|
+
CronExpression.prototype.next = function next () {
|
|
679
|
+
var schedule = this._findSchedule();
|
|
680
|
+
|
|
681
|
+
// Try to return ES6 compatible iterator
|
|
682
|
+
if (this._isIterator) {
|
|
683
|
+
return {
|
|
684
|
+
value: schedule,
|
|
685
|
+
done: !this.hasNext()
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return schedule;
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Find previous suitable date
|
|
694
|
+
*
|
|
695
|
+
* @public
|
|
696
|
+
* @return {CronDate|Object}
|
|
697
|
+
*/
|
|
698
|
+
CronExpression.prototype.prev = function prev () {
|
|
699
|
+
var schedule = this._findSchedule(true);
|
|
700
|
+
|
|
701
|
+
// Try to return ES6 compatible iterator
|
|
702
|
+
if (this._isIterator) {
|
|
703
|
+
return {
|
|
704
|
+
value: schedule,
|
|
705
|
+
done: !this.hasPrev()
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return schedule;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Check if next suitable date exists
|
|
714
|
+
*
|
|
715
|
+
* @public
|
|
716
|
+
* @return {Boolean}
|
|
717
|
+
*/
|
|
718
|
+
CronExpression.prototype.hasNext = function() {
|
|
719
|
+
var current = this._currentDate;
|
|
720
|
+
var hasIterated = this._hasIterated;
|
|
721
|
+
|
|
722
|
+
try {
|
|
723
|
+
this._findSchedule();
|
|
724
|
+
return true;
|
|
725
|
+
} catch (err) {
|
|
726
|
+
return false;
|
|
727
|
+
} finally {
|
|
728
|
+
this._currentDate = current;
|
|
729
|
+
this._hasIterated = hasIterated;
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Check if previous suitable date exists
|
|
735
|
+
*
|
|
736
|
+
* @public
|
|
737
|
+
* @return {Boolean}
|
|
738
|
+
*/
|
|
739
|
+
CronExpression.prototype.hasPrev = function() {
|
|
740
|
+
var current = this._currentDate;
|
|
741
|
+
var hasIterated = this._hasIterated;
|
|
742
|
+
|
|
743
|
+
try {
|
|
744
|
+
this._findSchedule(true);
|
|
745
|
+
return true;
|
|
746
|
+
} catch (err) {
|
|
747
|
+
return false;
|
|
748
|
+
} finally {
|
|
749
|
+
this._currentDate = current;
|
|
750
|
+
this._hasIterated = hasIterated;
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Iterate over expression iterator
|
|
756
|
+
*
|
|
757
|
+
* @public
|
|
758
|
+
* @param {Number} steps Numbers of steps to iterate
|
|
759
|
+
* @param {Function} callback Optional callback
|
|
760
|
+
* @return {Array} Array of the iterated results
|
|
761
|
+
*/
|
|
762
|
+
CronExpression.prototype.iterate = function iterate (steps, callback) {
|
|
763
|
+
var dates = [];
|
|
764
|
+
|
|
765
|
+
if (steps >= 0) {
|
|
766
|
+
for (var i = 0, c = steps; i < c; i++) {
|
|
767
|
+
try {
|
|
768
|
+
var item = this.next();
|
|
769
|
+
dates.push(item);
|
|
770
|
+
|
|
771
|
+
// Fire the callback
|
|
772
|
+
if (callback) {
|
|
773
|
+
callback(item, i);
|
|
774
|
+
}
|
|
775
|
+
} catch (err) {
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
} else {
|
|
780
|
+
for (var i = 0, c = steps; i > c; i--) {
|
|
781
|
+
try {
|
|
782
|
+
var item = this.prev();
|
|
783
|
+
dates.push(item);
|
|
784
|
+
|
|
785
|
+
// Fire the callback
|
|
786
|
+
if (callback) {
|
|
787
|
+
callback(item, i);
|
|
788
|
+
}
|
|
789
|
+
} catch (err) {
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return dates;
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Reset expression iterator state
|
|
800
|
+
*
|
|
801
|
+
* @public
|
|
802
|
+
*/
|
|
803
|
+
CronExpression.prototype.reset = function reset (newDate) {
|
|
804
|
+
this._currentDate = new CronDate(newDate || this._options.currentDate);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Stringify the expression
|
|
809
|
+
*
|
|
810
|
+
* @public
|
|
811
|
+
* @param {Boolean} [includeSeconds] Should stringify seconds
|
|
812
|
+
* @return {String}
|
|
813
|
+
*/
|
|
814
|
+
CronExpression.prototype.stringify = function stringify(includeSeconds) {
|
|
815
|
+
var resultArr = [];
|
|
816
|
+
for (var i = includeSeconds ? 0 : 1, c = CronExpression.map.length; i < c; ++i) {
|
|
817
|
+
var field = CronExpression.map[i];
|
|
818
|
+
var value = this.fields[field];
|
|
819
|
+
var constraint = CronExpression.constraints[i];
|
|
820
|
+
|
|
821
|
+
if (field === 'dayOfMonth' && this.fields.month.length === 1) {
|
|
822
|
+
constraint = { min: 1, max: CronExpression.daysInMonth[this.fields.month[0] - 1] };
|
|
823
|
+
} else if (field === 'dayOfWeek') {
|
|
824
|
+
// Prefer 0-6 range when serializing day of week field
|
|
825
|
+
constraint = { min: 0, max: 6 };
|
|
826
|
+
value = value[value.length - 1] === 7 ? value.slice(0, -1) : value;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
resultArr.push(stringifyField(value, constraint.min, constraint.max));
|
|
830
|
+
}
|
|
831
|
+
return resultArr.join(' ');
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Parse input expression (async)
|
|
836
|
+
*
|
|
837
|
+
* @public
|
|
838
|
+
* @param {String} expression Input expression
|
|
839
|
+
* @param {Object} [options] Parsing options
|
|
840
|
+
*/
|
|
841
|
+
CronExpression.parse = function parse(expression, options) {
|
|
842
|
+
var self = this;
|
|
843
|
+
if (typeof options === 'function') {
|
|
844
|
+
options = {};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function parse (expression, options) {
|
|
848
|
+
if (!options) {
|
|
849
|
+
options = {};
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (typeof options.currentDate === 'undefined') {
|
|
853
|
+
options.currentDate = new CronDate(undefined, self._tz);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Is input expression predefined?
|
|
857
|
+
if (CronExpression.predefined[expression]) {
|
|
858
|
+
expression = CronExpression.predefined[expression];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Split fields
|
|
862
|
+
var fields = [];
|
|
863
|
+
var atoms = (expression + '').trim().split(/\s+/);
|
|
864
|
+
|
|
865
|
+
if (atoms.length > 6) {
|
|
866
|
+
throw new Error('Invalid cron expression');
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Resolve fields
|
|
870
|
+
var start = (CronExpression.map.length - atoms.length);
|
|
871
|
+
for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
|
|
872
|
+
var field = CronExpression.map[i]; // Field name
|
|
873
|
+
var value = atoms[atoms.length > c ? i : i - start]; // Field value
|
|
874
|
+
|
|
875
|
+
if (i < start || !value) { // Use default value
|
|
876
|
+
fields.push(CronExpression._parseField(
|
|
877
|
+
field,
|
|
878
|
+
CronExpression.parseDefaults[i],
|
|
879
|
+
CronExpression.constraints[i]
|
|
880
|
+
)
|
|
881
|
+
);
|
|
882
|
+
} else {
|
|
883
|
+
var val = field === 'dayOfWeek' ? parseNthDay(value) : value;
|
|
884
|
+
|
|
885
|
+
fields.push(CronExpression._parseField(
|
|
886
|
+
field,
|
|
887
|
+
val,
|
|
888
|
+
CronExpression.constraints[i]
|
|
889
|
+
)
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
var mappedFields = {};
|
|
895
|
+
for (var i = 0, c = CronExpression.map.length; i < c; i++) {
|
|
896
|
+
var key = CronExpression.map[i];
|
|
897
|
+
mappedFields[key] = fields[i];
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
|
|
901
|
+
mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
|
|
902
|
+
return new CronExpression(mappedFields, options);
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Parses out the # special character for the dayOfWeek field & adds it to options.
|
|
906
|
+
*
|
|
907
|
+
* @param {String} val
|
|
908
|
+
* @return {String}
|
|
909
|
+
* @private
|
|
910
|
+
*/
|
|
911
|
+
function parseNthDay(val) {
|
|
912
|
+
var atoms = val.split('#');
|
|
913
|
+
if (atoms.length > 1) {
|
|
914
|
+
var nthValue = +atoms[atoms.length - 1];
|
|
915
|
+
if(/,/.test(val)) {
|
|
916
|
+
throw new Error('Constraint error, invalid dayOfWeek `#` and `,` '
|
|
917
|
+
+ 'special characters are incompatible');
|
|
918
|
+
}
|
|
919
|
+
if(/\//.test(val)) {
|
|
920
|
+
throw new Error('Constraint error, invalid dayOfWeek `#` and `/` '
|
|
921
|
+
+ 'special characters are incompatible');
|
|
922
|
+
}
|
|
923
|
+
if(/-/.test(val)) {
|
|
924
|
+
throw new Error('Constraint error, invalid dayOfWeek `#` and `-` '
|
|
925
|
+
+ 'special characters are incompatible');
|
|
926
|
+
}
|
|
927
|
+
if (atoms.length > 2 || Number.isNaN(nthValue) || (nthValue < 1 || nthValue > 5)) {
|
|
928
|
+
throw new Error('Constraint error, invalid dayOfWeek occurrence number (#)');
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
options.nthDayOfWeek = nthValue;
|
|
932
|
+
return atoms[0];
|
|
933
|
+
}
|
|
934
|
+
return val;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return parse(expression, options);
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Convert cron fields back to Cron Expression
|
|
943
|
+
*
|
|
944
|
+
* @public
|
|
945
|
+
* @param {Object} fields Input fields
|
|
946
|
+
* @param {Object} [options] Parsing options
|
|
947
|
+
* @return {Object}
|
|
948
|
+
*/
|
|
949
|
+
CronExpression.fieldsToExpression = function fieldsToExpression(fields, options) {
|
|
950
|
+
function validateConstraints (field, values, constraints) {
|
|
951
|
+
if (!values) {
|
|
952
|
+
throw new Error('Validation error, Field ' + field + ' is missing');
|
|
953
|
+
}
|
|
954
|
+
if (values.length === 0) {
|
|
955
|
+
throw new Error('Validation error, Field ' + field + ' contains no values');
|
|
956
|
+
}
|
|
957
|
+
for (var i = 0, c = values.length; i < c; i++) {
|
|
958
|
+
var value = values[i];
|
|
959
|
+
|
|
960
|
+
if (CronExpression._isValidConstraintChar(constraints, value)) {
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Check constraints
|
|
965
|
+
if (typeof value !== 'number' || Number.isNaN(value) || value < constraints.min || value > constraints.max) {
|
|
966
|
+
throw new Error(
|
|
967
|
+
'Constraint error, got value ' + value + ' expected range ' +
|
|
968
|
+
constraints.min + '-' + constraints.max
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
var mappedFields = {};
|
|
975
|
+
for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
|
|
976
|
+
var field = CronExpression.map[i]; // Field name
|
|
977
|
+
var values = fields[field];
|
|
978
|
+
validateConstraints(
|
|
979
|
+
field,
|
|
980
|
+
values,
|
|
981
|
+
CronExpression.constraints[i]
|
|
982
|
+
);
|
|
983
|
+
var copy = [];
|
|
984
|
+
var j = -1;
|
|
985
|
+
while (++j < values.length) {
|
|
986
|
+
copy[j] = values[j];
|
|
987
|
+
}
|
|
988
|
+
values = copy.sort(CronExpression._sortCompareFn)
|
|
989
|
+
.filter(function(item, pos, ary) {
|
|
990
|
+
return !pos || item !== ary[pos - 1];
|
|
991
|
+
});
|
|
992
|
+
if (values.length !== copy.length) {
|
|
993
|
+
throw new Error('Validation error, Field ' + field + ' contains duplicate values');
|
|
994
|
+
}
|
|
995
|
+
mappedFields[field] = values;
|
|
996
|
+
}
|
|
997
|
+
var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
|
|
998
|
+
mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
|
|
999
|
+
return new CronExpression(mappedFields, options || {});
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
module.exports = CronExpression;
|