cron-parser
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -5,3 +5,3 @@ { | ||
"description": "Node.js library for parsing crontab instructions", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"keywords": ["cron", "crontab", "parser"], | ||
@@ -8,0 +8,0 @@ "dependencies": {}, |
@@ -214,2 +214,13 @@ 'use strict'; | ||
/** | ||
* Returns true when the current weekday is the last occurrence of this weekday | ||
* for the present month. | ||
*/ | ||
CronDate.prototype.isLastWeekdayOfMonth = function() { | ||
// Check this by adding 7 days to the current date and seeing if it's | ||
// a different month | ||
var newDate = this._date.plus({ days: 7 }).startOf('day'); | ||
return this._date.month !== newDate.month; | ||
}; | ||
function CronDate (timestamp, tz) { | ||
@@ -216,0 +227,0 @@ var dateOpts = { zone: tz }; |
@@ -66,3 +66,3 @@ 'use strict'; | ||
{ min: 1, max: 12, chars: [] }, // Month | ||
{ min: 0, max: 7, chars: [] }, // Day of week | ||
{ min: 0, max: 7, chars: ['L'] }, // Day of week | ||
]; | ||
@@ -127,3 +127,3 @@ | ||
CronExpression.standardValidCharacters = /^[\d|/|*|\-|,]+$/; | ||
CronExpression.dayOfWeekValidCharacters = /^[\d|/|*|\-|,|\?]+$/; | ||
CronExpression.dayOfWeekValidCharacters = /^[\d|\dL|/|*|\-|,|\?]+$/; | ||
CronExpression.dayOfMonthValidCharacters = /^[\d|L|/|*|\-|,|\?]+$/; | ||
@@ -139,2 +139,12 @@ CronExpression.validCharacters = { | ||
CronExpression._isValidConstraintChar = function _isValidConstraintChar(constraints, value) { | ||
if (typeof value !== 'string') { | ||
return false; | ||
} | ||
return constraints.chars.some(function(char) { | ||
return value.indexOf(char) > -1; | ||
}); | ||
}; | ||
/** | ||
@@ -156,3 +166,3 @@ * Parse input interval | ||
value = value.replace(/[a-z]{1,3}/gi, function(match) { | ||
value = value.replace(/[a-z]{3}/gi, function(match) { | ||
match = match.toLowerCase(); | ||
@@ -204,3 +214,3 @@ | ||
if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) { | ||
if (CronExpression._isValidConstraintChar(constraints, value)) { | ||
stack.push(value); | ||
@@ -221,3 +231,3 @@ continue; | ||
if (typeof result === 'string' && constraints.chars.indexOf(result) > -1) { | ||
if (CronExpression._isValidConstraintChar(constraints, result)) { | ||
stack.push(result); | ||
@@ -493,8 +503,11 @@ return; | ||
* | ||
* @param {Array} dayOfMonth | ||
* @param {Array} expressions | ||
*/ | ||
function isLInDayOfMonth(dayOfMonth) { | ||
return dayOfMonth.length > 0 && dayOfMonth.indexOf('L') >= 0; | ||
function isLInExpressions(expressions) { | ||
return expressions.length > 0 && expressions.some(function(expression) { | ||
return typeof expression === 'string' && expression.indexOf('L') >= 0; | ||
}); | ||
} | ||
// Whether to use backwards directionality when searching | ||
@@ -512,2 +525,21 @@ reverse = reverse || false; | ||
function isLastWeekdayOfMonthMatch(expressions) { | ||
return expressions.some(function(expression) { | ||
// There might be multiple expressions and not all of them will contain | ||
// the "L". | ||
if (!isLInExpressions([expression])) { | ||
return false; | ||
} | ||
// The first character represents the weekday | ||
var weekday = Number.parseInt(expression[0]); | ||
if (Number.isNaN(weekday)) { | ||
throw new Error('Invalid last weekday of the month expression: ' + expression); | ||
} | ||
return currentDate.getDay() === weekday && currentDate.isLastWeekdayOfMonth(); | ||
}); | ||
} | ||
while (stepCount < LOOP_LIMIT) { | ||
@@ -539,6 +571,9 @@ stepCount++; | ||
var dayOfMonthMatch = matchSchedule(currentDate.getDate(), this.fields.dayOfMonth); | ||
if (isLInDayOfMonth(this.fields.dayOfMonth)) { | ||
if (isLInExpressions(this.fields.dayOfMonth)) { | ||
dayOfMonthMatch = dayOfMonthMatch || currentDate.isLastDayOfMonth(); | ||
} | ||
var dayOfWeekMatch = matchSchedule(currentDate.getDay(), this.fields.dayOfWeek); | ||
if (isLInExpressions(this.fields.dayOfWeek)) { | ||
dayOfWeekMatch = dayOfWeekMatch || isLastWeekdayOfMonthMatch(this.fields.dayOfWeek); | ||
} | ||
var isDayOfMonthWildcardMatch = this.fields.dayOfMonth.length >= CronExpression.daysInMonth[currentDate.getMonth()]; | ||
@@ -915,5 +950,6 @@ var isDayOfWeekWildcardMatch = this.fields.dayOfWeek.length === CronExpression.constraints[5].max - CronExpression.constraints[5].min + 1; | ||
if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) { | ||
if (CronExpression._isValidConstraintChar(constraints, value)) { | ||
continue; | ||
} | ||
// Check constraints | ||
@@ -920,0 +956,0 @@ if (typeof value !== 'number' || Number.isNaN(value) || value < constraints.min || value > constraints.max) { |
{ | ||
"name": "cron-parser", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "Node.js library for parsing crontab instructions", | ||
@@ -5,0 +5,0 @@ "main": "lib/parser.js", |
@@ -26,3 +26,3 @@ cron-parser | ||
│ │ │ │ │ | | ||
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun) | ||
│ │ │ │ │ └ day of week (0 - 7, 1L - 7L) (0 or 7 is Sun) | ||
│ │ │ │ └───── month (1 - 12) | ||
@@ -35,3 +35,3 @@ │ │ │ └────────── day of month (1 - 31, L) | ||
Supports mixed use of ranges and range increments (L and W characters are not supported currently). See tests for examples. | ||
Supports mixed use of ranges and range increments (W character not supported currently). See tests for examples. | ||
@@ -151,1 +151,23 @@ Usage | ||
* *tz* - Timezone string. It won't be used in case `utc` is enabled | ||
Last weekday of the month | ||
========================= | ||
This library supports parsing the range `0L - 7L` in the `weekday` position of | ||
the cron expression, where the `L` means "last occurrence of this weekday for | ||
the month in progress". | ||
For example, the following expression will run on the last monday of the month | ||
at midnight: | ||
``` | ||
0 0 * * * 1L | ||
``` | ||
The library also supports combining `L` expressions with other weekday | ||
expressions. For example, the following cron will run every Monday as well | ||
as the last Wednesday of the month: | ||
``` | ||
0 0 * * * 1,3L | ||
``` |
@@ -1,2 +0,1 @@ | ||
var util = require('util'); | ||
var test = require('tap').test; | ||
@@ -19,3 +18,2 @@ var CronParser = require('../lib/parser'); | ||
} | ||
} catch (err) { | ||
@@ -48,3 +46,2 @@ t.error(err, 'Parse read error'); | ||
t.equal(i, items); | ||
} catch (err) { | ||
@@ -73,3 +70,57 @@ t.error(err, 'Parse read error'); | ||
t.equal(next.getDate(), 28); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
test('parse cron with last weekday of the month', function(t) { | ||
var options = { | ||
currentDate: new Date(2021, 8, 1), | ||
endDate: new Date(2021, 11, 1) | ||
}; | ||
var testCases = [ | ||
{ expression: '0 0 0 * * 1L', expectedDate: 27 }, | ||
{ expression: '0 0 0 * * 2L', expectedDate: 28 }, | ||
{ expression: '0 0 0 * * 3L', expectedDate: 29 }, | ||
{ expression: '0 0 0 * * 4L', expectedDate: 30 }, | ||
{ expression: '0 0 0 * * 5L', expectedDate: 24 }, | ||
{ expression: '0 0 0 * * 6L', expectedDate: 25 }, | ||
{ expression: '0 0 0 * * 0L', expectedDate: 26 } | ||
]; | ||
testCases.forEach(function({ expression, expectedDate }) { | ||
t.test(expression, function(t) { | ||
try { | ||
var interval = CronParser.parseExpression(expression, options); | ||
t.equal(interval.hasNext(), true); | ||
var next = interval.next(); | ||
t.equal(next.getDate(), expectedDate); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
}); | ||
t.end(); | ||
}); | ||
test('parses expression that runs on both last monday and friday of the month', function(t) { | ||
var options = { | ||
currentDate: new Date(2021, 8, 1), | ||
endDate: new Date(2021, 11, 1) | ||
}; | ||
try { | ||
var interval = CronParser.parseExpression('0 0 0 * * 1L,5L', options); | ||
t.equal(interval.next().getDate(), 24); | ||
t.equal(interval.next().getDate(), 27); | ||
} catch (err) { | ||
@@ -82,1 +133,40 @@ t.error(err, 'Parse read error'); | ||
test('parses expression that runs on both every monday and last friday of mont', function(t) { | ||
var options = { | ||
currentDate: new Date(2021, 8, 1), | ||
endDate: new Date(2021, 8, 30) | ||
}; | ||
try { | ||
var interval = CronParser.parseExpression('0 0 0 * * 1,5L', options); | ||
var dates = []; | ||
while(true) { | ||
try { | ||
dates.push(interval.next().getDate()); | ||
} catch (e) { | ||
if (e.message !== 'Out of the timespan range') { | ||
throw e; | ||
} | ||
break; | ||
} | ||
} | ||
t.same(dates, [6, 13, 20, 24, 27]); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
test('fails to parse for invalid last weekday of month expression', function(t) { | ||
t.throws(function() { | ||
var interval = CronParser.parseExpression('0 0 0 * * L'); | ||
interval.next(); | ||
}); | ||
t.end(); | ||
}); |
@@ -210,2 +210,53 @@ 'use strict'; | ||
test('stringify cron expression with weekday L', function (t) { | ||
try { | ||
var expected = '0 0 0 * * 1L'; | ||
var interval = CronParser.parseExpression(expected, {}); | ||
var str = interval.stringify(true); | ||
t.equal(str, expected); | ||
str = CronParser.fieldsToExpression(interval.fields).stringify(true); | ||
t.equal(str, expected); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
test('stringify cron expression with multiple weekday, one of them with an L', function (t) { | ||
try { | ||
var expected = '0 0 0 * * 4,6L'; | ||
var interval = CronParser.parseExpression(expected, {}); | ||
var str = interval.stringify(true); | ||
t.equal(str, expected); | ||
str = CronParser.fieldsToExpression(interval.fields).stringify(true); | ||
t.equal(str, expected); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
test('stringify cron expression with multiple weekday, two of them with an L', function (t) { | ||
try { | ||
var expected = '0 0 0 * * 1L,5L'; | ||
var interval = CronParser.parseExpression(expected, {}); | ||
var str = interval.stringify(true); | ||
t.equal(str, expected); | ||
str = CronParser.fieldsToExpression(interval.fields).stringify(true); | ||
t.equal(str, expected); | ||
} catch (err) { | ||
t.error(err, 'Parse read error'); | ||
} | ||
t.end(); | ||
}); | ||
test('stringify from fields out of order', function (t) { | ||
@@ -212,0 +263,0 @@ |
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
145558
34
4151
171