Git Product home page Git Product logo

cron-schedule's Introduction

cron-schedule CircleCI

A zero-dependency cron parser and scheduler for Node.js, Deno and the browser. Published on NPM and JSR

ts semantic-release

Features

  • Parse cron expressions.
  • Get next or previous schedules from a specific starting date.
  • Check if a date matches a cron expression.
  • Schedule a function call based on a cron expression.
  • Supports Node.js, Deno and the browser (ESM only)
  • Lightweight and tree-shakeable.

Installation and usage

Node.js

Via npm:

$ npm install cron-schedule

Via yarn:

$ yarn add cron-schedule

Via pnpm:

$ pnpm add cron-schedule

We test our code against the following Node.js releases (18.20, 20.12). Other versions of node.js may also work, but this is not tested.

Usage
import { parseCronExpression } from 'cron-schedule'
const cron = parseCronExpression('*/5 * * * *')
console.log(cron.getNextDate(new Date(2020, 10, 20, 18, 32)))
// 2020-11-20T17:35:00.000Z

Browser via CDN

<script type="module">
  import { parseCronExpression } from 'https://cdn.skypack.dev/cron-schedule@:version'
  const cron = parseCronExpression('*/5 * * * *')
  console.log(cron.getNextDate(new Date(2020, 10, 20, 18, 32)))
  // 2020-11-20T17:35:00.000Z
</script>

The examples above uses Skypack.

Deno

import { parseCronExpression } from 'npm:cron-schedule@:version'
const cron = parseCronExpression('*/5 * * * *')
console.log(cron.getNextDate(new Date(2020, 10, 20, 18, 32)))
// 2020-11-20T17:35:00.000Z

Alternatively you can use deno add @p4sca1/cron-schedule and import from @p4sca1/cron-schedule, or import from jsr:@p4sca1/cron-schedule without an install step.

Note on :version placeholder

The deno import and Skypack CDN urls contain a :version placeholder. Replace :version with the desired version. Semver ranges are supported. To always use the latest 4.x version use ^4.0.0. See https://www.npmjs.com/package/cron-schedule for a list of available versions.

Work with cron expressions

// Import method to parse a cron expression.
import { parseCronExpression } from 'cron-schedule'

// Parse a cron expression to return a Cron instance.
const cron = parseCronExpression('*/5 * * * *')

// Get the next date starting from the given start date or now.
cron.getNextDate(startDate?: Date): Date

// Get the specified amount of future dates starting from the given start date or now.
cron.getNextDates(amount: number, startDate?: Date): Date[]

// Get an ES6 compatible iterator which iterates over the next dates starting from startDate or now.
// The iterator runs until the optional endDate is reached or forever.
// The advantage of an iterator is that you can get more further dates on demand by using iterator.next().
cron.getNextDatesIterator(startDate: Date = new Date(), endDate?: Date): Generator<Date, undefined, undefined>

// Get the previou date starting from the given start date or now.
cron.getPrevDate(startDate: Date = new Date()): Date

// Get the specified amount of previous dates starting from the given start date or now.
cron.getPrevDates(amount: number, startDate?: Date): Date[]

// Get an ES6 compatible iterator which iterates over the previous dates starting from startDate or now.
// The iterator runs until the optional endDate is reached or forever.
// The advantage of an iterator is that you can get more previous dates on demand by using iterator.next().
cron.getPrevDatesIterator(startDate: Date = new Date(), endDate?: Date): Generator<Date, undefined, undefined>

// Check whether there is a cron date at the given date.
cron.matchDate(date: Date): boolean

Schedule tasks based on cron expressions

You can schedule tasks to be executed based on a cron expression. cron-schedule comes with 2 different schedulers.

If you use TypeScript, make sure you set comilerOptions.nodeResolution to node16 or nodenext.

1. Timer based scheduler

The timer based cron scheduler creates one timer for every scheduled cron. When the node timeout limit of ~24 days would be exceeded, it uses multiple consecutive timeouts.

// Import the scheduler.
import { TimerBasedCronScheduler as scheduler } from 'cron-schedule/schedulers/timer-based.js'

// Create a timeout, which fill fire the task on the next cron date.
// An optional errorHandler can be provided, which is called when the task throws an error or returns a promise that gets rejected.
// Returns a handle which can be used to clear the timeout using clearTimeoutOrInterval.
scheduler.setTimeout(cron: Cron, task: () => unknown, opts?: { errorHandler?: (err: Error) => unknown }): ITimerHandle

