agenda
Version:
Light weight job scheduler for Node.js
93 lines (86 loc) • 3.6 kB
JavaScript
'use strict';
const humanInterval = require('human-interval');
const CronTime = require('cron').CronTime;
const moment = require('moment-timezone');
const date = require('date.js');
const debug = require('debug')('agenda:job');
/**
* Internal method used to compute next time a job should run and sets the proper values
* @returns {exports} instance of Job instance
*/
module.exports = function() {
const interval = this.attrs.repeatInterval;
const timezone = this.attrs.repeatTimezone;
const repeatAt = this.attrs.repeatAt;
this.attrs.nextRunAt = undefined;
const dateForTimezone = date => {
date = moment(date);
if (timezone !== null) {
date.tz(timezone);
}
return date;
};
/**
* Internal method that computes the interval
* @returns {undefined}
*/
const computeFromInterval = () => {
debug('[%s:%s] computing next run via interval [%s]', this.attrs.name, this.attrs._id, interval);
let lastRun = this.attrs.lastRunAt || new Date();
lastRun = dateForTimezone(lastRun);
try {
const cronTime = new CronTime(interval);
let nextDate = cronTime._getNextDateFrom(lastRun);
if (nextDate.valueOf() === lastRun.valueOf()) {
// Handle cronTime giving back the same date for the next run time
nextDate = cronTime._getNextDateFrom(dateForTimezone(new Date(lastRun.valueOf() + 1000)));
}
this.attrs.nextRunAt = nextDate;
debug('[%s:%s] nextRunAt set to [%s]', this.attrs.name, this.attrs._id, this.attrs.nextRunAt.toISOString());
} catch (e) {
// Nope, humanInterval then!
try {
if (!this.attrs.lastRunAt && humanInterval(interval)) {
this.attrs.nextRunAt = lastRun.valueOf();
debug('[%s:%s] nextRunAt set to [%s]', this.attrs.name, this.attrs._id, this.attrs.nextRunAt.toISOString());
} else {
this.attrs.nextRunAt = lastRun.valueOf() + humanInterval(interval);
debug('[%s:%s] nextRunAt set to [%s]', this.attrs.name, this.attrs._id, this.attrs.nextRunAt.toISOString());
}
} catch (e) {}
} finally {
if (isNaN(this.attrs.nextRunAt)) {
this.attrs.nextRunAt = undefined;
debug('[%s:%s] failed to calculate nextRunAt due to invalid repeat interval', this.attrs.name, this.attrs._id);
this.fail('failed to calculate nextRunAt due to invalid repeat interval');
}
}
};
/**
* Internal method to compute next run time from the repeat string
* @returns {undefined}
*/
function computeFromRepeatAt() {
const lastRun = this.attrs.lastRunAt || new Date();
const nextDate = date(repeatAt).valueOf();
// If you do not specify offset date for below test it will fail for ms
const offset = Date.now();
if (offset === date(repeatAt, offset).valueOf()) {
this.attrs.nextRunAt = undefined;
debug('[%s:%s] failed to calculate repeatAt due to invalid format', this.attrs.name, this.attrs._id);
this.fail('failed to calculate repeatAt time due to invalid format');
} else if (nextDate.valueOf() === lastRun.valueOf()) {
this.attrs.nextRunAt = date('tomorrow at ', repeatAt);
debug('[%s:%s] nextRunAt set to [%s]', this.attrs.name, this.attrs._id, this.attrs.nextRunAt.toISOString());
} else {
this.attrs.nextRunAt = date(repeatAt);
debug('[%s:%s] nextRunAt set to [%s]', this.attrs.name, this.attrs._id, this.attrs.nextRunAt.toISOString());
}
}
if (interval) {
computeFromInterval.call(this);
} else if (repeatAt) {
computeFromRepeatAt.call(this);
}
return this;
};