Socket
Socket
Sign inDemoInstall

cron

Package Overview
Dependencies
Maintainers
3
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cron - npm Package Compare versions

Comparing version 2.2.0 to 2.3.0

examples/utc_offset_syntax.js

330

lib/time.js

@@ -222,2 +222,6 @@ const CONSTRAINTS = [

getNextDateFrom: function (start, zone) {
return this._getNextDateFrom(start, zone);
},
/**

@@ -272,5 +276,4 @@ * Get next date matching the specified cron time.

Please refer to the canonical issue (https://github.com/kelektiv/node-cron/issues/467) and provide the following string if you would like to help debug:
Time Zone: ${zone || '""'} - Cron String: ${this} - UTC offset: ${date.format(
'Z'
)} - current Date: ${luxon.DateTime.local().toString()}`
Time Zone: ${zone || '""'} - Cron String: ${this} - UTC offset: ${date.offset}
- current Date: ${luxon.DateTime.local().toString()}`
);

@@ -285,2 +288,8 @@ }

date = date.set({ day: 1, hour: 0, minute: 0, second: 0 });
if (this._forwardDSTJump(0, 0, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -299,2 +308,8 @@ }

date = date.set({ hour: 0, minute: 0, second: 0 });
if (this._forwardDSTJump(0, 0, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -313,2 +328,7 @@ }

date = date.set({ hour: 0, minute: 0, second: 0 });
if (this._forwardDSTJump(0, 0, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -318,6 +338,21 @@ }

if (!(date.hour in this.hour) && Object.keys(this.hour).length !== 24) {
date = date.set({
hour: date.hour === 23 && diff > 86400000 ? 0 : date.hour + 1
});
const expectedHour =
date.hour === 23 && diff > 86400000 ? 0 : date.hour + 1;
const expectedMinute = date.minute; // expect no change.
date = date.set({ hour: expectedHour });
date = date.set({ minute: 0, second: 0 });
// When this is the case, Asking luxon to go forward by 1 hour actually made us go forward by more hours...
// This indicates that somewhere between these two time points, a forward DST adjustment has happened.
// When this happens, the job should be scheduled to execute as though the time has come when the jump is made.
// Therefore, the job should be scheduled on the first tick after the forward jump.
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
// backwards jumps do not seem to have any problems (i.e. double activations),
// so they need not be handled in a similar way.
continue;

@@ -330,6 +365,17 @@ }

) {
date = date.set({
minute: date.minute === 59 && diff > 3600000 ? 0 : date.minute + 1
});
const expectedMinute =
date.minute === 59 && diff > 3600000 ? 0 : date.minute + 1;
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
date = date.set({ minute: expectedMinute });
date = date.set({ second: 0 });
// Same case as with hours: DST forward jump.
// This must be accounted for if a minute increment pushed us to a jumping point.
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -342,5 +388,16 @@ }

) {
date = date.set({
second: date.second === 59 && diff > 60000 ? 0 : date.second + 1
});
const expectedSecond =
date.second === 59 && diff > 60000 ? 0 : date.second + 1;
const expectedMinute = date.minute + (expectedSecond === 60);
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
date = date.set({ second: expectedSecond });
// Seconds can cause it too, imagine 21:59:59 -> 23:00:00.
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -350,3 +407,15 @@ }

if (date.toMillis() === firstDate) {
date = date.set({ second: date.second + 1 });
const expectedSecond = date.second + 1;
const expectedMinute = date.minute + (expectedSecond === 60);
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
date = date.set({ second: expectedSecond });
// Same as always.
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
const [done, newDate] = this._findPreviousDSTJump(date);
date = newDate;
if (done) break;
}
continue;

@@ -362,2 +431,237 @@ }

/**
* Search backwards in time 1 minute at a time, to detect a DST forward jump.
* When the jump is found, the range of the jump is investigated to check for acceptable cron times.
*
* A pair is returned, whose first is a boolean representing if an acceptable time was found inside the jump,
* and whose second is a DateTime representing the first millisecond after the jump.
*
* The input date is expected to be decently close to a DST jump.
* Up to a day in the past is checked before an error is thrown.
* @param date
* @return [boolean, DateTime]
*/
_findPreviousDSTJump: function (date) {
/** @type number */
let expectedMinute, expectedHour, actualMinute, actualHour;
/** @type DateTime */
let maybeJumpingPoint = date;
// representing one day of backwards checking. If this is hit, the input must be wrong.
const iterationLimit = 60 * 24;
let iteration = 0;
do {
if (++iteration > iterationLimit) {
throw new Error(
`ERROR: This DST checking related function assumes the input DateTime (${date.toISO()}) is within 24 hours of a DST jump.`
);
}
expectedMinute = maybeJumpingPoint.minute - 1;
expectedHour = maybeJumpingPoint.hour;
if (expectedMinute < 0) {
expectedMinute += 60;
expectedHour = (expectedHour + 24 - 1) % 24; // Subtract 1 hour, but we must account for the -1 case.
}
maybeJumpingPoint = maybeJumpingPoint.minus({ minute: 1 });
actualMinute = maybeJumpingPoint.minute;
actualHour = maybeJumpingPoint.hour;
} while (expectedMinute === actualMinute && expectedHour === actualHour);
// Setting the seconds and milliseconds to zero is necessary for two reasons:
// Firstly, the range checking function needs the earliest moment after the jump.
// Secondly, this DateTime may be used for scheduling jobs, if there existed a job in the skipped range.
const afterJumpingPoint = maybeJumpingPoint
.plus({ minute: 1 }) // back to the first minute _after_ the jump
.set({ seconds: 0, millisecond: 0 });
// Get the lower bound of the range to check as well. This only has to be accurate down to minutes.
const beforeJumpingPoint = afterJumpingPoint.minus({ second: 1 });
if (
date.month in this.month &&
date.day in this.dayOfMonth &&
date.getWeekDay() in this.dayOfWeek
) {
return [
this._checkTimeInSkippedRange(beforeJumpingPoint, afterJumpingPoint),
afterJumpingPoint
];
}
// no valid time in the range for sure, units that didn't change from the skip mismatch.
return [false, afterJumpingPoint];
},
/**
* Given 2 DateTimes, which represent 1 second before and immediately after a DST forward jump,
* checks if a time in the skipped range would have been a valid CronJob time.
*
* Could technically work with just one of these values, extracting the other by adding or subtracting seconds.
* However, this couples the input DateTime to actually being tied to a DST jump,
* which would make the function harder to test.
* This way the logic just tests a range of minutes and hours, regardless if there are skipped time points underneath.
*
* Assumes the DST jump started no earlier than 0:00 and jumped forward by at least 1 minute, to at most 23:59.
* i.e. The day is assumed constant, but the jump is not assumed to be an hour long.
* Empirically, it is almost always one hour, but very, very rarely 30 minutes.
*
* Assumes dayOfWeek, dayOfMonth and month match all match, so only the hours, minutes and seconds are to be checked.
* @param {DateTime} beforeJumpingPoint
* @param {DateTime} afterJumpingPoint
* @returns {boolean}
*/
_checkTimeInSkippedRange: function (beforeJumpingPoint, afterJumpingPoint) {
// start by getting the first minute & hour inside the skipped range.
const startingMinute = (beforeJumpingPoint.minute + 1) % 60;
const startingHour =
(beforeJumpingPoint.hour + (startingMinute === 0)) % 24;
const hourRangeSize = afterJumpingPoint.hour - startingHour + 1;
const isHourJump = startingMinute === 0 && afterJumpingPoint.minute === 0;
// There exist DST jumps other than 1 hour long, and the function is built to deal with it.
// It may be overkill to assume some cases, but it shouldn't cost much at runtime.
// https://en.wikipedia.org/wiki/Daylight_saving_time_by_country
if (hourRangeSize === 2 && isHourJump) {
// Exact 1 hour jump, most common real-world case.
// There is no need to check minutes and seconds, as any value would suffice.
return startingHour in this.hour;
} else if (hourRangeSize === 1) {
// less than 1 hour jump, rare but does exist.
return (
startingHour in this.hour &&
this._checkTimeInSkippedRangeSingleHour(
startingMinute,
afterJumpingPoint.minute
)
);
} else {
// non-round or multi-hour jump. (does not exist in the real world at the time of writing)
return this._checkTimeInSkippedRangeMultiHour(
startingHour,
startingMinute,
afterJumpingPoint.hour,
afterJumpingPoint.minute
);
}
},
/**
* Component of checking if a CronJob time existed in a DateTime range skipped by DST.
* This subroutine makes a further assumption that the skipped range is fully contained in one hour,
* and that all other larger units are valid for the job.
*
* for example a jump from 02:00:00 to 02:30:00, but not from 02:00:00 to 03:00:00.
* @see _checkTimeInSkippedRange
*
* This is done by checking if any minute in startMinute - endMinute is valid, excluding endMinute.
* For endMinute, there is only a match if the 0th second is a valid time.
*/
_checkTimeInSkippedRangeSingleHour: function (startMinute, endMinute) {
for (let minute = startMinute; minute < endMinute; ++minute) {
if (minute in this.minute) return true;
}
// Unless the very last second of the jump matched, there is no match.
return endMinute in this.minute && 0 in this.second;
},
/**
* Component of checking if a CronJob time existed in a DateTime range skipped by DST.
* This subroutine assumes the jump touches at least 2 hours, but the jump does not necessarily fully contain these hours.
*
* @see _checkTimeInSkippedRange
*
* This is done by defining the minutes to check for the first and last hour,
* and checking all 60 minutes for any hours in between them.
*
* If any hour x minute combination is a valid time, true is returned.
* The endMinute x endHour combination is only checked with the 0th second, since the rest would be out of the range.
*
* @param startHour {number}
* @param startMinute {number}
* @param endHour {number}
* @param endMinute {number}
*/
_checkTimeInSkippedRangeMultiHour: function (
startHour,
startMinute,
endHour,
endMinute
) {
if (startHour >= endHour) {
throw new Error(
`ERROR: This DST checking related function assumes the forward jump starting hour (${startHour}) is less than the end hour (${endHour})`
);
}
/** @type number[] */
const firstHourMinuteRange = Array.from(
{ length: 60 - startMinute },
(_, k) => startMinute + k
);
/** @type {number[]} The final minute is not contained on purpose. Every minute in this range represents one for which any second is valid. */
const lastHourMinuteRange = Array.from(
{ length: endMinute },
(_, k) => k
);
/** @type number[] */
const middleHourMinuteRange = Array.from({ length: 60 }, (_, k) => k);
/** @type (number) => number[] */
const selectRange = forHour => {
if (forHour === startHour) {
return firstHourMinuteRange;
} else if (forHour === endHour) {
return lastHourMinuteRange;
} else {
return middleHourMinuteRange;
}
};
// Include the endHour: Selecting the right range still ensures no values outside the skip are checked.
for (let hour = startHour; hour <= endHour; ++hour) {
if (!(hour in this.hour)) continue;
// The hour matches, so if the minute is in the range, we have a match!
const usingRange = selectRange(hour);
for (const minute of usingRange) {
// All minutes in any of the selected ranges represent minutes which are fully contained in the jump,
// So we need not check the seconds. If the minute is in there, it is a match.
if (minute in this.minute) return true;
}
}
// The endMinute of the endHour was not checked in the loop, because only the 0th second of it is in the range.
// Arriving here means no match was found yet, but this final check may turn up as a match.
return (
endHour in this.hour && endMinute in this.minute && 0 in this.second
);
},
/**
* Given expected and actual hours and minutes, report if a DST forward jump occurred.
*
* This is the case when the expected is smaller than the acutal.
*
* It is not sufficient to check only hours, because some parts of the world apply DST by shifting in minutes.
* Better to account for it by checking minutes too, before an Australian of Lord Howe Island call us.
* @param expectedHour
* @param expectedMinute
* @param {DateTime} actualDate
*/
_forwardDSTJump: function (expectedHour, expectedMinute, actualDate) {
const actualHour = actualDate.hour;
const actualMinute = actualDate.minute;
const hoursJumped = expectedHour % 24 < actualHour;
const minutesJumped = expectedMinute % 60 < actualMinute;
return hoursJumped || minutesJumped;
},
/**
* wildcard, or all params in array (for to string)

@@ -364,0 +668,0 @@ */

2

package.json
{
"name": "cron",
"description": "Cron jobs for your node",
"version": "2.2.0",
"version": "2.3.0",
"author": "Nick Campbell <nicholas.j.campbell@gmail.com> (https://github.com/ncb000gt)",

@@ -6,0 +6,0 @@ "bugs": {

@@ -1,6 +0,5 @@

# Looking for maintainers/contributors
## Looking for maintainers/contributors
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.
This project is looking for help! If you're interested in helping with the project please reach out to me (ncb000gt) on [Twitter](https://twitter.com/ncb000gt) and let me know. I'd love for it to continue on, but it needs a lot of attention. You can also join the [Discord server](https://discord.gg/yyKns29zch) to learn more about what needs to be done.
# node-cron

@@ -16,76 +15,52 @@

[![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)
[![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
typically done using the cron syntax. We allow you to execute a function
whenever your scheduled job triggers. We also allow you to execute a job
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,
leaving it off will default to 0 and match the Unix behavior.
Cron is a tool that allows you to execute _something_ on a schedule. This is typically done using the cron syntax. We allow you to execute a function whenever your scheduled job triggers. We also allow you to execute a job 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, leaving it off will default to 0 and match the Unix behavior.
# Installation
## Installation
npm install cron
```
npm install cron
```
# If You Are Submitting Bugs/Issues
## Versions and Backwards compatibility breaks:
Because we can't magically know what you are doing to expose an issue, it is
best if you provide a snippet of code. This snippet need not include your secret
sauce, but it must replicate the issue you are describing. The issues that get
closed without resolution tend to be the ones without code examples. Thanks.
As goes with semver, breaking backwards compatibility should be explicit in the versioning of your library. As such, we'll upgrade the version of this module in accordance with breaking changes (I'm not always great about doing it this way so if you notice that there are breaking changes that haven't been bumped appropriately please let me know).
# Versions and Backwards compatibility breaks:
## Usage (basic cron usage):
As goes with semver, breaking backwards compatibility should be explicit in the
versioning of your library. As such, we'll upgrade the version of this module
in accordance with breaking changes (I'm not always great about doing it this
way so if you notice that there are breaking changes that haven't been bumped
appropriately please let me know).
# 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'
'* * * * * *',
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 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.
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.
There are more examples available in this repository at:
[/examples](https://github.com/kelektiv/node-cron/tree/master/examples)
There are more examples available in this repository at: [/examples](https://github.com/kelektiv/node-cron/tree/master/examples)
# Available Cron patterns:
## Available Cron patterns:
Asterisk. E.g. *
Ranges. E.g. 1-3,5
Steps. E.g. */2
```
Asterisks e.g. *
Ranges e.g. 1-3,5
Steps e.g. */2
```
[Read up on cron patterns here](http://crontab.org). Note the examples in the
link have five fields, and 1 minute as the finest granularity, but this library
has six fields, with 1 second as the finest granularity.
[Read up on cron patterns here](http://crontab.org). Note the examples in the link have five fields, and 1 minute as the finest granularity, but this library has six fields, with 1 second as the finest granularity.
There are tools that help when constructing your cronjobs. You might find
something like https://crontab.guru/ or https://cronjob.xyz/ helpful. But,
note that these don't necessarily accept the exact same syntax as this
library, for instance, it doesn't accept the `seconds` field, so keep that in
mind.
There are tools that help when constructing your cronjobs. You might find something like https://crontab.guru/ or https://cronjob.xyz/ helpful. But, note that these don't necessarily accept the exact same syntax as this library, for instance, it doesn't accept the `seconds` field, so keep that in mind.
# 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.
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.

@@ -97,27 +72,12 @@ - Seconds: 0-59

- Months: 0-11 (Jan-Dec)
- Day of Week: 0-6 (Sun-Sat)
- 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 `Date` or Luxon `DateTime` 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 Luxon `DateTime` 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 issue [GH-47](https://github.com/kelektiv/node-cron/issues/47#issuecomment-459762775)
# API
## API
Parameter Based
Parameter Based

@@ -129,65 +89,41 @@ - `job` - shortcut to `new cron.CronJob()`.

- `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.
- `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. This should be an integer amount representing the number of minutes offset (like `120` for +2 hours or `-90` for -1.5 hours) 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.
- `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
## Community
This is a community effort project. In the truest sense, this project started as
an open source project from [cron.js](http://github.com/padolsey/cron.js) and
grew into something else. Other people have contributed code, time, and
oversight to the project. At this point there are too many to name here so I'll
just say thanks.
Join the [Discord server](https://discord.gg/yyKns29zch)! Here you can discuss issues and get help in a more casual forum than GitHub.
# License
## Contributing
MIT
### Submitting Bugs/Issues
Before submitting a bug, please search the existing issues, [Discord](https://discord.gg/yyKns29zch) conversations, and the web to see if someone else has run into the same issue before.
Because we can't magically know what you are doing to expose an issue, it is best if you provide a snippet of code. This snippet need not include your secret sauce, but it must replicate the issue you are describing. The issues that get closed without resolution tend to be the ones without code examples. Thanks.
### Acknowledgements
This is a community effort project. In the truest sense, this project started as an open source project from [cron.js](http://github.com/padolsey/cron.js) and grew into something else. Other people have contributed code, time, and oversight to the project. At this point there are too many to name here so I'll just say thanks.
## License
MIT

@@ -295,3 +295,17 @@ /* eslint-disable no-new */

it('should generete the right next days when cron is set to every minute', () => {
it('should expose _getNextDateFrom as a public function', () => {
const cronTime = new cron.CronTime('0 */10 * * * *');
cronTime._getNextDateFrom = jest.fn();
const testDate = new Date('2018-08-10T02:19:59.999Z');
const testTimezone = 'Asia/Amman';
cronTime.getNextDateFrom(testDate, testTimezone);
expect(cronTime._getNextDateFrom).toHaveBeenCalledWith(
testDate,
testTimezone
);
});
it('should generate the right next days when cron is set to every minute', () => {
const cronTime = new cron.CronTime('* * * * *');

@@ -307,3 +321,3 @@ const min = 60000;

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

@@ -351,4 +365,161 @@ const min = 60000 * 15;

});
it('should generate the right N next days for * * * * *', () => {
it('Should schedule jobs inside time zone changes that shifts time forward to the end of the shift, for weekly jobs', () => {
let currentDate = luxon.DateTime.fromISO('2018-03-29T23:15', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('30 0 * * 5'); // the next 0:30 is March 30th, but it will jump from 0:00 to 1:00.
let nextDate = cronTime._getNextDateFrom(currentDate, 'Asia/Amman');
expect(nextDate - currentDate).toEqual(1000 * 60 * 45); // 45 minutes is 30T00:00, which jumps to 1:00 which is past the trigger of 0:30.
// the next one should just be at 0:30 again. i.e. a week minus 30 minutes.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(3600000 * 24 * 7 - 60000 * 30);
// the next one is again at 0:30, but now we're 'back to normal' with weekly offsets.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 3600 * 24 * 7);
});
it('Should schedule jobs inside time zone changes that shifts the time forward to the end of the shift, for daily jobs', () => {
let currentDate = luxon.DateTime.fromISO('2018-03-29T23:45', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('30 0 * * *'); // the next 0:30 is March 30th, but it will jump from 0:00 to 1:00.
let nextDate = cronTime._getNextDateFrom(currentDate, 'Asia/Amman');
expect(nextDate - currentDate).toEqual(1000 * 60 * 15); // 15 minutes is 30T00:00, which jumps to 1:00 which is past the trigger of 0:30.
// the next one is tomorrow at 0:30, so 23h30m.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 3600 * 24 - 1000 * 60 * 30);
// back to normal.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 3600 * 24);
});
it('Should schedule jobs inside time zone changes that shifts the time forward to the end of the shift, for hourly jobs', () => {
let currentDate = luxon.DateTime.fromISO('2018-03-29T23:45', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('30 * * * *'); // the next 0:30 is March 30th, but it will jump from 0:00 to 1:00.
let nextDate = cronTime._getNextDateFrom(currentDate, 'Asia/Amman');
expect(nextDate - currentDate).toEqual(1000 * 60 * 15); // 15 minutes is 30T00:00, which jumps to 1:00 which is past the trigger of 0:30.
// the next one is at 1:30, so 30m.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 60 * 30);
// back to normal.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 3600);
});
it('Should schedule jobs inside time zone changes that shifts the time forward to the end of the shift, for minutely jobs', () => {
let currentDate = luxon.DateTime.fromISO('2018-03-29T23:59', {
zone: 'Asia/Amman'
});
const cronTime = new cron.CronTime('* * * * *'); // the next minute is 0:00 is March 30th, but it will jump from 0:00 to 1:00.
let nextDate = cronTime._getNextDateFrom(currentDate, 'Asia/Amman');
expect(nextDate - currentDate).toEqual(1000 * 60);
// the next one is at 1:01:00, this should still be 60 seconds in the future.
currentDate = nextDate;
nextDate = cronTime._getNextDateFrom(currentDate);
expect(nextDate - currentDate).toEqual(1000 * 60);
});
// Do not think a similar test for secondly job is necessary, the minutely one already ensured no double hits in the overlap zone.
it('Should throw when dates that are not within 24 hours of DST jumps are checked for past DST jumps', () => {
const cronTime = new cron.CronTime('* * * * *');
const tryFindPastDST = isoTime => () => {
const maybeBadDate = luxon.DateTime.fromISO(isoTime, {
zone: 'Asia/Amman'
});
expect(maybeBadDate.invalid).toEqual(null);
cronTime._findPreviousDSTJump(maybeBadDate);
};
// This timezone jumps from 0:00 to 1:00 on March 30th, so the cutoff is March 31st 1:00:00
expect(tryFindPastDST('2018-03-31T00:59:00')).not.toThrow();
expect(tryFindPastDST('2018-03-31T01:00:00')).toThrow();
});
// The following few DST related tests do not need specific dates that are actually DST,
// the functions they are calling assume the given parameters encapsulate a DST jump,
// and use the raw hour and minute data to check it from there.
it('Should correctly scan time periods as if they are DST jumps, half hour jumps', () => {
let endDate = luxon.DateTime.fromISO('2023-01-01T16:00:00.000', {
zone: 'Europe/Amsterdam'
});
let startDate = endDate.minus({ minute: 30, second: 1 });
const cronTime = new cron.CronTime('5 16 * * *'); // at 16:05:00.
let jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
endDate = endDate.plus({ minute: 30 }); // 16:30:00
startDate = endDate.minus({ minute: 30, second: 1 }); // 15:59:59
jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(true);
});
it('Should not include seconds in the minute after the DST jump as part of the jump scan', () => {
const endDate = luxon.DateTime.fromISO('2023-01-01T16:00:00.000', {
zone: 'Europe/Amsterdam'
});
// 1 hour jump case
let startDate = endDate.minus({ hour: 1, second: 1 });
const cronTime = new cron.CronTime('1 5 16 * * *'); // at 16:00:01.
let jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
// 'quirky' jump case
startDate = endDate.minus({ hour: 1, minute: 45, second: 1 });
jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
});
it('Should correctly scan time periods as if they are DST jumps, full hour jumps', () => {
let endDate = luxon.DateTime.fromISO('2023-01-01T16:00:00.000', {
zone: 'Europe/Amsterdam'
});
let startDate = endDate.minus({ hour: 1, second: 1 });
const cronTime = new cron.CronTime('5 16 * * *'); // at 16:05:00.
let jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
endDate = endDate.plus({ hour: 1 }); // 17:00:00
startDate = endDate.minus({ hour: 1, second: 1 }); // 15:59:59
jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(true);
});
// A 'quirky' DST jump is one that should not break the implementation, but does not exist in real life (yet)
it('Should correctly scan time periods as if they are DST jumps, quirky jumps (1)', () => {
// Testing a jump that is less than an hour long, but wraps around an hour.
let endDate = luxon.DateTime.fromISO('2023-01-01T16:15:00.000', {
zone: 'Europe/Amsterdam'
});
let startDate = endDate.minus({ minute: 45, second: 1 }); // 15:29:59
const cronTime = new cron.CronTime('30 16 * * *'); // at 16:30:00.
let jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
endDate = endDate.plus({ minute: 30 }); // 16:45:00
startDate = endDate.minus({ minute: 50, second: 1 }); // 15:54:59
jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(true);
});
it('Should correctly scan time periods as if they are DST jumps, quirky jumps (2)', () => {
// Testing a jump that is over an hour long.
let endDate = luxon.DateTime.fromISO('2023-01-01T16:15:00.000', {
zone: 'Europe/Amsterdam'
});
let startDate = endDate.minus({ hour: 3, minute: 45, second: 1 }); // 12:29:59
const cronTime = new cron.CronTime('30 16 * * *'); // at 16:30:00.
let jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(false);
endDate = endDate.plus({ minute: 30 }); // 16:45:00
startDate = endDate.minus({ hour: 3, minute: 45, second: 1 }); // 12:59:59
jobInRange = cronTime._checkTimeInSkippedRange(startDate, endDate);
expect(jobInRange).toEqual(true);
});
it('Enforces the hour difference assumption for handling multi-hour DST jumps', () => {
const cronTime = new cron.CronTime('30 16 * * *');
expect(() => {
cronTime._checkTimeInSkippedRangeMultiHour(15, 0, 15, 30);
}).toThrow();
});
it('should generate the right N next days for * * * * *', () => {
const cronTime = new cron.CronTime('* * * * *');
let currentDate = luxon.DateTime.local().set({ second: 0, millisecond: 0 });

@@ -400,3 +571,3 @@ for (let i = 0; i < 100; i++) {

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

@@ -403,0 +574,0 @@ const previousDate = new Date(Date.UTC(2018, 3, 0, 0, 0));

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc