Socket
Socket
Sign inDemoInstall

cron

Package Overview
Dependencies
1
Maintainers
2
Versions
66
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.8.2 to 1.8.3

.editorconfig

2

examples/basic.js
const CronJob = require('../lib/cron.js').CronJob;
console.log('Before job instantiation');
const job = new CronJob('* * 7-23 * * *', function() {
const job = new CronJob('* * * * * *', function() {
const d = new Date();

@@ -6,0 +6,0 @@ console.log('Every second:', d);

@@ -1,703 +0,23 @@

(function(root, factory) {
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['moment-timezone'], factory);
define(['luxon'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(
require('moment-timezone'),
require('child_process')
);
module.exports = factory(require('luxon'), require('child_process'));
} else {
root.Cron = factory(root.moment);
root.Cron = factory(root.luxon);
}
})(this, function(moment, childProcess) {
})(this, function (luxon, childProcess) {
var exports = {};
var timeUnits = [
'second',
'minute',
'hour',
'dayOfMonth',
'month',
'dayOfWeek'
];
var spawn = childProcess && childProcess.spawn;
const CronTime = require('./time')(luxon);
const CronJob = require('./job')(CronTime, spawn);
function CronTime(source, zone, utcOffset) {
this.source = source;
if (zone) {
if (moment.tz.names().indexOf(zone) === -1) {
throw new Error('Invalid timezone.');
}
this.zone = zone;
}
if (typeof utcOffset !== 'undefined') this.utcOffset = utcOffset;
var that = this;
timeUnits.map(function(timeUnit) {
that[timeUnit] = {};
});
if (this.source instanceof Date || this.source._isAMomentObject) {
this.source = moment(this.source);
this.realDate = true;
} else {
this._parse();
this._verifyParse();
}
}
CronTime.constraints = [[0, 59], [0, 59], [0, 23], [1, 31], [0, 11], [0, 6]];
CronTime.monthConstraints = [
31,
29, // support leap year...not perfect
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
];
CronTime.parseDefaults = ['0', '*', '*', '*', '*', '*'];
CronTime.aliases = {
jan: 0,
feb: 1,
mar: 2,
apr: 3,
may: 4,
jun: 5,
jul: 6,
aug: 7,
sep: 8,
oct: 9,
nov: 10,
dec: 11,
sun: 0,
mon: 1,
tue: 2,
wed: 3,
thu: 4,
fri: 5,
sat: 6
};
CronTime.prototype = {
_verifyParse: function() {
var months = Object.keys(this.month);
var ok = false;
/* if a dayOfMonth is not found in all months, we only need to fix the last
wrong month to prevent infinite loop */
var lastWrongMonth = NaN;
for (var i = 0; i < months.length; i++) {
var m = months[i];
var con = CronTime.monthConstraints[parseInt(m, 10)];
var dsom = Object.keys(this.dayOfMonth);
for (var j = 0; j < dsom.length; j++) {
var dom = dsom[j];
if (dom <= con) {
ok = true;
}
}
if (!ok) {
// save the month in order to be fixed if all months fails (infinite loop)
lastWrongMonth = m;
console.warn("Month '" + m + "' is limited to '" + con + "' days.");
}
}
// infinite loop detected (dayOfMonth is not found in all months)
if (!ok) {
var con = CronTime.monthConstraints[parseInt(lastWrongMonth, 10)];
var dsom = Object.keys(this.dayOfMonth);
for (var k = 0; k < dsom.length; k++) {
var dom = dsom[k];
if (dom > con) {
delete this.dayOfMonth[dom];
var fixedDay = Number(dom) % con;
this.dayOfMonth[fixedDay] = true;
}
}
}
},
/**
* calculates the next send time
*/
sendAt: function(i) {
var date = this.realDate ? this.source : moment();
// Set the timezone if given (http://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/)
if (this.zone) {
date = date.tz(this.zone);
}
if (typeof this.utcOffset !== 'undefined') {
date = date.utcOffset(this.utcOffset);
}
if (this.realDate) {
const diff = moment().diff(date, 's');
if (diff > 0) {
throw new Error('WARNING: Date in past. Will never be fired.');
}
return date;
}
// If the i argument is not given, return the next send time
if (isNaN(i) || i < 0) {
date = this._getNextDateFrom(date);
return date;
} else {
// Else return the next i send times
var dates = [];
for (; i > 0; i--) {
date = this._getNextDateFrom(date);
dates.push(moment(date));
}
return dates;
}
},
/**
* Get the number of milliseconds in the future at which to fire our callbacks.
*/
getTimeout: function() {
return Math.max(-1, this.sendAt() - moment());
},
/**
* writes out a cron string
*/
toString: function() {
return this.toJSON().join(' ');
},
/**
* Json representation of the parsed cron syntax.
*/
toJSON: function() {
var self = this;
return timeUnits.map(function(timeName) {
return self._wcOrAll(timeName);
});
},
/**
* get next date that matches parsed cron time
*/
_getNextDateFrom: function(start, zone) {
var date;
var firstDate = moment(start).valueOf();
if (zone) {
date = moment(start).tz(zone);
} else {
date = moment(start);
}
if (!this.realDate) {
const milliseconds =
(start.milliseconds && start.milliseconds()) ||
(start.getMilliseconds && start.getMilliseconds()) ||
0;
if (milliseconds > 0) {
date.milliseconds(0);
date.seconds(date.seconds() + 1);
}
}
if (date.toString() === 'Invalid date') {
throw new Error('ERROR: You specified an invalid date.');
}
// it shouldn't take more than 5 seconds to find the next execution time
// being very generous with this. Throw error if it takes too long to find the next time to protect from
// infinite loop.
var timeout = Date.now() + 5000;
// determine next date
while (true) {
var diff = date - start;
var prevMonth = date.month();
var prevDay = date.days();
var prevMinute = date.minutes();
var prevSeconds = date.seconds();
var origDate = new Date(date);
if (Date.now() > timeout) {
throw new Error(
`Something went wrong. cron reached maximum iterations.
Please open an issue (https://github.com/kelektiv/node-cron/issues/new) and provide the following string
Time Zone: ${zone || '""'} - Cron String: ${this} - UTC offset: ${date.format(
'Z'
)} - current Date: ${moment().toString()}`
);
}
if (
!(date.month() in this.month) &&
Object.keys(this.month).length !== 12
) {
date.add(1, 'M');
if (date.month() === prevMonth) {
date.add(1, 'M');
}
date.date(1);
date.hours(0);
date.minutes(0);
date.seconds(0);
continue;
}
if (
!(date.date() in this.dayOfMonth) &&
Object.keys(this.dayOfMonth).length !== 31 &&
!(
date.day() in this.dayOfWeek &&
Object.keys(this.dayOfWeek).length !== 7
)
) {
date.add(1, 'd');
if (date.days() === prevDay) {
date.add(1, 'd');
}
date.hours(0);
date.minutes(0);
date.seconds(0);
continue;
}
if (
!(date.day() in this.dayOfWeek) &&
Object.keys(this.dayOfWeek).length !== 7 &&
!(
date.date() in this.dayOfMonth &&
Object.keys(this.dayOfMonth).length !== 31
)
) {
date.add(1, 'd');
if (date.days() === prevDay) {
date.add(1, 'd');
}
date.hours(0);
date.minutes(0);
date.seconds(0);
if (date <= origDate) {
date = this._findDST(origDate);
}
continue;
}
if (
!(date.hours() in this.hour) &&
Object.keys(this.hour).length !== 24
) {
origDate = moment(date);
var curHour = date.hours();
date.hours(
date.hours() === 23 && diff > 86400000 ? 0 : date.hours() + 1
);
/*
* Moment Date will not allow you to set the time to 2 AM if there is no 2 AM (on the day we change the clock)
* We will therefore jump to 3AM if time stayed at 1AM
*/
if (curHour === date.hours()) {
date.hours(date.hours() + 2);
}
date.minutes(0);
date.seconds(0);
if (date <= origDate) {
date = this._findDST(origDate);
}
continue;
}
if (
!(date.minutes() in this.minute) &&
Object.keys(this.minute).length !== 60
) {
origDate = moment(date);
date.minutes(
date.minutes() === 59 && diff > 60 * 60 * 1000
? 0
: date.minutes() + 1
);
date.seconds(0);
if (date <= origDate) {
date = this._findDST(origDate);
}
continue;
}
if (
!(date.seconds() in this.second) &&
Object.keys(this.second).length !== 60
) {
origDate = moment(date);
date.seconds(
date.seconds() === 59 && diff > 60 * 1000 ? 0 : date.seconds() + 1
);
if (date <= origDate) {
date = this._findDST(origDate);
}
continue;
}
if (date.valueOf() === firstDate) {
date.seconds(date.seconds() + 1);
continue;
}
break;
}
return date;
},
/**
* get next date that is a valid DST date
*/
_findDST: function(date) {
var newDate = moment(date);
while (newDate <= date) {
// eslint seems to trigger here, it is wrong
newDate.add(1, 's');
}
return newDate;
},
/**
* wildcard, or all params in array (for to string)
*/
_wcOrAll: function(type) {
if (this._hasAll(type)) return '*';
var all = [];
for (var time in this[type]) {
all.push(time);
}
return all.join(',');
},
_hasAll: function(type) {
var constrain = CronTime.constraints[timeUnits.indexOf(type)];
for (var i = constrain[0], n = constrain[1]; i < n; i++) {
if (!(i in this[type])) return false;
}
return true;
},
_parse: function() {
var aliases = CronTime.aliases;
var source = this.source.replace(/[a-z]{1,3}/gi, function(alias) {
alias = alias.toLowerCase();
if (alias in aliases) {
return aliases[alias];
}
throw new Error('Unknown alias: ' + alias);
});
var split = source.replace(/^\s\s*|\s\s*$/g, '').split(/\s+/);
var cur;
var i = 0;
var len = timeUnits.length;
// seconds are optional
if (split.length < timeUnits.length - 1) {
throw new Error('Too few fields');
}
if (split.length > timeUnits.length) {
throw new Error('Too many fields');
}
for (; i < timeUnits.length; i++) {
// If the split source string doesn't contain all digits,
// assume defaults for first n missing digits.
// This adds support for 5-digit standard cron syntax
cur = split[i - (len - split.length)] || CronTime.parseDefaults[i];
this._parseField(cur, timeUnits[i], CronTime.constraints[i]);
}
},
_parseField: function(field, type, constraints) {
var rangePattern = /^(\d+)(?:-(\d+))?(?:\/(\d+))?$/g;
var typeObj = this[type];
var pointer;
var low = constraints[0];
var high = constraints[1];
var fields = field.split(',');
fields.forEach(function(field) {
var wildcardIndex = field.indexOf('*');
if (wildcardIndex !== -1 && wildcardIndex !== 0) {
throw new Error('Field (' + field + ') has an invalid wildcard expression');
}
});
// * is a shortcut to [lower-upper] range
field = field.replace(/\*/g, low + '-' + high);
// commas separate information, so split based on those
var allRanges = field.split(',');
for (var i = 0; i < allRanges.length; i++) {
if (allRanges[i].match(rangePattern)) {
allRanges[i].replace(rangePattern, function($0, lower, upper, step) {
lower = parseInt(lower, 10);
upper = parseInt(upper, 10) || undefined;
const wasStepDefined = !isNaN(parseInt(step, 10));
if (step === '0') {
throw new Error('Field (' + field + ') has a step of zero');
}
step = parseInt(step, 10) || 1;
if (upper && lower > upper) {
throw new Error('Field (' + field + ') has an invalid range');
}
const outOfRangeError =
lower < low ||
(upper && upper > high) ||
(!upper && lower > high);
if (outOfRangeError) {
throw new Error('Field (' + field + ') value is out of range');
}
// Positive integer higher than constraints[0]
lower = Math.min(Math.max(low, ~~Math.abs(lower)), high);
// Positive integer lower than constraints[1]
if (upper) {
upper = Math.min(high, ~~Math.abs(upper));
} else {
// If step is provided, the default upper range is the highest value
upper = wasStepDefined ? high : lower;
}
// Count from the lower barrier to the upper
pointer = lower;
do {
typeObj[pointer] = true;
pointer += step;
} while (pointer <= upper);
});
} else {
throw new Error('Field (' + field + ') cannot be parsed');
}
}
}
};
function command2function(cmd) {
var command;
var args;
switch (typeof cmd) {
case 'string':
args = cmd.split(' ');
command = args.shift();
cmd = spawn.bind(undefined, command, args);
break;
case 'object':
command = cmd && cmd.command;
if (command) {
args = cmd.args;
var options = cmd.options;
cmd = spawn.bind(undefined, command, args, options);
}
break;
}
return cmd;
}
function CronJob(
cronTime,
onTick,
onComplete,
startNow,
timeZone,
context,
runOnInit,
utcOffset,
unrefTimeout
) {
var _cronTime = cronTime;
var argCount = 0;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] !== undefined) {
argCount++;
}
}
if (typeof cronTime !== 'string' && argCount === 1) {
// crontime is an object...
onTick = cronTime.onTick;
onComplete = cronTime.onComplete;
context = cronTime.context;
startNow = cronTime.start || cronTime.startNow || cronTime.startJob;
timeZone = cronTime.timeZone;
runOnInit = cronTime.runOnInit;
_cronTime = cronTime.cronTime;
utcOffset = cronTime.utcOffset;
unrefTimeout = cronTime.unrefTimeout;
}
this.context = context || this;
this._callbacks = [];
this.onComplete = command2function(onComplete);
this.cronTime = new CronTime(_cronTime, timeZone, utcOffset);
this.unrefTimeout = unrefTimeout;
addCallback.call(this, command2function(onTick));
if (runOnInit) {
this.lastExecution = new Date();
fireOnTick.call(this);
}
if (startNow) {
start.call(this);
}
return this;
}
var addCallback = function(callback) {
if (typeof callback === 'function') this._callbacks.push(callback);
};
CronJob.prototype.addCallback = addCallback;
CronJob.prototype.setTime = function(time) {
if (!(time instanceof CronTime))
throw new Error('time must be an instance of CronTime.');
this.stop();
this.cronTime = time;
};
CronJob.prototype.nextDate = function() {
return this.cronTime.sendAt();
};
var fireOnTick = function() {
for (var i = this._callbacks.length - 1; i >= 0; i--)
this._callbacks[i].call(this.context, this.onComplete);
};
CronJob.prototype.fireOnTick = fireOnTick;
CronJob.prototype.nextDates = function(i) {
return this.cronTime.sendAt(i);
};
var start = function() {
if (this.running) return;
var MAXDELAY = 2147483647; // The maximum number of milliseconds setTimeout will wait.
var self = this;
var timeout = this.cronTime.getTimeout();
var remaining = 0;
var startTime;
if (this.cronTime.realDate) this.runOnce = true;
function _setTimeout(timeout) {
startTime = Date.now();
self._timeout = setTimeout(callbackWrapper, timeout);
if (self.unrefTimeout && typeof self._timeout.unref === 'function') {
self._timeout.unref();
}
}
// The callback wrapper checks if it needs to sleep another period or not
// and does the real callback logic when it's time.
function callbackWrapper() {
var diff = startTime + timeout - Date.now();
if (diff > 0) {
var newTimeout = self.cronTime.getTimeout();
if (newTimeout > diff) {
newTimeout = diff;
}
remaining += newTimeout;
}
// If there is sleep time remaining, calculate how long and go to sleep
// again. This processing might make us miss the deadline by a few ms
// times the number of sleep sessions. Given a MAXDELAY of almost a
// month, this should be no issue.
self.lastExecution = new Date();
if (remaining) {
if (remaining > MAXDELAY) {
remaining -= MAXDELAY;
timeout = MAXDELAY;
} else {
timeout = remaining;
remaining = 0;
}
_setTimeout(timeout);
} else {
// We have arrived at the correct point in time.
self.running = false;
// start before calling back so the callbacks have the ability to stop the cron job
if (!self.runOnce) self.start();
self.fireOnTick();
}
}
if (timeout >= 0) {
this.running = true;
// Don't try to sleep more than MAXDELAY ms at a time.
if (timeout > MAXDELAY) {
remaining = timeout - MAXDELAY;
timeout = MAXDELAY;
}
_setTimeout(timeout);
} else {
this.stop();
}
};
CronJob.prototype.start = start;
CronJob.prototype.lastDate = function() {
return this.lastExecution;
};
/**
* Stop the cronjob.
* Extend Luxon DateTime
*/
CronJob.prototype.stop = function() {
if (this._timeout) clearTimeout(this._timeout);
this.running = false;
if (typeof this.onComplete === 'function') this.onComplete();
luxon.DateTime.prototype.getWeekDay = function () {
return this.weekday === 7 ? 0 : this.weekday;
};
exports.job = function(
exports.job = (
cronTime,

@@ -712,4 +32,4 @@ onTick,

unrefTimeout
) {
return new CronJob(
) =>
new CronJob(
cronTime,

@@ -725,15 +45,8 @@ onTick,

);
};
exports.time = function(cronTime, timeZone) {
return new CronTime(cronTime, timeZone);
};
exports.time = (cronTime, timeZone) => new CronTime(cronTime, timeZone);
exports.sendAt = function(cronTime) {
return exports.time(cronTime).sendAt();
};
exports.sendAt = cronTime => exports.time(cronTime).sendAt();
exports.timeout = function(cronTime) {
return exports.time(cronTime).getTimeout();
};
exports.timeout = cronTime => exports.time(cronTime).getTimeout();

@@ -740,0 +53,0 @@ exports.CronJob = CronJob;

{
"name": "cron",
"description": "Cron jobs for your node",
"version": "1.8.2",
"author": "Nick Campbell <nicholas.j.campbell@gmail.com> (http://github.com/ncb000gt)",
"bugs": {
"url": "http://github.com/kelektiv/node-cron/issues"
},
"repository": {
"type": "git",
"url": "http://github.com/kelektiv/node-cron.git"
},
"main": "lib/cron",
"scripts": {
"test": "jest"
},
"dependencies": {
"moment-timezone": "^0.5.x"
},
"devDependencies": {
"chai": "~4.2.x",
"eslint": "~6.3.x",
"eslint-config-prettier": "~6.2.x",
"eslint-config-standard": "~14.1.x",
"eslint-plugin-import": "~2.18.x",
"eslint-plugin-jest": "^22.17.0",
"eslint-plugin-node": "~10.0.x",
"eslint-plugin-prettier": "~3.1.x",
"eslint-plugin-promise": "~4.2.x",
"eslint-plugin-standard": "~4.0.x",
"jest": "^24.9.0",
"prettier": "~1.18.x",
"sinon": "~7.4.x"
},
"keywords": [
"cron",
"node cron",
"node-cron",
"schedule",
"scheduler",
"cronjob",
"cron job"
],
"license": "MIT",
"contributors": [
"Romain Beauxis <toots@rastageeks.org> (https://github.com/toots)",
"James Padolsey <> (https://github.com/jamespadolsey)",
"Finn Herpich <fh@three-heads.de> (https://github.com/ErrorProne)",
"Clifton Cunningham <clifton.cunningham@gmail.com> (https://github.com/cliftonc)",
"Eric Abouaf <eric.abouaf@gmail.com> (https://github.com/neyric)",
"humanchimp <morphcham@gmail.com> (https://github.com/humanchimp)",
"Craig Condon <craig@spiceapps.com> (https://github.com/spiceapps)",
"Dan Bear <daniel@hulu.com> (https://github.com/danhbear)",
"Vadim Baryshev <vadimbaryshev@gmail.com> (https://github.com/baryshev)",
"Leandro Ferrari <lfthomaz@gmail.com> (https://github.com/lfthomaz)",
"Gregg Zigler <greggzigler@gmail.com> (https://github.com/greggzigler)",
"Jordan Abderrachid <jabderrachid@gmail.com> (https://github.com/jordanabderrachid)",
"Masakazu Matsushita <matsukaz@gmail.com> (matsukaz)",
"Christopher Lunt <me@kirisu.co.uk> (https://github.com/kirisu)"
]
"name": "cron",
"description": "Cron jobs for your node",
"version": "1.8.3",
"author": "Nick Campbell <nicholas.j.campbell@gmail.com> (http://github.com/ncb000gt)",
"bugs": {
"url": "http://github.com/kelektiv/node-cron/issues"
},
"repository": {
"type": "git",
"url": "http://github.com/kelektiv/node-cron.git"
},
"main": "lib/cron",
"scripts": {
"lint": "eslint {lib,tests}/*.js",
"test": "jest --coverage"
},
"dependencies": {
"luxon": "^1.23.x"
},
"devDependencies": {
"chai": "~4.2.x",
"eslint": "~6.8.x",
"eslint-config-prettier": "^6.11.x",
"eslint-config-standard": "~14.1.x",
"eslint-module-utils": "~2.6.x",
"eslint-plugin-import": "~2.20.x",
"eslint-plugin-jest": "~23.8.x",
"eslint-plugin-node": "~11.1.x",
"eslint-plugin-prettier": "~3.1.x",
"eslint-plugin-promise": "~4.2.x",
"eslint-plugin-standard": "~4.0.x",
"jest": "~28.0.x",
"prettier": "~2.0.x",
"sinon": "^9.0.x"
},
"keywords": [
"cron",
"node cron",
"node-cron",
"schedule",
"scheduler",
"cronjob",
"cron job"
],
"license": "MIT",
"contributors": [
"Romain Beauxis <toots@rastageeks.org> (https://github.com/toots)",
"James Padolsey <> (https://github.com/jamespadolsey)",
"Finn Herpich <fh@three-heads.de> (https://github.com/ErrorProne)",
"Clifton Cunningham <clifton.cunningham@gmail.com> (https://github.com/cliftonc)",
"Eric Abouaf <eric.abouaf@gmail.com> (https://github.com/neyric)",
"humanchimp <morphcham@gmail.com> (https://github.com/humanchimp)",
"Craig Condon <craig@spiceapps.com> (https://github.com/spiceapps)",
"Dan Bear <daniel@hulu.com> (https://github.com/danhbear)",
"Vadim Baryshev <vadimbaryshev@gmail.com> (https://github.com/baryshev)",
"Leandro Ferrari <lfthomaz@gmail.com> (https://github.com/lfthomaz)",
"Gregg Zigler <greggzigler@gmail.com> (https://github.com/greggzigler)",
"Jordan Abderrachid <jabderrachid@gmail.com> (https://github.com/jordanabderrachid)",
"Masakazu Matsushita <matsukaz@gmail.com> (matsukaz)",
"Christopher Lunt <me@kirisu.co.uk> (https://github.com/kirisu)"
],
"jest": {
"collectCoverage": true,
"collectCoverageFrom": [
"lib/*.js"
],
"coverageThreshold": {
"global": {
"statements": 80,
"branches": 80,
"functions": 70,
"lines": 80
}
}
}
}

@@ -1,7 +0,18 @@

node-cron
=
# Looking for maintainers/contributors
[![Build Status](https://travis-ci.org/kelektiv/node-cron.svg?branch=master)](https://travis-ci.org/kelektiv/node-cron)
[![Dependency Status](https://david-dm.org/ncb000gt/node-cron.svg)](https://david-dm.org/ncb000gt/node-cron)
This project is looking for help! If you're interested in helping with the project please reach out to me [ncb000gt](https://twitter.com/ncb000gt) and let me know. I'd love for it to continue on, but it needs a lot of attention.
# node-cron
[![Version](https://badgen.net/npm/v/cron?icon=npm)](https://badgen.net/npm/v/cron)
[![Build Status](https://badgen.net/github/status/kelektiv/node-cron?icon=github)](https://badgen.net/github/status/kelektiv/node-cron)
[![Build Checks](https://badgen.net/github/checks/kelektiv/node-cron?icon=github)](https://badgen.net/github/checks/kelektiv/node-cron)
[![Dependency Status](https://badgen.net/david/dep/kelektiv/node-cron)](https://badgen.net/david/dev/kelektiv/node-cron)
[![Code Coverage](https://badgen.net/codecov/c/github/kelektiv/node-cron?icon=codecov)](https://badgen.net/codecov/c/github/kelektiv/node-cron)
[![Known Vulnerabilities](https://snyk.io/test/github/kelektiv/node-cron/badge.svg)](https://snyk.io/test/github/kelektiv/node-cron)
[![Minified size](https://badgen.net/bundlephobia/min/cron)](https://badgen.net/bundlephobia/min/cron)
[![Minzipped size](https://badgen.net/bundlephobia/minzip/cron)](https://badgen.net/bundlephobia/minzip/cron)
[![monthly downloads](https://badgen.net/npm/dm/cron?icon=npm)](https://badgen.net/npm/dm/cron)
Cron is a tool that allows you to execute _something_ on a schedule. This is

@@ -11,18 +22,14 @@ typically done using the cron syntax. We allow you to execute a function

external to the javascript process using `child_process`. Additionally, this
library goes beyond the basic cron syntax and allows you to
supply a Date object. This will be used as the trigger for your callback. Cron
syntax is still an acceptable CronTime format. Although the Cron patterns
supported here extend on the standard Unix format to support seconds digits,
library goes beyond the basic cron syntax and allows you to
supply a Date object. This will be used as the trigger for your callback. Cron
syntax is still an acceptable CronTime format. Although the Cron patterns
supported here extend on the standard Unix format to support seconds digits,
leaving it off will default to 0 and match the Unix behavior.
# Installation
Installation
==
npm install cron
# If You Are Submitting Bugs/Issues
If You Are Submitting Bugs/Issues
==
Because we can't magically know what you are doing to expose an issue, it is

@@ -33,6 +40,4 @@ best if you provide a snippet of code. This snippet need not include your secret

# Versions and Backwards compatibility breaks:
Versions and Backwards compatibility breaks:
==
As goes with semver, breaking backwards compatibility should be explicit in the

@@ -44,16 +49,20 @@ versioning of your library. As such, we'll upgrade the version of this module

# Usage (basic cron usage):
Usage (basic cron usage):
==
```javascript
var CronJob = require('cron').CronJob;
var job = new CronJob('* * * * * *', function() {
console.log('You will see this message every second');
}, null, true, 'America/Los_Angeles');
job.start();
var job = new CronJob(
'* * * * * *',
function() {
console.log('You will see this message every second');
},
null,
true,
'America/Los_Angeles'
);
// Use this if the 4th param is default value(false)
// job.start()
```
Note - You need to explicitly start a job in order to make it run. This gives a
little more control over running your jobs.
Note - You don't need to explicitly start a job in order to make it run since the 4th parameter is set to `true`. However, by default you need to call `job.start()` to start the cron job, which gives a little more control over running your jobs.

@@ -63,6 +72,4 @@ There are more examples available in this repository at:

# Available Cron patterns:
Available Cron patterns:
==
Asterisk. E.g. *

@@ -82,110 +89,99 @@ Ranges. E.g. 1-3,5

# Cron Ranges
Cron Ranges
==
When specifying your cron values you'll need to make sure that your values fall
within the ranges. For instance, some cron's use a 0-7 range for the day of
week where both 0 and 7 represent Sunday. We do not. And that is an optimisation.
week where both 0 and 7 represent Sunday. We do not. And that is an optimisation.
* Seconds: 0-59
* Minutes: 0-59
* Hours: 0-23
* Day of Month: 1-31
* Months: 0-11 (Jan-Dec)
* Day of Week: 0-6 (Sun-Sat)
- Seconds: 0-59
- Minutes: 0-59
- Hours: 0-23
- Day of Month: 1-31
- Months: 0-11 (Jan-Dec)
- Day of Week: 0-6 (Sun-Sat)
# Gotchas
Gotchas
==
- Millisecond level granularity in JS or moment date objects.
Because computers take time to do things, there may be some delay in execution.
This should be on the order of milliseconds. This module doesn't allow MS level
granularity for the regular cron syntax, but _does_ allow you to specify a real
date of execution in either a javascript date object or a moment object.
When this happens you may find that you aren't able to execute a job that
_should_ run in the future like with `new Date().setMilliseconds(new Date().getMilliseconds() + 1)`. This is due to those cycles of execution
above. This wont be the same for everyone because of compute speed. When I
tried it locally I saw that somewhere around the 4-5 ms mark was where I got
consistent ticks using real dates, but anything less than that would result
in an exception. This could be really confusing. We could restrict the
granularity for all dates to seconds, but felt that it wasn't a huge problem
so long as you were made aware. If this becomes more of an issue, We can
revisit it.
- Arrow Functions for `onTick`
Arrow functions get their `this` context from their parent scope. Thus, if you use them, you will not get
the `this` context of the cronjob. You can read a little more in this ticket [GH-40](https://github.com/kelektiv/node-cron/issues/47#issuecomment-459762775)
* Millisecond level granularity in JS or moment date objects.
Because computers take time to do things, there may be some delay in execution.
This should be on the order of milliseconds. This module doesn't allow MS level
granularity for the regular cron syntax, but _does_ allow you to specify a real
date of execution in either a javascript date object or a moment object.
When this happens you may find that you aren't able to execute a job that
_should_ run in the future like with `new Date().setMilliseconds(new
Date().getMilliseconds() + 1)`. This is due to those cycles of execution
above. This wont be the same for everyone because of compute speed. When I
tried it locally I saw that somewhere around the 4-5 ms mark was where I got
consistent ticks using real dates, but anything less than that would result
in an exception. This could be really confusing. We could restrict the
granularity for all dates to seconds, but felt that it wasn't a huge problem
so long as you were made aware. If this becomes more of an issue, We can
revisit it.
* Arrow Functions for `onTick`
Arrow functions get their `this` context from their parent scope. Thus, if you use them, you will not get
the `this` context of the cronjob. You can read a little more in this ticket [GH-40](https://github.com/kelektiv/node-cron/issues/47#issuecomment-459762775)
# API
API
==
Parameter Based
* `job` - shortcut to `new cron.CronJob()`.
* `time` - shortcut to `new cron.CronTime()`.
* `sendAt` - tells you when a `CronTime` will be run.
* `timeout` - tells you when the next timeout is.
* `CronJob`
* `constructor(cronTime, onTick, onComplete, start, timezone, context,
runOnInit, unrefTimeout)` - Of note, the first parameter here can be a JSON object that
has the below names and associated types (see examples above).
* `cronTime` - [REQUIRED] - The time to fire off your job. This can be in
the form of cron syntax or a JS
[Date](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date) object.
* `onTick` - [REQUIRED] - The function to fire at the specified time. If an
`onComplete` callback was provided, `onTick` will receive it as an argument.
`onTick` may call `onComplete` when it has finished its work.
* `onComplete` - [OPTIONAL] - A function that will fire when the job is
stopped with `job.stop()`, and may also be called by `onTick` at the end of each run.
* `start` - [OPTIONAL] - Specifies whether to start the job just before
exiting the constructor. By default this is set to false. If left at default
you will need to call `job.start()` in order to start the job (assuming
`job` is the variable you set the cronjob to). This does not immediately
fire your `onTick` function, it just gives you more control over the
behavior of your jobs.
* `timeZone` - [OPTIONAL] - Specify the timezone for the execution. This
will modify the actual time relative to your timezone. If the timezone is
invalid, an error is thrown. You can check all timezones available at
[Moment Timezone Website](http://momentjs.com/timezone/). Probably don't use
both.
`timeZone` and `utcOffset` together or weird things may happen.
* `context` - [OPTIONAL] - The context within which to execute the onTick
method. This defaults to the cronjob itself allowing you to call
`this.stop()`. However, if you change this you'll have access to the
functions and values within your context object.
* `runOnInit` - [OPTIONAL] - This will immediately fire your `onTick`
function as soon as the requisite initialization has happened. This option
is set to `false` by default for backwards compatibility.
* `utcOffset` - [OPTIONAL] - This allows you to specify the offset of your
timezone rather than using the `timeZone` param. Probably don't use both
`timeZone` and `utcOffset` together or weird things may happen.
* `unrefTimeout` - [OPTIONAL] - If you have code that keeps the event loop
running and want to stop the node process when that finishes regardless of
the state of your cronjob, you can do so making use of this parameter. This
is off by default and cron will run as if it needs to control the event
loop. For more information take a look at
[timers#timers_timeout_unref](https://nodejs.org/api/timers.html#timers_timeout_unref)
from the NodeJS docs.
* `start` - Runs your job.
* `stop` - Stops your job.
* `setTime` - Stops and changes the time for the `CronJob`. Param must be a `CronTime`.
* `lastDate` - Tells you the last execution date.
* `nextDates` - Provides an array of the next set of dates that will trigger an `onTick`.
* `fireOnTick` - Allows you to override the `onTick` calling behavior. This
matters so only do this if you have a really good reason to do so.
* `addCallback` - Allows you to add `onTick` callbacks.
* `CronTime`
* `constructor(time)`
* `time` - [REQUIRED] - The time to fire off your job. This can be in the
form of cron syntax or a JS
[Date](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date)
object.
- `job` - shortcut to `new cron.CronJob()`.
- `time` - shortcut to `new cron.CronTime()`.
- `sendAt` - tells you when a `CronTime` will be run.
- `timeout` - tells you when the next timeout is.
- `CronJob`
- `constructor(cronTime, onTick, onComplete, start, timezone, context, runOnInit, utcOffset, unrefTimeout)` - Of note, the first parameter here can be a JSON object that
has the below names and associated types (see examples above).
- `cronTime` - [REQUIRED] - The time to fire off your job. This can be in
the form of cron syntax or a JS
[Date](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date) object.
- `onTick` - [REQUIRED] - The function to fire at the specified time. If an
`onComplete` callback was provided, `onTick` will receive it as an argument.
`onTick` may call `onComplete` when it has finished its work.
- `onComplete` - [OPTIONAL] - A function that will fire when the job is
stopped with `job.stop()`, and may also be called by `onTick` at the end of each run.
- `start` - [OPTIONAL] - Specifies whether to start the job just before
exiting the constructor. By default this is set to false. If left at default
you will need to call `job.start()` in order to start the job (assuming
`job` is the variable you set the cronjob to). This does not immediately
fire your `onTick` function, it just gives you more control over the
behavior of your jobs.
- `timeZone` - [OPTIONAL] - Specify the timezone for the execution. This
will modify the actual time relative to your timezone. If the timezone is
invalid, an error is thrown. You can check all timezones available at
[Moment Timezone Website](http://momentjs.com/timezone/). Probably don't use
both `timeZone` and `utcOffset` together or weird things may happen.
- `context` - [OPTIONAL] - The context within which to execute the onTick
method. This defaults to the cronjob itself allowing you to call
`this.stop()`. However, if you change this you'll have access to the
functions and values within your context object.
- `runOnInit` - [OPTIONAL] - This will immediately fire your `onTick`
function as soon as the requisite initialization has happened. This option
is set to `false` by default for backwards compatibility.
- `utcOffset` - [OPTIONAL] - This allows you to specify the offset of your
timezone rather than using the `timeZone` param. Probably don't use both
`timeZone` and `utcOffset` together or weird things may happen.
- `unrefTimeout` - [OPTIONAL] - If you have code that keeps the event loop
running and want to stop the node process when that finishes regardless of
the state of your cronjob, you can do so making use of this parameter. This
is off by default and cron will run as if it needs to control the event
loop. For more information take a look at
[timers#timers_timeout_unref](https://nodejs.org/api/timers.html#timers_timeout_unref)
from the NodeJS docs.
- `start` - Runs your job.
- `stop` - Stops your job.
- `setTime` - Stops and changes the time for the `CronJob`. Param must be a `CronTime`.
- `lastDate` - Tells you the last execution date.
- `nextDates` - Provides an array of the next set of dates that will trigger an `onTick`.
- `fireOnTick` - Allows you to override the `onTick` calling behavior. This
matters so only do this if you have a really good reason to do so.
- `addCallback` - Allows you to add `onTick` callbacks.
- `CronTime`
- `constructor(time)`
- `time` - [REQUIRED] - The time to fire off your job. This can be in the
form of cron syntax or a JS
[Date](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date)
object.
# Contributions
Contributions
==
This is a community effort project. In the truest sense, this project started as

@@ -197,6 +193,4 @@ an open source project from [cron.js](http://github.com/padolsey/cron.js) and

# License
License
==
MIT
/* eslint-disable no-new */
const sinon = require('sinon');
const cron = require('../lib/cron');
describe('cron', () => {

@@ -29,3 +30,3 @@ let clock;

callback,
function() {
() => {
expect(callback).toHaveBeenCalledTimes(1);

@@ -70,3 +71,3 @@ done();

callback,
function() {
() => {
expect(callback).toHaveBeenCalledTimes(5);

@@ -114,3 +115,3 @@ done();

callback,
function() {
() => {
expect(callback).toHaveBeenCalledTimes(5);

@@ -135,3 +136,3 @@ done();

it('should run every second for a range ([start]-[end] * * * * *)', done => {
it('should run every second for a range ([start]-[end] * * * * *) with onComplete', done => {
const callback = jest.fn();

@@ -141,3 +142,3 @@ const job = new cron.CronJob(

callback,
function() {
() => {
expect(callback).toHaveBeenCalledTimes(8);

@@ -187,3 +188,3 @@ done();

onTick: callback,
onComplete: function() {
onComplete: () => {
expect(callback).toHaveBeenCalledTimes(1);

@@ -206,3 +207,3 @@ done();

'00 30 * * * *',
function() {
() => {
l.push(Math.floor(Date.now() / 60000));

@@ -216,3 +217,3 @@ },

expect(l.length).toBe(10);
expect(l).toHaveLength(10);
expect(l.every(i => i % 30 === 0)).toBe(true);

@@ -233,3 +234,3 @@

it('should run every 45 minutes for 2 hours (0 */45 * * * *)', done => {
it('should run every 45 minutes for 2 hours (0 */45 * * * *) with onComplete', done => {
const callback = jest.fn();

@@ -239,3 +240,3 @@ const job = new cron.CronJob(

callback,
function() {
() => {
expect(callback).toHaveBeenCalledTimes(4);

@@ -252,11 +253,10 @@ done();

it('should start and stop job', done => {
it('should start and stop job from outside', done => {
const callback = jest.fn();
const job = new cron.CronJob(
'* * * * * *',
function() {
function () {
callback();
this.stop();
},
function() {
() => {
expect(callback).toHaveBeenCalledTimes(1);

@@ -272,2 +272,20 @@ clock.restore();

it('should start and stop job from inside (default context)', done => {
const callback = jest.fn();
new cron.CronJob(
'* * * * * *',
function () {
callback();
this.stop();
},
() => {
expect(callback).toHaveBeenCalledTimes(1);
clock.restore();
done();
},
true
);
clock.tick(1000);
});
describe('with date', () => {

@@ -282,3 +300,3 @@ it('should run on a specific date', () => {

d,
function() {
() => {
var t = new Date();

@@ -305,3 +323,3 @@ expect(t.getSeconds()).toBe(d.getSeconds());

d,
function() {
() => {
var t = new Date();

@@ -311,3 +329,3 @@ expect(t.getSeconds()).toBe(d.getSeconds());

},
function() {
() => {
expect(callback).toHaveBeenCalledTimes(1);

@@ -323,3 +341,3 @@ done();

it('should wait and not fire immediately', function() {
it('should wait and not fire immediately', () => {
const clock = sinon.useFakeTimers();

@@ -339,23 +357,43 @@ const callback = jest.fn();

});
it('should wait but fire on init', () => {
const clock = sinon.useFakeTimers();
const callback = jest.fn();
const d = new Date().getTime() + 31 * 86400 * 1000;
var job = cron.job({
cronTime: new Date(d),
onTick: callback,
runOnInit: true
});
expect(callback).toHaveBeenCalledTimes(1);
job.start();
clock.tick(1000);
clock.restore();
job.stop();
expect(callback).toHaveBeenCalledTimes(1);
});
});
describe('with timezone', () => {
it('should run a job using cron syntax', function() {
it('should run a job using cron syntax', () => {
const callback = jest.fn();
const moment = require('moment-timezone');
const luxon = require('luxon');
let zone = 'America/Chicago';
// New Orleans time
const t = moment();
t.tz(zone);
let t = luxon.DateTime.local().setZone(zone);
// Current time
const d = moment();
const d = luxon.DateTime.local();
// If current time is New Orleans time, switch to Los Angeles..
if (t.hours() === d.hours()) {
if (t.hour === d.hour) {
zone = 'America/Los_Angeles';
t.tz(zone);
t = t.setZone(zone);
}
expect(d.hours()).not.toBe(t.hours());
expect(d.hour).not.toBe(t.hour);

@@ -365,3 +403,3 @@ // If t = 59s12m then t.setSeconds(60)

// this and no testRun callback.
t.add(1, 's');
t = t.plus({ seconds: 1 });
// Run a job designed to be executed at a given

@@ -371,3 +409,3 @@ // time in `zone`, making sure that it is a different

const job = new cron.CronJob(
t.seconds() + ' ' + t.minutes() + ' ' + t.hours() + ' * * *',
t.second + ' ' + t.minute + ' ' + t.hour + ' * * *',
callback,

@@ -385,22 +423,21 @@ null,

it('should run a job using a date', function() {
const moment = require('moment-timezone');
it('should run a job using a date', () => {
const luxon = require('luxon');
let zone = 'America/Chicago';
// New Orleans time
const t = moment();
t.tz(zone);
let t = luxon.DateTime.local().setZone(zone);
// Current time
const d = moment();
let d = luxon.DateTime.local();
// If current time is New Orleans time, switch to Los Angeles..
if (t.hours() === d.hours()) {
if (t.hour === d.hour) {
zone = 'America/Los_Angeles';
t.tz(zone);
t = t.setZone(zone);
}
expect(d.hours()).not.toBe(t.hours());
d.add(1, 'second');
expect(d.hour).not.toBe(t.hour);
d = d.plus({ seconds: 1 });
const clock = sinon.useFakeTimers(d.valueOf());
const callback = jest.fn();
const job = new cron.CronJob(d._d, callback, null, true, zone);
const job = new cron.CronJob(d.toJSDate(), callback, null, true, zone);
clock.tick(1000);

@@ -412,8 +449,8 @@ clock.restore();

it('should test if timezone is valid.', function() {
expect(function() {
it('should test if timezone is valid.', () => {
expect(() => {
// eslint-disable-next-line no-new
new cron.CronJob({
cronTime: '* * * * * *',
onTick: function() {},
onTick: () => {},
timeZone: 'fake/timezone'

@@ -425,3 +462,3 @@ });

it('should start, change time, start again', function() {
it('should start, change time, start again', () => {
const callback = jest.fn();

@@ -447,4 +484,12 @@ const clock = sinon.useFakeTimers();

it('should start, change time, exception', function() {
it('should setTime with invalid object', () => {
const callback = jest.fn();
const job = new cron.CronJob('* * * * * *', callback);
expect(() => {
job.setTime(undefined);
}).toThrow();
});
it('should start, change time, exception', () => {
const callback = jest.fn();
var clock = sinon.useFakeTimers();

@@ -460,3 +505,3 @@

job.stop();
expect(function() {
expect(() => {
job.setTime(time);

@@ -470,3 +515,3 @@ }).toThrow();

it('should scope onTick to running job', function() {
it('should scope onTick to running job', () => {
const clock = sinon.useFakeTimers();

@@ -476,3 +521,3 @@

'* * * * * *',
function() {
function () {
expect(job).toBeInstanceOf(cron.CronJob);

@@ -491,3 +536,3 @@ expect(job).toEqual(this);

it('should scope onTick to object', function() {
it('should scope onTick to object', () => {
const clock = sinon.useFakeTimers();

@@ -497,3 +542,3 @@

'* * * * * *',
function() {
function () {
expect(this.hello).toEqual('world');

@@ -514,3 +559,3 @@ expect(job).not.toEqual(this);

it('should scope onTick to object within contstructor object', function() {
it('should scope onTick to object within constructor object', () => {
const clock = sinon.useFakeTimers();

@@ -520,3 +565,3 @@

cronTime: '* * * * * *',
onTick: function() {
onTick: function () {
expect(this.hello).toEqual('world');

@@ -535,7 +580,7 @@ expect(job).not.toEqual(this);

it('should not get into an infinite loop on invalid times', function() {
expect(function() {
it('should not get into an infinite loop on invalid times', () => {
expect(() => {
new cron.CronJob(
'* 60 * * * *',
function() {
() => {
expect.ok(true);

@@ -548,6 +593,6 @@ },

expect(function() {
expect(() => {
new cron.CronJob(
'* * 24 * * *',
function() {
() => {
expect.ok(true);

@@ -561,3 +606,3 @@ },

it('should test start of month', function() {
it('should test start of month', () => {
const callback = jest.fn();

@@ -585,3 +630,3 @@ const d = new Date('12/31/2014');

it('should not fire if time was adjusted back', function() {
it('should not fire if time was adjusted back', () => {
const callback = jest.fn();

@@ -601,3 +646,3 @@ const clock = sinon.useFakeTimers({

it('should run every day', function() {
it('should run every day', () => {
const callback = jest.fn();

@@ -625,3 +670,3 @@ const d = new Date('12/31/2014');

it('should run every 2 hours between hours', function() {
it('should run every 2 hours between hours', () => {
const callback = jest.fn();

@@ -653,3 +698,3 @@ const d = new Date('12/31/2014');

it('should run every minute', function() {
it('should run every minute', () => {
const callback = jest.fn();

@@ -678,3 +723,3 @@ const d = new Date('12/31/2014');

it('should run every day', function() {
it('should run every day at 12:30', () => {
const callback = jest.fn();

@@ -707,3 +752,3 @@ const d = new Date('12/31/2014');

it('should trigger onTick at midnight', function() {
it('should trigger onTick at midnight', () => {
const callback = jest.fn();

@@ -731,3 +776,3 @@ const d = new Date('12/31/2014');

it('should run every day UTC', function() {
it('should run every day UTC', () => {
const callback = jest.fn();

@@ -762,3 +807,3 @@ const d = new Date('12/31/2014');

// from https://github.com/kelektiv/node-cron/issues/180#issuecomment-154108131
it('should run once not double', function() {
it('should run once not double', () => {
const callback = jest.fn();

@@ -782,17 +827,14 @@ const d = new Date(2015, 1, 1, 1, 1, 41, 0);

describe('with utcOffset', function() {
it('should run a job using cron syntax with number format utcOffset', function() {
describe('with utcOffset', () => {
it('should run a job using cron syntax with number format utcOffset', () => {
const clock = sinon.useFakeTimers();
const callback = jest.fn();
const moment = require('moment-timezone');
const luxon = require('luxon');
// Current time
const t = moment();
const t = luxon.DateTime.local();
// UTC Offset decreased by an hour
const utcOffset = t.utcOffset() - 60;
const utcOffset = t.offset - 60;
const job = new cron.CronJob(
t.seconds() + ' ' + t.minutes() + ' ' + t.hours() + ' * * *',
t.second + ' ' + t.minute + ' ' + t.hour + ' * * *',
callback,

@@ -817,13 +859,10 @@ null,

it('should run a job using cron syntax with string format utcOffset', function() {
it('should run a job using cron syntax with string format utcOffset', () => {
const clock = sinon.useFakeTimers();
const callback = jest.fn();
const moment = require('moment-timezone');
const luxon = require('luxon');
// Current time
const t = moment();
const t = luxon.DateTime.local();
// UTC Offset decreased by an hour (string format '(+/-)HH:mm')
const utcOffset = t.utcOffset() - 60;
const utcOffset = t.offset - 60;
let utcOffsetString = utcOffset > 0 ? '+' : '-';

@@ -835,3 +874,3 @@ utcOffsetString += ('0' + Math.floor(Math.abs(utcOffset) / 60)).slice(-2);

var job = new cron.CronJob(
t.seconds() + ' ' + t.minutes() + ' ' + t.hours() + ' * * *',
t.second + ' ' + t.minute + ' ' + t.hour + ' * * *',
callback,

@@ -857,3 +896,3 @@ null,

it('should run a job using cron syntax with number format utcOffset that is 0', function() {
it('should run a job using cron syntax with number format utcOffset that is 0', () => {
const clock = sinon.useFakeTimers();

@@ -882,4 +921,4 @@ const callback = jest.fn();

it('should be able to detect out of range days of month', function() {
expect(function() {
it('should be able to detect out of range days of month', () => {
expect(() => {
new cron.CronTime('* * 32 FEB *');

@@ -889,2 +928,57 @@ }).toThrow();

});
it('should give the next date to run at', () => {
const callback = jest.fn();
const clock = sinon.useFakeTimers();
const job = new cron.CronJob('* * * * * *', callback);
const d = Date.now();
expect(job.nextDate().toMillis()).toEqual(d + 1000);
clock.restore();
});
it('should give the next dates to run at', () => {
const callback = jest.fn();
const clock = sinon.useFakeTimers();
const job = new cron.CronJob('* * * * * *', callback);
const d = Date.now();
expect(job.nextDates(5).map(d => d.toMillis())).toEqual([
d + 1000,
d + 2000,
d + 3000,
d + 4000,
d + 5000
]);
clock.restore();
});
it('should automatically setup a new timeout if we roll past the max timeout delay', () => {
const callback = jest.fn();
const clock = sinon.useFakeTimers();
const d = new Date();
d.setMilliseconds(2147485647 * 2); // MAXDELAY in `job.js` + 2000.
const job = new cron.CronJob(d, callback);
job.start();
clock.tick(2147483648);
expect(callback).toHaveBeenCalledTimes(0);
clock.tick(2147489648);
expect(callback).toHaveBeenCalledTimes(1);
job.stop();
clock.restore();
});
it('should give the last execution date', () => {
const callback = jest.fn();
const clock = sinon.useFakeTimers();
const job = new cron.CronJob('* * * * * *', callback);
job.start();
clock.tick(1000);
expect(callback).toHaveBeenCalledTimes(1);
expect(job.lastDate().getTime()).toEqual(1000);
job.stop();
clock.restore();
});
});
/* eslint-disable no-new */
const sinon = require('sinon');
const luxon = require('luxon');
const cron = require('../lib/cron');
const moment = require('moment-timezone');
describe('crontime', function() {
it('should test stars (* * * * * *)', function() {
expect(function() {
describe('crontime', () => {
it('should test stars (* * * * * *)', () => {
expect(() => {
new cron.CronTime('* * * * * *');

@@ -13,4 +13,4 @@ }).not.toThrow();

it('should test digit (0 * * * * *)', function() {
expect(function() {
it('should test digit (0 * * * * *)', () => {
expect(() => {
new cron.CronTime('0 * * * * *');

@@ -20,4 +20,4 @@ }).not.toThrow();

it('should test multi digits (08 * * * * *)', function() {
expect(function() {
it('should test multi digits (08 * * * * *)', () => {
expect(() => {
new cron.CronTime('08 * * * * *');

@@ -27,4 +27,4 @@ }).not.toThrow();

it('should test all digits (08 8 8 8 8 5)', function() {
expect(function() {
it('should test all digits (08 8 8 8 8 5)', () => {
expect(() => {
new cron.CronTime('08 * * * * *');

@@ -34,4 +34,4 @@ }).not.toThrow();

it('should test too many digits (08 8 8 8 8 5)', function() {
expect(function() {
it('should test too many digits (08 8 8 8 8 5)', () => {
expect(() => {
new cron.CronTime('08 * * * * *');

@@ -41,4 +41,4 @@ }).not.toThrow();

it('should test standard cron format (* * * * *)', function() {
expect(function() {
it('should test standard cron format (* * * * *)', () => {
expect(() => {
new cron.CronTime('* * * * *');

@@ -48,3 +48,3 @@ }).not.toThrow();

it('should test standard cron format (8 8 8 8 5)', function() {
it('should test standard cron format (8 8 8 8 5)', () => {
const standard = new cron.CronTime('8 8 8 8 5');

@@ -61,4 +61,4 @@ const extended = new cron.CronTime('0 8 8 8 8 5');

it('should test hyphen (0-10 * * * * *)', function() {
expect(function() {
it('should test hyphen (0-10 * * * * *)', () => {
expect(() => {
new cron.CronTime('0-10 * * * * *');

@@ -68,4 +68,4 @@ }).not.toThrow();

it('should test multi hyphens (0-10 0-10 * * * *)', function() {
expect(function() {
it('should test multi hyphens (0-10 0-10 * * * *)', () => {
expect(() => {
new cron.CronTime('0-10 0-10 * * * *');

@@ -75,4 +75,4 @@ }).not.toThrow();

it('should test all hyphens (0-10 0-10 1-10 1-10 0-6 0-1)', function() {
expect(function() {
it('should test all hyphens (0-10 0-10 1-10 1-10 0-6 0-1)', () => {
expect(() => {
new cron.CronTime('0-10 0-10 1-10 1-10 0-6 0-1');

@@ -82,4 +82,4 @@ }).not.toThrow();

it('should test comma (0,10 * * * * *)', function() {
expect(function() {
it('should test comma (0,10 * * * * *)', () => {
expect(() => {
new cron.CronTime('0,10 * * * * *');

@@ -89,4 +89,4 @@ }).not.toThrow();

it('should test multi commas (0,10 0,10 * * * *)', function() {
expect(function() {
it('should test multi commas (0,10 0,10 * * * *)', () => {
expect(() => {
new cron.CronTime('0,10 0,10 * * * *');

@@ -96,4 +96,4 @@ }).not.toThrow();

it('should test all commas (0,10 0,10 1,10 1,10 0,6 0,1)', function() {
expect(function() {
it('should test all commas (0,10 0,10 1,10 1,10 0,6 0,1)', () => {
expect(() => {
new cron.CronTime('0,10 0,10 1,10 1,10 0,6 0,1');

@@ -103,4 +103,4 @@ }).not.toThrow();

it('should test alias (* * * * jan *)', function() {
expect(function() {
it('should test alias (* * * * jan *)', () => {
expect(() => {
new cron.CronTime('* * * * jan *');

@@ -110,4 +110,4 @@ }).not.toThrow();

it('should test multi aliases (* * * * jan,feb *)', function() {
expect(function() {
it('should test multi aliases (* * * * jan,feb *)', () => {
expect(() => {
new cron.CronTime('* * * * jan,feb *');

@@ -117,4 +117,4 @@ }).not.toThrow();

it('should test all aliases (* * * * jan,feb mon,tue)', function() {
expect(function() {
it('should test all aliases (* * * * jan,feb mon,tue)', () => {
expect(() => {
new cron.CronTime('* * * * jan,feb mon,tue');

@@ -124,4 +124,4 @@ }).not.toThrow();

it('should test unknown alias (* * * * jar *)', function() {
expect(function() {
it('should test unknown alias (* * * * jar *)', () => {
expect(() => {
new cron.CronTime('* * * * jar *');

@@ -131,4 +131,4 @@ }).toThrow();

it('should test unknown alias - short (* * * * j *)', function() {
expect(function() {
it('should test unknown alias - short (* * * * j *)', () => {
expect(() => {
new cron.CronTime('* * * * j *');

@@ -138,4 +138,4 @@ }).toThrow();

it('should test too few fields', function() {
expect(function() {
it('should test too few fields', () => {
expect(() => {
new cron.CronTime('* * * *', null, null);

@@ -145,4 +145,4 @@ }).toThrow();

it('should test too many fields', function() {
expect(function() {
it('should test too many fields', () => {
expect(() => {
new cron.CronTime('* * * * * * *', null, null);

@@ -152,4 +152,4 @@ }).toThrow();

it('should test out of range values', function() {
expect(function() {
it('should test out of range values', () => {
expect(() => {
new cron.CronTime('* * * * 1234', null, null);

@@ -159,4 +159,4 @@ }).toThrow();

it('should test invalid wildcard expression', function() {
expect(function() {
it('should test invalid wildcard expression', () => {
expect(() => {
new cron.CronTime('* * * * 0*');

@@ -166,4 +166,4 @@ }).toThrow();

it('should test invalid step', function() {
expect(function() {
it('should test invalid step', () => {
expect(() => {
new cron.CronTime('* * * 1/0 *');

@@ -173,4 +173,4 @@ }).toThrow();

it('should test invalid range', function() {
expect(function() {
it('should test invalid range', () => {
expect(() => {
new cron.CronTime('* 2-1 * * *');

@@ -180,9 +180,9 @@ }).toThrow();

it('should test Date', function() {
it('should test Date', () => {
const d = new Date();
const ct = new cron.CronTime(d);
expect(ct.source.isSame(d.getTime())).toBe(true);
expect(ct.source.toMillis()).toEqual(d.getTime());
});
it('should test day roll-over', function() {
it('should test day roll-over', () => {
const numHours = 24;

@@ -195,8 +195,8 @@ const ct = new cron.CronTime('0 0 17 * * *');

expect(next - start).toBeLessThan(24 * 60 * 60 * 1000);
expect(next._d.getTime()).toBeGreaterThan(start.getTime());
expect(next.toMillis()).toBeGreaterThan(start.getTime());
}
});
it('should test illegal repetition syntax', function() {
expect(function() {
it('should test illegal repetition syntax', () => {
expect(() => {
new cron.CronTime('* * /4 * * *');

@@ -206,3 +206,3 @@ }).toThrow();

it('should test next date', function() {
it('should test next date', () => {
const ct = new cron.CronTime('0 0 */4 * * *');

@@ -214,12 +214,11 @@

expect(nextdt._d.getTime()).toBeGreaterThan(nextDate.getTime());
expect(nextdt.hours() % 4).toEqual(0);
expect(nextdt.toMillis()).toBeGreaterThan(nextDate.getTime());
expect(nextdt.hour % 4).toEqual(0);
});
it('should throw an exception because next date is invalid', function() {
it('should throw an exception because next date is invalid', () => {
const ct = new cron.CronTime('0 0 * * * *');
const nextDate = new Date('My invalid date string');
try {
// eslint-disable-next-line no-unused-vars
const nextdt = ct._getNextDateFrom(nextDate);
ct._getNextDateFrom(nextDate);
} catch (e) {

@@ -230,4 +229,5 @@ expect(e.message).toEqual('ERROR: You specified an invalid date.');

it('should test next real date', function() {
it('should test next real date', () => {
const initialDate = new Date();
initialDate.setDate(initialDate.getDate() + 1); // In other case date will be in the past
const ct = new cron.CronTime(initialDate);

@@ -237,3 +237,3 @@

nextDate.setMonth(nextDate.getMonth() + 1);
expect(nextDate.getTime()).toBeGreaterThan(ct.source._d.getTime());
expect(nextDate.getTime()).toBeGreaterThan(ct.source.toMillis());
const nextdt = ct.sendAt(0);

@@ -243,9 +243,56 @@ // there shouldn't be a "next date" when using a real date.

// so the return should be the date passed in unless explicitly reset
expect(nextdt.isBefore(nextDate)).toBeTruthy();
expect(nextdt.isSame(initialDate)).toBeTruthy();
expect(nextdt < nextDate).toBeTruthy();
expect(nextdt.toMillis()).toEqual(initialDate.getTime());
});
describe('should throw an exception because `L` not supported', function() {
it('(* * * L * *)', function() {
expect(function() {
describe('presets', () => {
it('should parse @secondly', () => {
const cronTime = new cron.CronTime('@secondly');
expect(cronTime.toString()).toEqual('* * * * * *');
});
it('should parse @minutely', () => {
const cronTime = new cron.CronTime('@minutely');
expect(cronTime.toString()).toEqual('0 * * * * *');
});
it('should parse @hourly', () => {
const cronTime = new cron.CronTime('@hourly');
expect(cronTime.toString()).toEqual('0 0 * * * *');
});
it('should parse @daily', () => {
const cronTime = new cron.CronTime('@daily');
expect(cronTime.toString()).toEqual('0 0 0 * * *');
});
it('should parse @weekly', () => {
const cronTime = new cron.CronTime('@weekly');
expect(cronTime.toString()).toEqual('0 0 0 * * 0');
});
it('should parse @weekdays', () => {
const cronTime = new cron.CronTime('@weekdays');
expect(cronTime.toString()).toEqual('0 0 0 * * 1,2,3,4,5');
});
it('should parse @weekends', () => {
const cronTime = new cron.CronTime('@weekends');
expect(cronTime.toString()).toEqual('0 0 0 * * 0,6');
});
it('should parse @monthly', () => {
const cronTime = new cron.CronTime('@monthly');
expect(cronTime.toString()).toEqual('0 0 0 1 * *');
});
it('should parse @yearly', () => {
const cronTime = new cron.CronTime('@yearly');
expect(cronTime.toString()).toEqual('0 0 0 1 0 *');
});
});
describe('should throw an exception because `L` not supported', () => {
it('(* * * L * *)', () => {
expect(() => {
new cron.CronTime('* * * L * *');

@@ -255,4 +302,4 @@ }).toThrow();

it('(* * * * * L)', function() {
expect(function() {
it('(* * * * * L)', () => {
expect(() => {
new cron.CronTime('* * * * * L');

@@ -263,15 +310,19 @@ }).toThrow();

it('should strip off millisecond', function() {
it('should strip off millisecond', () => {
const cronTime = new cron.CronTime('0 */10 * * * *');
const x = cronTime._getNextDateFrom(new Date('2018-08-10T02:20:00.999Z'));
expect(x.toISOString()).toEqual('2018-08-10T02:30:00.000Z');
expect(x.toMillis()).toEqual(
new Date('2018-08-10T02:30:00.000Z').getTime()
);
});
it('should strip off millisecond (2)', function() {
it('should strip off millisecond (2)', () => {
const cronTime = new cron.CronTime('0 */10 * * * *');
const x = cronTime._getNextDateFrom(new Date('2018-08-10T02:19:59.999Z'));
expect(x.toISOString()).toEqual('2018-08-10T02:20:00.000Z');
expect(x.toMillis()).toEqual(
new Date('2018-08-10T02:20:00.000Z').getTime()
);
});
it('should generete the right next days when cron is set to every minute', function() {
it('should generete the right next days when cron is set to every minute', () => {
const cronTime = new cron.CronTime('* * * * *');

@@ -287,3 +338,3 @@ const min = 60000;

it('should generete the right next days when cron is set to every 15 min', function() {
it('should generete the right next days when cron is set to every 15 min', () => {
const cronTime = new cron.CronTime('*/15 * * * *');

@@ -298,3 +349,3 @@ const min = 60000 * 15;

});
it('should work around time zone changes that shifts time back (1)', function() {
it('should work around time zone changes that shifts time back (1)', () => {
const d = new Date('10-7-2018');

@@ -305,15 +356,22 @@ // America/Sao_Paulo has a time zone change around NOV 3 2018.

expect(nextDate.valueOf()).toEqual(
moment('2018-11-04T09:00:00.000-02:00').valueOf()
luxon.DateTime.fromISO('2018-11-04T09:00:00.000-02:00').valueOf()
);
});
it('should work around time zone changes that shifts time back (2)', function() {
it('should work around time zone changes that shifts time back (2)', () => {
// Asia/Amman DST ends in 26 - OCT-2018 (-1 to hours)
const d = moment.tz('2018-10-25T23:00', 'Asia/Amman');
const currentDate = luxon.DateTime.fromISO('2018-10-25T23:00', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('0 0 * * *');
const nextDate = cronTime._getNextDateFrom(d, 'Asia/Amman');
expect(nextDate - moment.tz('2018-10-26T00:00', 'Asia/Amman')).toEqual(0);
const nextDate = cronTime._getNextDateFrom(currentDate, 'Asia/Amman');
const expectedDate = luxon.DateTime.fromISO('2018-10-26T00:00', {
zone: 'Asia/Amman'
});
expect(nextDate - expectedDate).toEqual(0);
});
it('should work around time zone changes that shifts time forward', function() {
it('should work around time zone changes that shifts time forward', () => {
// Asia/Amman DST starts in 30-March-2018 (+1 to hours)
let currentDate = moment.tz('2018-03-29T23:00', 'Asia/Amman');
let currentDate = luxon.DateTime.fromISO('2018-03-29T23:00', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('* * * * *');

@@ -326,7 +384,5 @@ for (let i = 0; i < 100; i++) {

});
it('should generate the right N next days for * * * * *', function() {
it('should generate the right N next days for * * * * *', () => {
const cronTime = new cron.CronTime('* * * * *');
let currentDate = moment()
.seconds(0)
.milliseconds(0);
let currentDate = luxon.DateTime.local().set({ second: 0, millisecond: 0 });
for (let i = 0; i < 100; i++) {

@@ -338,10 +394,7 @@ const nextDate = cronTime._getNextDateFrom(currentDate);

});
it('should generate the right N next days for 0 0 9 * * *', function() {
it('should generate the right N next days for 0 0 9 * * *', () => {
const cronTime = new cron.CronTime('0 0 9 * * *');
let currentDate = moment()
.utc()
.seconds(0)
.milliseconds(0)
.hours(9)
.minutes(0);
let currentDate = luxon.DateTime.local()
.setZone('utc')
.set({ hour: 9, minute: 0, second: 0, millisecond: 0 });
for (let i = 0; i < 100; i++) {

@@ -353,8 +406,7 @@ const nextDate = cronTime._getNextDateFrom(currentDate);

});
it('should generate the right N next days for 0 0 * * * with a time zone', function() {
it('should generate the right N next days for 0 0 * * * with a time zone', () => {
const cronTime = new cron.CronTime('0 * * * *');
let currentDate = moment
.tz('2018-11-02T23:00', 'America/Sao_Paulo')
.seconds(0)
.milliseconds(0);
let currentDate = luxon.DateTime.fromISO('2018-11-02T23:00', {
zone: 'America/Sao_Paulo'
}).set({ second: 0, millisecond: 0 });
for (let i = 0; i < 25; i++) {

@@ -369,8 +421,7 @@ const nextDate = cronTime._getNextDateFrom(

});
it('should generate the right N next days for */3 * * * * with a time zone', function() {
it('should generate the right N next days for */3 * * * * with a time zone', () => {
const cronTime = new cron.CronTime('*/3 * * * *');
let currentDate = moment
.tz('2018-11-02T23:00', 'America/Sao_Paulo')
.seconds(0)
.milliseconds(0);
let currentDate = luxon.DateTime.fromISO('2018-11-02T23:00', {
zone: 'America/Sao_Paulo'
}).set({ second: 0, millisecond: 0 });
for (let i = 0; i < 25; i++) {

@@ -385,3 +436,3 @@ const nextDate = cronTime._getNextDateFrom(

});
it('should generete the right next day when cron is set to every 15 min in Feb', function() {
it('should generete the right next day when cron is set to every 15 min in Feb', () => {
const cronTime = new cron.CronTime('*/15 * * FEB *');

@@ -394,19 +445,19 @@ const previousDate = new Date(Date.UTC(2018, 3, 0, 0, 0));

});
it('should generate the right next day when cron is set to both day of the month and day of the week (1)', function() {
it('should generate the right next day when cron is set to both day of the month and day of the week (1)', () => {
const cronTime = new cron.CronTime('0 8 1 * 4');
const previousDate = new Date(Date.UTC(2019, 3, 22, 0, 0));
const previousDate = new Date(Date.UTC(2019, 3, 21, 0, 0));
const nextDate = cronTime._getNextDateFrom(previousDate, 'UTC');
expect(nextDate.valueOf()).toEqual(
new Date(Date.UTC(2019, 3, 25, 8, 0)).valueOf()
expect(nextDate.toMillis()).toEqual(
new Date(Date.UTC(2019, 3, 25, 8, 0)).getTime()
);
});
it('should generate the right next day when cron is set to both day of the month and day of the week (2)', function() {
it('should generate the right next day when cron is set to both day of the month and day of the week (2)', () => {
const cronTime = new cron.CronTime('0 8 1 * 4');
const previousDate = new Date(Date.UTC(2019, 3, 26, 0, 0));
const nextDate = cronTime._getNextDateFrom(previousDate, 'UTC');
expect(nextDate.valueOf()).toEqual(
new Date(Date.UTC(2019, 4, 1, 8, 0)).valueOf()
expect(nextDate.toMillis()).toEqual(
new Date(Date.UTC(2019, 4, 1, 8, 0)).getTime()
);
});
it('should generate the right next day when cron is set to both day of the month and day of the week (3)', function() {
it('should generate the right next day when cron is set to both day of the month and day of the week (3)', () => {
const cronTime = new cron.CronTime('0 8 1 * 4');

@@ -420,10 +471,8 @@ const previousDate = new Date(Date.UTC(2019, 7, 1, 7, 59));

it('should accept 0 as a valid UTC offset', function() {
it('should accept 0 as a valid UTC offset', () => {
const clock = sinon.useFakeTimers();
const cronTime = new cron.CronTime('0 11 * * *', null, 0);
const expected = moment()
.add(11, 'hours')
.unix();
const actual = cronTime.sendAt().unix();
const expected = luxon.DateTime.local().plus({ hours: 11 }).toSeconds();
const actual = cronTime.sendAt().toSeconds();

@@ -435,10 +484,8 @@ expect(actual).toEqual(expected);

it('should accept -120 as a valid UTC offset', function() {
it('should accept -120 as a valid UTC offset', () => {
const clock = sinon.useFakeTimers();
const cronTime = new cron.CronTime('0 11 * * *', null, -120);
const expected = moment()
.add(13, 'hours')
.unix();
const actual = cronTime.sendAt().unix();
const expected = luxon.DateTime.local().plus({ hours: 13 }).toSeconds();
const actual = cronTime.sendAt().toSeconds();

@@ -449,2 +496,26 @@ expect(actual).toEqual(expected);

});
it('should accept 4 as a valid UTC offset', () => {
const clock = sinon.useFakeTimers();
const cronTime = new cron.CronTime('0 11 * * *', null, 5);
const expected = luxon.DateTime.local().plus({ hours: 6 }).toSeconds();
const actual = cronTime.sendAt().toSeconds();
expect(actual).toEqual(expected);
clock.restore();
});
it('should detect real date in the past', () => {
const clock = sinon.useFakeTimers();
const d = new Date();
clock.tick(1000);
const time = new cron.CronTime(d);
expect(() => {
time.sendAt();
}).toThrow();
clock.restore();
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc