cron-parser
Advanced tools
Comparing version 0.4.5 to 0.5.0
@@ -32,7 +32,9 @@ 'use strict'; | ||
this.setDate(day + 1); | ||
this.setHours(0); | ||
this.setMinutes(0); | ||
this.setSeconds(0); | ||
if (this.getDate() === day) { | ||
this.setDate(day + 1); | ||
this.setDate(day + 2); | ||
} | ||
@@ -45,3 +47,9 @@ }; | ||
Date.prototype.addHour = function addHour () { | ||
this.setHours(this.getHours() + 1); | ||
var hours = this.getHours(); | ||
this.setHours(hours + 1); | ||
if (this.getHours() === hours) { | ||
this.setHours(hours + 2); | ||
} | ||
this.setMinutes(0); | ||
@@ -48,0 +56,0 @@ this.setSeconds(0); |
@@ -20,4 +20,4 @@ 'use strict'; | ||
this._options = options; | ||
this._currentDate = new Date(options.currentDate.toUTCString()); | ||
this._endDate = options.endDate ? new Date(options.endDate.toUTCString()) : null; | ||
this._currentDate = new Date(options.currentDate); | ||
this._endDate = options.endDate ? new Date(options.endDate) : null; | ||
this._fields = {}; | ||
@@ -64,2 +64,21 @@ | ||
/** | ||
* Days in month | ||
* @type {number[]} | ||
*/ | ||
CronExpression.daysInMonth = [ | ||
31, | ||
28, | ||
31, | ||
30, | ||
31, | ||
30, | ||
31, | ||
31, | ||
30, | ||
31, | ||
30, | ||
31 | ||
]; | ||
/** | ||
* Field aliases | ||
@@ -129,4 +148,4 @@ * @type {Object} | ||
//Check for valid characters. | ||
if (!CronExpression._validateCharacters(value)){ | ||
// Check for valid characters. | ||
if (!(/^[\d|/|*|\-|,]+$/.test(value))) { | ||
throw new Error('Invalid characters, got value: ' + value) | ||
@@ -137,3 +156,3 @@ } | ||
if (value.indexOf('*') !== -1) { | ||
value = value.replace(/\*/g, constraints[0] + '-' + constraints[1]); | ||
value = value.replace(/\*/g, constraints.join('-')); | ||
} | ||
@@ -163,8 +182,10 @@ | ||
if (result instanceof Array) { // Make sequence linear | ||
result.forEach(function (value) { | ||
for (var i = 0, c = result.length; i < c; i++) { | ||
var value = result[i]; | ||
// Check constraints | ||
if (!CronExpression._validateConstraint(value, constraints)) { | ||
if (value < constraints[0] || value > constraints[1]) { | ||
throw new Error( | ||
'Constraint error, got value ' + value + ' expected range ' + | ||
constraints[0] + '-' + constraints[1] | ||
'Constraint error, got value ' + value + ' expected range ' + | ||
constraints[0] + '-' + constraints[1] | ||
); | ||
@@ -178,8 +199,8 @@ } | ||
max = Math.max.apply(Math, stack); | ||
}); | ||
} | ||
} else { // Scalar value | ||
result = parseInt(result, 10); | ||
result = +result; | ||
// Check constraints | ||
if (!CronExpression._validateConstraint(result, constraints)) { | ||
if (result < constraints[0] || result > constraints[1]) { | ||
throw new Error( | ||
@@ -201,9 +222,7 @@ 'Constraint error, got value ' + result + ' expected range ' + | ||
if (val.indexOf(',') !== -1) { | ||
var atoms = val.split(','); | ||
atoms.forEach(function(value, index) { | ||
handleResult(parseRepeat(value.toString())); | ||
}); | ||
var atoms = val.split(','); | ||
if (atoms.length > 1) { | ||
for (var i = 0, c = atoms.length; i < c; i++) { | ||
handleResult(parseRepeat(atoms[i])); | ||
} | ||
} else { | ||
@@ -224,11 +243,9 @@ handleResult(parseRepeat(val)); | ||
var repeatInterval = 1; | ||
var atoms = val.split('/'); | ||
if (val.indexOf('/') !== -1) { | ||
var atoms = val.split('/'); | ||
repeatInterval = atoms[atoms.length - 1]; | ||
if (atoms.length > 1) { | ||
return parseRange(atoms[0], atoms[atoms.length - 1]); | ||
} | ||
return parseRange(atoms[0], repeatInterval); | ||
} else { | ||
return parseRange(val, repeatInterval); | ||
} | ||
return parseRange(val, repeatInterval); | ||
} | ||
@@ -246,14 +263,13 @@ | ||
var stack = []; | ||
var atoms = val.split('-'); | ||
if (val.indexOf('-') !== -1) { | ||
var atoms = val.split('-'); | ||
// Validate format | ||
if (atoms.length != 2) { | ||
throw new Error('Invalid range format: ' + val); | ||
if (atoms.length > 1 ) { | ||
// Invalid range, return value | ||
if (atoms.length < 2 || !atoms[0].length) { | ||
return +val; | ||
} | ||
// Validate range | ||
var min = parseInt(atoms[0], 10); | ||
var max = parseInt(atoms[1], 10); | ||
var min = +atoms[0]; | ||
var max = +atoms[1]; | ||
@@ -285,5 +301,5 @@ if (Number.isNaN(min) || Number.isNaN(max) || | ||
return stack; | ||
} else { | ||
return val; | ||
} | ||
return +val; | ||
} | ||
@@ -295,117 +311,61 @@ | ||
/** | ||
* Detect if input range fully matches constraint bounds | ||
* @param {Array} range Input range | ||
* @param {Array} constraints Input constraints | ||
* @returns {Boolean} | ||
* @private | ||
*/ | ||
CronExpression._isWildcardRange = function _isWildcardRange (range, constraints) { | ||
if (!(range instanceof Array)) { | ||
return false; | ||
} | ||
if (constraints.length !== 2) { | ||
return false; | ||
} | ||
return range.length === (constraints[1] - (constraints[0] < 1 ? - 1 : 0)); | ||
}; | ||
/** | ||
* Match field value | ||
* Find next matching schedule date | ||
* | ||
* @param {String} value | ||
* @param {Array} sequence | ||
* @return {Boolean} | ||
* @return {Date} | ||
* @private | ||
*/ | ||
CronExpression._matchSchedule = function matchSchedule (value, sequence) { | ||
for (var i = 0, c = sequence.length; i < c; i++) { | ||
if (sequence[i] >= value) { | ||
return sequence[i] === value; | ||
CronExpression.prototype._findSchedule = function _findSchedule () { | ||
/** | ||
* Match field value | ||
* | ||
* @param {String} value | ||
* @param {Array} sequence | ||
* @return {Boolean} | ||
* @private | ||
*/ | ||
function matchSchedule (value, sequence) { | ||
for (var i = 0, c = sequence.length; i < c; i++) { | ||
if (sequence[i] >= value) { | ||
return sequence[i] === value; | ||
} | ||
} | ||
} | ||
return sequence[0] === value; | ||
}; | ||
/** | ||
* Validate expression allowed characters | ||
* | ||
* @param {String} value Input expression | ||
* @returns {Boolean} | ||
* @private | ||
*/ | ||
CronExpression._validateCharacters = function _validateCharacters (value) { | ||
var regex = new RegExp('^[\\d|/|*|\\-|,]+$'); | ||
return regex.test(value); | ||
}; | ||
/** | ||
* Constraint validation | ||
* | ||
* @private | ||
* @static | ||
* @param {Object} value alue to check | ||
* @return {Boolean} True if validation succeeds, false if not | ||
*/ | ||
CronExpression._validateConstraint = function _validateConstraint (value, constraints) { | ||
if (value < constraints[0] || value > constraints[1]) { | ||
return false; | ||
return sequence[0] === value; | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Detect if input range fully matches constraint bounds | ||
* @param {Array} range Input range | ||
* @param {Array} constraints Input constraints | ||
* @returns {Boolean} | ||
* @private | ||
*/ | ||
function isWildcardRange (range, constraints) { | ||
if (range instanceof Array && !range.length) { | ||
return false; | ||
} | ||
/** | ||
* Timespan validation | ||
* | ||
* @private | ||
* @static | ||
* @param {Date} current Current date | ||
* @param {Date} end End date | ||
* @return {Boolean} Return true if timespan is still valid, otherwise return false | ||
*/ | ||
CronExpression._validateTimespan = function _validateTimespan (current, end) { | ||
if (end && (end.getTime() - current.getTime()) < 0) { | ||
return false; | ||
} | ||
if (constraints.length !== 2) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Find next matching schedule date | ||
* | ||
* @return {Date} | ||
* @private | ||
*/ | ||
CronExpression.prototype._findSchedule = function _findSchedule () { | ||
// Validate timespan | ||
if (!CronExpression._validateTimespan(this._currentDate, this._endDate)) { | ||
throw new Error('Out of the timespan range'); | ||
return range.length === (constraints[1] - (constraints[0] < 1 ? - 1 : 0)); | ||
} | ||
var current = new Date(this._currentDate.toUTCString()); | ||
var currentDate = new Date(this._currentDate); | ||
var endDate = this._endDate; | ||
// Reset | ||
if (this._fields.second.length === 1 && this._fields.second[0] === 0) { | ||
current.addMinute(); | ||
} else { | ||
current.addSecond(); | ||
// Append minute if second is 0 | ||
if (this._fields.second[0] === 0) { | ||
currentDate.addMinute(); | ||
} | ||
// Iterate and match schedule | ||
// Find matching schedule | ||
while (true) { | ||
// Validate timespan | ||
if (!CronExpression._validateTimespan(current, this._endDate)) { | ||
if (endDate && (endDate.getTime() - currentDate.getTime()) < 0) { | ||
throw new Error('Out of the timespan range'); | ||
} | ||
// Match month | ||
if (!CronExpression._matchSchedule(current.getMonth() + 1, this._fields.month)) { | ||
current.addMonth(); | ||
continue; | ||
} | ||
console.log(currentDate); | ||
@@ -423,10 +383,31 @@ // Day of month and week matching: | ||
var dayOfMonthMatch = CronExpression._matchSchedule(current.getDate(), this._fields.dayOfMonth); | ||
var dayOfWeekMatch = CronExpression._matchSchedule(current.getDay(), this._fields.dayOfWeek); | ||
var isDayOfMonthWildcardMatch = CronExpression._isWildcardRange(this._fields.dayOfMonth, CronExpression.constraints[3]); | ||
var isDayOfWeekWildcardMatch = CronExpression._isWildcardRange(this._fields.dayOfWeek, CronExpression.constraints[5]); | ||
var dayOfMonthMatch = matchSchedule(currentDate.getDate(), this._fields.dayOfMonth); | ||
var dayOfWeekMatch = matchSchedule(currentDate.getDay(), this._fields.dayOfWeek); | ||
var isDayOfMonthWildcardMatch = isWildcardRange(this._fields.dayOfMonth, CronExpression.constraints[3]); | ||
var isMonthWildcardMatch = isWildcardRange(this._fields.dayOfWeek, CronExpression.constraints[4]); | ||
var isDayOfWeekWildcardMatch = isWildcardRange(this._fields.dayOfWeek, CronExpression.constraints[5]); | ||
// Validate days in month if explicit value is given | ||
if (!isMonthWildcardMatch) { | ||
var currentYear = currentDate.getYear(); | ||
var currentMonth = currentDate.getMonth() + 1; | ||
var previousMonth = currentMonth === 1 ? 11 : currentMonth - 1; | ||
var daysInPreviousMonth = CronExpression.daysInMonth[previousMonth - 1]; | ||
var daysOfMontRangeMax = this._fields.dayOfMonth[this._fields.dayOfMonth.length - 1]; | ||
// Handle leap year | ||
var isLeap = !((currentYear % 4) || (!(currentYear % 100) && (currentYear % 400))); | ||
if (isLeap) { | ||
daysInPreviousMonth = 29; | ||
} | ||
if (this._fields.month[0] === previousMonth && daysInPreviousMonth < daysOfMontRangeMax) { | ||
throw new Error('Invalid explicit day of month definition'); | ||
} | ||
} | ||
// Add day if not day of month is set (and no match) and day of week is wildcard | ||
if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !dayOfMonthMatch) { | ||
current.addDay(); | ||
currentDate.addDay(); | ||
continue; | ||
@@ -437,3 +418,3 @@ } | ||
if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !dayOfWeekMatch) { | ||
current.addDay(); | ||
currentDate.addDay(); | ||
continue; | ||
@@ -445,9 +426,15 @@ } | ||
!dayOfMonthMatch && !dayOfWeekMatch) { | ||
current.addDay(); | ||
currentDate.addDay(); | ||
continue; | ||
} | ||
// Match month | ||
if (!matchSchedule(currentDate.getMonth() + 1, this._fields.month)) { | ||
currentDate.addMonth(); | ||
continue; | ||
} | ||
// Match hour | ||
if (!CronExpression._matchSchedule(current.getHours(), this._fields.hour)) { | ||
current.addHour(); | ||
if (!matchSchedule(currentDate.getHours(), this._fields.hour)) { | ||
currentDate.addHour(); | ||
continue; | ||
@@ -457,4 +444,4 @@ } | ||
// Match minute | ||
if (!CronExpression._matchSchedule(current.getMinutes(), this._fields.minute)) { | ||
current.addMinute(); | ||
if (!matchSchedule(currentDate.getMinutes(), this._fields.minute)) { | ||
currentDate.addMinute(); | ||
continue; | ||
@@ -464,11 +451,10 @@ } | ||
// Match second | ||
if (!CronExpression._matchSchedule(current.getSeconds(), this._fields.second)) { | ||
current.addSecond(); | ||
if (!matchSchedule(currentDate.getSeconds(), this._fields.second)) { | ||
currentDate.addSecond(); | ||
continue; | ||
} | ||
break; | ||
} | ||
return (this._currentDate = current); | ||
return (this._currentDate = currentDate); | ||
}; | ||
@@ -539,3 +525,3 @@ | ||
CronExpression.prototype.reset = function reset () { | ||
this._currentDate = new Date(this._options.currentDate.toUTCString()); | ||
this._currentDate = new Date(this._options.currentDate); | ||
}; | ||
@@ -542,0 +528,0 @@ |
{ | ||
"name": "cron-parser", | ||
"version": "0.4.5", | ||
"version": "0.5.0", | ||
"description": "Node.js library for parsing crontab instructions", | ||
@@ -5,0 +5,0 @@ "main": "lib/parser.js", |
16
test.js
var CronExpression = require('./lib/expression'); | ||
CronExpression.parse('0 0 0 1 1 *', function(err, interval) { | ||
var count = 5; | ||
while (count) { | ||
try { | ||
console.log(interval.next()); | ||
count--; | ||
} catch (e) { | ||
break; | ||
} | ||
} | ||
}); | ||
var nextTime = CronExpression.parse("0 4 29 3 *").next(); | ||
console.log(nextTime); | ||
//var nextTime = CronExpression.parse("@yearly").next(); | ||
//console.log(nextTime) |
@@ -531,1 +531,17 @@ var util = require('util'); | ||
}); | ||
test('day of month value can\'t be larger than days in month maximum value if it\'s defined explicitly', function(t) { | ||
CronExpression.parse('0 4 29 2 *', function(err, interval) { | ||
t.ifError(err, 'Interval parse error'); | ||
t.ok(interval, 'Interval parsed'); | ||
try { | ||
interval.next(); | ||
t.ok(false, 'Should fail'); | ||
} catch (e) { | ||
t.ok(true, 'Failed as expected'); | ||
} | ||
t.end(); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
62220