// Create an interval, which will fire the given task on every future cron date.
// This uses consecutive calls to scheduler.setTimeout under the hood.
// An optional errorHandler can be provided, which is called when the task throws an error or returns a promise that gets rejected.
// The task remains scheduled when an error occurs.
// Returns a handle which can be used to clear the timeout using clearTimeoutOrInterval.
scheduler.setInterval(cron: Cron, task: () => unknown, opts?: { errorHandler?: (err: Error) => unknown }): ITimerHandle

// Clear a timeout or interval, making sure that the task will no longer execute.
scheduler.clearTimeoutOrInterval(handle: ITimerHandle): void

Pros:

  • A task is scheduled exactly to the second of the next cron date.

Cons:

  • There is one timer per task, which could lead to lower performance compared to the interval based scheduler if you have many scheduled tasks.

2. Interval based scheduler

The interval based scheduler checks for due task in a fixed interval. So there is only one interval for all tasks assigned to a scheduler. You can have multiple instances of an interval based scheduler.

// Import the scheduler.
import { IntervalBasedCronScheduler } from 'cron-schedule/schedulers/interval-based.js'

// Instantiate a new instance of the scheduler with the given interval. In this example, the scheduler would check every 60 seconds.
const scheduler = new IntervalBasedCronScheduler(60 * 1000)

// Register a new task that will be executed on every future cron date, or only on the next cron date if isOneTimeTask is true.
// An optional errorHandler can be provided, which is called when the task throws an error or returns a promise that gets rejected.
// The task remains scheduled when an error occurs (if not a one time task). Tasks are at max executed only once per interval.
// Returns an id to be used with unregisterTask.
scheduler.registerTask(cron: Cron, task: () => unknown, opts?: { isOneTimeTask?: boolean, errorHandler?: (err: Error) => unknown }): number

// Unregister a task causing it to no longer be executed.
scheduler.unregisterTask(id: number): void

// You can stop the scheduler, which clears the interval.
scheduler.stop()

// You can start the scheduler after stopping it again. A newly created scheduler is started by default.
// Tasks that were due while the scheduler was stopped will be executed on the next interval tick (but only a single time).
scheduler.start()

Pros:

  • Only one interval for all tasks, which is quite performant.

Cons:

  • Tasks are not executed exactly on the cron date.
  • Tasks can only be executed once per interval.

For most people, the timer based scheduler should be a good option. However, setTimeout can be unreliable for long delays. When you have problems with long timeouts / intervals being skipped, or have performance problems because of many scheduled tasks, you should consider the interval based scheduler.

Cron expression format

cron_schedule uses the linux cron syntax as described here with the addition that you can optionally specify seconds by prepending the minute field with another field.

┌───────────── second (0 - 59, optional)
│ ┌───────────── minute (0 - 59)
│ │ ┌───────────── hour (0 - 23)
│ │ │ ┌───────────── day of month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12)
│ │ │ │ │ ┌───────────── weekday (0 - 7)
* * * * * *

All linux cron features are supported, including

  • lists
  • ranges
  • ranges in lists
  • step values
  • month names (jan,feb,... - case insensitive)
  • weekday names (mon,tue,... - case insensitive)
  • time nicknames (@yearly, @annually, @monthly, @weekly, @daily, @hourly - case insensitive)

For simple timing tasks like every x seconds, you should consider using setInterval which is more suitable for simple timing tasks, as it does not have the calculation overhead.

Cron validation

Looking for a way to validate cron expressions in your backend (node.js) or in the browser with support for multiple presets? Check out cron-validate!

Use the npm-cron-schedule preset to validate that cron expressions are supported by cron-schedule.

cron-schedule's People

Contributors

airfooox avatar buffcode avatar p4sca1 avatar renovate-bot avatar renovate[bot] avatar renzo-s avatar semantic-release-bot avatar sky0hunter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

cron-schedule's Issues

Cron Job Task Issue

Hi @P4sca1
It seems the task of the scheduler runs twice when the cron time is due. Is there anything i am doing wrong?

function job(){
 console.log('run')
}

const cron = parseCronExpression('*/15 * * * *');

scheduler.setInterval(cron, job);

When i run the code above it executes the function job twice every 15 minutes

Incorrect values returned for getPrevDate

Hi,
When running this code:

let schedule = parseCronExpression('0 10 * * 3')
wedSched.getPrevDate(new Date('2024-05-01T09:00:00'))

expected result is: 2024-04-24T00:00:00.000Z
actual result is: 2024-03-27T00:00:00.000Z

Node: 20.12.2

Error handling for scheduled tasks

Timer based scheduler:
When a task throws an error at one time, it will no longer execute in the future as it is not rescheduled.

Interval based scheduler:
An error in one task causes all other tasks scheduled to be executed at the same time to not execute.

Instead we should catch errors, call some kind of error callback (optional) and continue as normal.

Confusion on Scheduling a Cron Task

Hi,
I don't see in the Readme how to schedule a task that repeats based on a CRON string. I tried to schedule a task to run every 5 seconds. But it doesn't really work. It runs once only. It says there are only two schedulers , timeout and interval. But I don't want either, I want a schedular to run a task continously based on a cron string. Just like cron. What's the secret?

import { parseCronExpression, TimerBasedCronScheduler as scheduler } from 'cron-schedule'
const cron = parseCronExpression('*/5 * * * *')

scheduler.setTimeout(cron, () => { // This doesn't feel right, but also only runs once
console.log(new Date(), 'TASK EXECUTED')
})

Issue. with Quarterly Cron Schedules

Hi

This may be a problem with one of your dependancies, not the project itself, as https://crontab.guru seems to have the same problem.

However a sample of the issue.

Start Date of a cron is: 16th May 2021
Cron Expression is: 0 0 16 */3 *

The next list of dates are.
next at 2021-07-16 00:00:00
then at 2021-10-16 00:00:00
then at 2022-01-16 00:00:00
then at 2022-04-16 00:00:00
then at 2022-07-16 00:00:00

This is wrong, it should be August, November, February etc.

If you switch it to 2 or 4, you get correct list of dates.

Regards
Charles

Incomplete parsing of ranges (n-n, n-[n+1])

Given the following input:

parseCronExpression('* 1-2 * * *');

the following exception is thrown:

Failed to parse 1-2: Invalid range (start: 1, end: 2).

Though of course it can be rewritten to 1,2 this libraries claims to conform to the linux cron whose man page states:

Ranges are two numbers separated with a hyphen. The specified range is inclusive. [...]
The first number must be less than or equal to the second one.
(https://man7.org/linux/man-pages/man5/crontab.5.html)

The following input should be allowed (but is not at the moment):

  • 1-1
  • 1-2

Cron.matchDate returns always true when using weekdays and days of month

Because of this line in matchDate: (this.days.indexOf(day) !== -1 || this.weekdays.indexOf(weekday) !== -1) an expression like * * * * * 0,6 (Only on Saturday's or Sunday's) will always return true since indexOf(day) will always return true. I believe correct would be using logical AND instead of OR.

Thanks for the package, just what I was looking for. This behaviour is not hard to overwrite, just letting you know 👍

Ref:

(this.days.indexOf(day) !== -1 || this.weekdays.indexOf(weekday) !== -1)

Type definitions can fail to load when loading in a 'esmodule-mode' project

Using this library when Typescript is configured with moduleResolution: "NodeNext", type definitions can fail to load. This is resolved by including the type definitions in the exports map of package.json.

"exports": {
"require": "./dist/cron-schedule.cjs.min.js",
"import": "./dist/cron-schedule.min.mjs"
},

The following patch fixes it:

{
...
  "exports": {
    "require": "./dist/cron-schedule.cjs.min.js",
-   "import": "./dist/cron-schedule.min.mjs"
+   "import": "./dist/cron-schedule.min.mjs",
+   "types": "./dist/index.d.ts"
  },
...
}

Love your library!

Cron iterator

Have a cron iterator method which returns an interator that can get next and prev dates using .prev and .next.

es2022 vs es2022 targeting?

One thing I noticed, while looking at v4.0.0-next.3, in the tsconfig is that you are targeting es2022, though I am wondering whether targeting es2020 would be better, given that the code is meant to support running in browser?

Safari for example seems to not be quite es2022 compliant: https://caniuse.com/?search=ES13

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

circleci
.circleci/config.yml
  • node 5.2.0
  • cimg/node 20.12
github-actions
.github/workflows/codeql-analysis.yml
  • actions/checkout v4
  • github/codeql-action v3
  • github/codeql-action v3
  • github/codeql-action v3
npm
package.json
  • @biomejs/biome 1.7.1
  • @semantic-release/changelog 6.0.3
  • @semantic-release/git 10.0.1
  • @types/node 20.12.7
  • rimraf 5.0.5
  • semantic-release 23.0.8
  • typescript 5.4.5
  • vitest 1.5.2
  • node >=18
  • pnpm 9.0.6+sha256.0624e30eff866cdeb363b15061bdb7fd9425b17bc1bb42c22f5f4efdea21f6b3

  • Check this box to trigger a request for Renovate to run again on this repository

Error on every weekday

code:

  const cron = parseCronExpression('0 12 10 ? * THU *');
  console.log("next date: " + cron.getNextDate());

error:

  Error: Invalid cron expression: expected 5 or 6 elements.
  at K (C:\Dev\otros\nautioPupeter\node_modules\cron-schedule\src\cron-parser.ts:178:11)
  at Object.<anonymous> (C:\Dev\otros\nautioPupeter\index.ts:125:33)
  at Module._compile (node:internal/modules/cjs/loader:1101:14)
  at Module.m._compile (C:\Dev\otros\nautioPupeter\node_modules\ts-node\src\index.ts:1371:23)
  at Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
  at Object.require.extensions.<computed> [as .ts] (C:\Dev\otros\nautioPupeter\node_modules\ts-node\src\index.ts:1374:12)
  at Module.load (node:internal/modules/cjs/loader:981:32)
  at Function.Module._load (node:internal/modules/cjs/loader:822:12)
  at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
  at main (C:\Dev\otros\nautioPupeter\node_modules\ts-node\src\bin.ts:331:12)

Cron Schedule Manager

Do you think an optional manager could be implemented? Such as this one (https://github.com/cfurst/CronJobManager), which is aimed at the cron package?

I think the implementation could be really simple and it would improve a lot with environments with multiple cron jobs.

I can help if you need.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here are some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two Factor Authentication for your account, set its level to "Authorization only" in your account settings. semantic-release cannot publish with the default "
Authorization and writes" level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot 📦🚀

this package could not be loaded into a non-babel, native esm node.js environment

this package could not be loaded into a non-babel, native esm node.js environment

this appears in the process log,

(node:6368) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/path/to/my/project/node_modules/cron-schedule/dist/cron-schedule.esm.min.js:1

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Build issue for scheduler import, with Vite

The documentation indicates the following for the import for the scheduler:

import { IntervalBasedCronScheduler as scheduler } from 'cron-schedule/schedulers/interval-based.js'

Though when I do this in VS Code it says it cant' find it. The following works though, but then breaks in execution of the app:

import { IntervalBasedCronScheduler as scheduler } from 'cron-schedule/dist/schedulers/interval-based.js'

Originally I though this was a documentation issue, but now I am wondering whether the packaging has some issues, since if I use cron-schedule/schedulers/interval-based.js dev runtime works, build build time fails.

Error output:

src/views/MagicScreen.vue:45:54 - error TS2307: Cannot find module 'cron-schedule/schedulers/timer-based.js' or its corresponding type declarations.

45 import { TimerBasedCronScheduler as scheduler } from 'cron-schedule/schedulers/timer-based.js';
                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

My import is presented as follows in my Vue file:

import { parseCronExpression } from 'cron-schedule';
import { TimerBasedCronScheduler as scheduler } from 'cron-schedule/schedulers/timer-based.js';

I am using Vite 3.2.3 for packaging.

Looking at issue #284, but yarn add [email protected] -S didn't change anything.

Weekday range to sunday edge case

Heyho,

there is a little bug when using a range for weekdays which includes sunday as '7', due to the replacement with '0'.

I.e.: 5 10 * * 6-7 will throw Failed to parse 6-7: Invalid range (start: 6, end: 0).

year support is needed

got error for cron expression '* * * * * ? *'
Invalid cron expression: expected 5 or 6 elements.
spring scheduler can support that well

Add timezone support

At the moment, the cron jobs are checked against the local time.
When constructing a Schedule an optional timezone parameter could be used.
parseCronExpression would then get a second parameter timezone which is passed to the Schedule constructor.
For ease of use, it would be great if time zone names would be supported, but we would need to include and maintain a database of timezones. Also time zone offsets are not static but change over time and throughout the year. Maybe the timezone parameter should be a function utcOffset that returns the desired utc offset. One could then use other libraries such as moment-timezone to get the desired offset and cron-schedule bundle size would not increased (or use a peer dependency?). We also need to consider what happens when utcOffset returns diifferent offsets in subsequent calls (which might be realistic due to summer time / DST).

Seconds are rounded to 0

Hi,

It seems I run into some issue or I am doing something wrong.

I have the following code:

var cron = require('cron-schedule')
const time = cron.parseCronExpression('*/2 * * * *')
console.log(time.getNextDate(new Date()))

This cron should give me 2 minutes after the current date, but somehow the seconds are always rounded to 0. Why is that?
It doesn't take into calculation seconds at all. My guess if you set the date to 2021-01-10 18:12:35.293 you should get 2021-01-10 18:14:35.293 but instead you get the following (seconds rounded to 0).

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.