Giter Club home page Giter Club logo

croner's Introduction

Hi, I'm Hexagon!

A selection of my work:

  • My blog at hexagon.56k.guru, where i write about JavaScript, my librares and contributions and more.

  • Croner - JavaScript cron parser/scheduler. ESM. Node, Deno, Bun, Browser. No dependencies.

  • Croner-rust - Rust cron parser/scheduler. Fully featured, performant and minimalistic.

  • Pup - Fully featured process manager, written in typescript with love for Deno.

  • Lumocs - Easy to use static site generator for documentation, utilizing Lume and Deno.

  • Base64 - Base64 and Base64url to string or arraybuffer. ESM. Node, Deno, Bun, Browser. No dependencies.

  • Minitz - Minimal timezone conversion library for JavaScript, written for Croner, but now it's own package. No dependencies.

  • Entsoe-api-client - Unofficial ENTSO-e REST API Client for Deno and Node.

  • Bundlee - Static file bundler for web frameworks supported by Deno (and usable on cli).

  • Proper-tags - Modernised (pure ESM) version of the popular package common-tags.

  • DETRIS - Server side, web based multiplayer Tetris-like game written in Deno using Deno KV as persistent storage.

  • (etc...)

Other than working on my own projects, I often find myself converting existing packages from CommonJS to Deno/Node/Bun ESM.

🍔 Support My Work:

I'm fueled by coffee and burgers! If you've benefited from my work or believe in supporting open-source, consider backing me on GitHub Sponsors.

croner's People

Contributors

egfx-notifications avatar enzom-uy avatar gilles-crealp avatar hexagon avatar hophamlam avatar iisalazar avatar lorrding avatar maricn avatar mceachen avatar mryellow avatar mzpl avatar rsk2 avatar unkelpehr 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

croner's Issues

New feature: Option for legacy dom-OR-dow

By default, croner work differently from the standard refered to by @vixie. If you specify both day-of-month and day-of-week ( 0 0 1 * MON) croner would only run when there is a monday at the 1st of any month. This enable you to set patterns that run on the first monday of any month (0 0 1-7 * MON) and other clever stuff.

In classic cron (Vixie, for example), day-of-month and day-of-week are ORed, which makes 0 0 1 * MON run at the 1st of any month AND at mondays.

	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
	 * like many bizarre things, it's the standard.
	 */

Croner should support this with options, maybe

{ 
   legacyMode: true
}

Day skipping fix breaks calculation on DST start

I was trying to figure out what changes have been made to croner before updating to the current release. I read f8b2d3e and got the impression that you rely on the difference between the local timezone and the provided one to get the next valid time. Assuming that I understood this correctly I figured that there would still be an issue if the local and target timezone start and end daylight saving time at the same time or if you just explicitly state your local timezone.
To verify my assumption I wrote the following test:

	test("0 30 2 * * * with 365 iterations should return 365 days from now in Europe/Berlin", function () {
		let scheduler = new Cron("0 30 2 * * *", { timezone: "Europe/Berlin" }),
			prevRun = new Date(),
			nextRun,
			iterations = 365,
			compareDay = new Date();
			
		compareDay.setDate(compareDay.getDate() + iterations );
		
		while(iterations-->0) {
			nextRun = scheduler.next(prevRun),
			prevRun = nextRun;
			console.log(nextRun.toLocaleString("en-US"));
		}

		// Set seconds, minutes and hours to 00:00:00
		compareDay.setMilliseconds(0);
		compareDay.setSeconds(0);
		compareDay.setMinutes(0);
		compareDay.setHours(0);

		// Do comparison
		assert.equal(Math.abs(nextRun.getTime()-compareDay.getTime())<13*60*60*1000, true);

	});

And I also ran this with 0 30 2 * * * and Europe/Budapest as well as 0 30 1 * * * and Europe/London. Since I'm based in Germany all of those tests failed for me, to reproduce you'd have to find a timezone matching your local one and specify the correct time that would be skipped on a DST change.

The tests produce the following output at croner v4.4.0

~/p/croner ((4.4.0)) > npm run test

> [email protected] test
> uvu test test.croner.js

node/js/test.croner.js
• • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • 10/27/2022, 2:30:00 AM
10/28/2022, 2:30:00 AM
10/29/2022, 2:30:00 AM
10/30/2022, 2:30:00 AM
10/31/2022, 2:30:00 AM
11/1/2022, 2:30:00 AM
11/2/2022, 2:30:00 AM
11/3/2022, 2:30:00 AM
11/4/2022, 2:30:00 AM
11/5/2022, 2:30:00 AM
11/6/2022, 2:30:00 AM
11/7/2022, 2:30:00 AM
11/8/2022, 2:30:00 AM
11/9/2022, 2:30:00 AM
11/10/2022, 2:30:00 AM
11/11/2022, 2:30:00 AM
11/12/2022, 2:30:00 AM
11/13/2022, 2:30:00 AM
11/14/2022, 2:30:00 AM
11/15/2022, 2:30:00 AM
11/16/2022, 2:30:00 AM
11/17/2022, 2:30:00 AM
11/18/2022, 2:30:00 AM
11/19/2022, 2:30:00 AM
11/20/2022, 2:30:00 AM
11/21/2022, 2:30:00 AM
11/22/2022, 2:30:00 AM
11/23/2022, 2:30:00 AM
11/24/2022, 2:30:00 AM
11/25/2022, 2:30:00 AM
11/26/2022, 2:30:00 AM
11/27/2022, 2:30:00 AM
11/28/2022, 2:30:00 AM
11/29/2022, 2:30:00 AM
11/30/2022, 2:30:00 AM
12/1/2022, 2:30:00 AM
12/2/2022, 2:30:00 AM
12/3/2022, 2:30:00 AM
12/4/2022, 2:30:00 AM
12/5/2022, 2:30:00 AM
12/6/2022, 2:30:00 AM
12/7/2022, 2:30:00 AM
12/8/2022, 2:30:00 AM
12/9/2022, 2:30:00 AM
12/10/2022, 2:30:00 AM
12/11/2022, 2:30:00 AM
12/12/2022, 2:30:00 AM
12/13/2022, 2:30:00 AM
12/14/2022, 2:30:00 AM
12/15/2022, 2:30:00 AM
12/16/2022, 2:30:00 AM
12/17/2022, 2:30:00 AM
12/18/2022, 2:30:00 AM
12/19/2022, 2:30:00 AM
12/20/2022, 2:30:00 AM
12/21/2022, 2:30:00 AM
12/22/2022, 2:30:00 AM
12/23/2022, 2:30:00 AM
12/24/2022, 2:30:00 AM
12/25/2022, 2:30:00 AM
12/26/2022, 2:30:00 AM
12/27/2022, 2:30:00 AM
12/28/2022, 2:30:00 AM
12/29/2022, 2:30:00 AM
12/30/2022, 2:30:00 AM
12/31/2022, 2:30:00 AM
1/1/2023, 2:30:00 AM
1/2/2023, 2:30:00 AM
1/3/2023, 2:30:00 AM
1/4/2023, 2:30:00 AM
1/5/2023, 2:30:00 AM
1/6/2023, 2:30:00 AM
1/7/2023, 2:30:00 AM
1/8/2023, 2:30:00 AM
1/9/2023, 2:30:00 AM
1/10/2023, 2:30:00 AM
1/11/2023, 2:30:00 AM
1/12/2023, 2:30:00 AM
1/13/2023, 2:30:00 AM
1/14/2023, 2:30:00 AM
1/15/2023, 2:30:00 AM
1/16/2023, 2:30:00 AM
1/17/2023, 2:30:00 AM
1/18/2023, 2:30:00 AM
1/19/2023, 2:30:00 AM
1/20/2023, 2:30:00 AM
1/21/2023, 2:30:00 AM
1/22/2023, 2:30:00 AM
1/23/2023, 2:30:00 AM
1/24/2023, 2:30:00 AM
1/25/2023, 2:30:00 AM
1/26/2023, 2:30:00 AM
1/27/2023, 2:30:00 AM
1/28/2023, 2:30:00 AM
1/29/2023, 2:30:00 AM
1/30/2023, 2:30:00 AM
1/31/2023, 2:30:00 AM
2/1/2023, 2:30:00 AM
2/2/2023, 2:30:00 AM
2/3/2023, 2:30:00 AM
2/4/2023, 2:30:00 AM
2/5/2023, 2:30:00 AM
2/6/2023, 2:30:00 AM
2/7/2023, 2:30:00 AM
2/8/2023, 2:30:00 AM
2/9/2023, 2:30:00 AM
2/10/2023, 2:30:00 AM
2/11/2023, 2:30:00 AM
2/12/2023, 2:30:00 AM
2/13/2023, 2:30:00 AM
2/14/2023, 2:30:00 AM
2/15/2023, 2:30:00 AM
2/16/2023, 2:30:00 AM
2/17/2023, 2:30:00 AM
2/18/2023, 2:30:00 AM
2/19/2023, 2:30:00 AM
2/20/2023, 2:30:00 AM
2/21/2023, 2:30:00 AM
2/22/2023, 2:30:00 AM
2/23/2023, 2:30:00 AM
2/24/2023, 2:30:00 AM
2/25/2023, 2:30:00 AM
2/26/2023, 2:30:00 AM
2/27/2023, 2:30:00 AM
2/28/2023, 2:30:00 AM
3/1/2023, 2:30:00 AM
3/2/2023, 2:30:00 AM
3/3/2023, 2:30:00 AM
3/4/2023, 2:30:00 AM
3/5/2023, 2:30:00 AM
3/6/2023, 2:30:00 AM
3/7/2023, 2:30:00 AM
3/8/2023, 2:30:00 AM
3/9/2023, 2:30:00 AM
3/10/2023, 2:30:00 AM
3/11/2023, 2:30:00 AM
3/12/2023, 2:30:00 AM
3/13/2023, 2:30:00 AM
3/14/2023, 2:30:00 AM
3/15/2023, 2:30:00 AM
3/16/2023, 2:30:00 AM
3/17/2023, 2:30:00 AM
3/18/2023, 2:30:00 AM
3/19/2023, 2:30:00 AM
3/20/2023, 2:30:00 AM
3/21/2023, 2:30:00 AM
3/22/2023, 2:30:00 AM
3/23/2023, 2:30:00 AM
3/24/2023, 2:30:00 AM
3/25/2023, 2:30:00 AM
3/27/2023, 2:30:00 AM
3/28/2023, 2:30:00 AM
3/29/2023, 2:30:00 AM
3/30/2023, 2:30:00 AM
3/31/2023, 2:30:00 AM
4/1/2023, 2:30:00 AM
4/2/2023, 2:30:00 AM
4/3/2023, 2:30:00 AM
4/4/2023, 2:30:00 AM
4/5/2023, 2:30:00 AM
4/6/2023, 2:30:00 AM
4/7/2023, 2:30:00 AM
4/8/2023, 2:30:00 AM
4/9/2023, 2:30:00 AM
4/10/2023, 2:30:00 AM
4/11/2023, 2:30:00 AM
4/12/2023, 2:30:00 AM
4/13/2023, 2:30:00 AM
4/14/2023, 2:30:00 AM
4/15/2023, 2:30:00 AM
4/16/2023, 2:30:00 AM
4/17/2023, 2:30:00 AM
4/18/2023, 2:30:00 AM
4/19/2023, 2:30:00 AM
4/20/2023, 2:30:00 AM
4/21/2023, 2:30:00 AM
4/22/2023, 2:30:00 AM
4/23/2023, 2:30:00 AM
4/24/2023, 2:30:00 AM
4/25/2023, 2:30:00 AM
4/26/2023, 2:30:00 AM
4/27/2023, 2:30:00 AM
4/28/2023, 2:30:00 AM
4/29/2023, 2:30:00 AM
4/30/2023, 2:30:00 AM
5/1/2023, 2:30:00 AM
5/2/2023, 2:30:00 AM
5/3/2023, 2:30:00 AM
5/4/2023, 2:30:00 AM
5/5/2023, 2:30:00 AM
5/6/2023, 2:30:00 AM
5/7/2023, 2:30:00 AM
5/8/2023, 2:30:00 AM
5/9/2023, 2:30:00 AM
5/10/2023, 2:30:00 AM
5/11/2023, 2:30:00 AM
5/12/2023, 2:30:00 AM
5/13/2023, 2:30:00 AM
5/14/2023, 2:30:00 AM
5/15/2023, 2:30:00 AM
5/16/2023, 2:30:00 AM
5/17/2023, 2:30:00 AM
5/18/2023, 2:30:00 AM
5/19/2023, 2:30:00 AM
5/20/2023, 2:30:00 AM
5/21/2023, 2:30:00 AM
5/22/2023, 2:30:00 AM
5/23/2023, 2:30:00 AM
5/24/2023, 2:30:00 AM
5/25/2023, 2:30:00 AM
5/26/2023, 2:30:00 AM
5/27/2023, 2:30:00 AM
5/28/2023, 2:30:00 AM
5/29/2023, 2:30:00 AM
5/30/2023, 2:30:00 AM
5/31/2023, 2:30:00 AM
6/1/2023, 2:30:00 AM
6/2/2023, 2:30:00 AM
6/3/2023, 2:30:00 AM
6/4/2023, 2:30:00 AM
6/5/2023, 2:30:00 AM
6/6/2023, 2:30:00 AM
6/7/2023, 2:30:00 AM
6/8/2023, 2:30:00 AM
6/9/2023, 2:30:00 AM
6/10/2023, 2:30:00 AM
6/11/2023, 2:30:00 AM
6/12/2023, 2:30:00 AM
6/13/2023, 2:30:00 AM
6/14/2023, 2:30:00 AM
6/15/2023, 2:30:00 AM
6/16/2023, 2:30:00 AM
6/17/2023, 2:30:00 AM
6/18/2023, 2:30:00 AM
6/19/2023, 2:30:00 AM
6/20/2023, 2:30:00 AM
6/21/2023, 2:30:00 AM
6/22/2023, 2:30:00 AM
6/23/2023, 2:30:00 AM
6/24/2023, 2:30:00 AM
6/25/2023, 2:30:00 AM
6/26/2023, 2:30:00 AM
6/27/2023, 2:30:00 AM
6/28/2023, 2:30:00 AM
6/29/2023, 2:30:00 AM
6/30/2023, 2:30:00 AM
7/1/2023, 2:30:00 AM
7/2/2023, 2:30:00 AM
7/3/2023, 2:30:00 AM
7/4/2023, 2:30:00 AM
7/5/2023, 2:30:00 AM
7/6/2023, 2:30:00 AM
7/7/2023, 2:30:00 AM
7/8/2023, 2:30:00 AM
7/9/2023, 2:30:00 AM
7/10/2023, 2:30:00 AM
7/11/2023, 2:30:00 AM
7/12/2023, 2:30:00 AM
7/13/2023, 2:30:00 AM
7/14/2023, 2:30:00 AM
7/15/2023, 2:30:00 AM
7/16/2023, 2:30:00 AM
7/17/2023, 2:30:00 AM
7/18/2023, 2:30:00 AM
7/19/2023, 2:30:00 AM
7/20/2023, 2:30:00 AM
7/21/2023, 2:30:00 AM
7/22/2023, 2:30:00 AM
7/23/2023, 2:30:00 AM
7/24/2023, 2:30:00 AM
7/25/2023, 2:30:00 AM
7/26/2023, 2:30:00 AM
7/27/2023, 2:30:00 AM
7/28/2023, 2:30:00 AM
7/29/2023, 2:30:00 AM
7/30/2023, 2:30:00 AM
7/31/2023, 2:30:00 AM
8/1/2023, 2:30:00 AM
8/2/2023, 2:30:00 AM
8/3/2023, 2:30:00 AM
8/4/2023, 2:30:00 AM
8/5/2023, 2:30:00 AM
8/6/2023, 2:30:00 AM
8/7/2023, 2:30:00 AM
8/8/2023, 2:30:00 AM
8/9/2023, 2:30:00 AM
8/10/2023, 2:30:00 AM
8/11/2023, 2:30:00 AM
8/12/2023, 2:30:00 AM
8/13/2023, 2:30:00 AM
8/14/2023, 2:30:00 AM
8/15/2023, 2:30:00 AM
8/16/2023, 2:30:00 AM
8/17/2023, 2:30:00 AM
8/18/2023, 2:30:00 AM
8/19/2023, 2:30:00 AM
8/20/2023, 2:30:00 AM
8/21/2023, 2:30:00 AM
8/22/2023, 2:30:00 AM
8/23/2023, 2:30:00 AM
8/24/2023, 2:30:00 AM
8/25/2023, 2:30:00 AM
8/26/2023, 2:30:00 AM
8/27/2023, 2:30:00 AM
8/28/2023, 2:30:00 AM
8/29/2023, 2:30:00 AM
8/30/2023, 2:30:00 AM
8/31/2023, 2:30:00 AM
9/1/2023, 2:30:00 AM
9/2/2023, 2:30:00 AM
9/3/2023, 2:30:00 AM
9/4/2023, 2:30:00 AM
9/5/2023, 2:30:00 AM
9/6/2023, 2:30:00 AM
9/7/2023, 2:30:00 AM
9/8/2023, 2:30:00 AM
9/9/2023, 2:30:00 AM
9/10/2023, 2:30:00 AM
9/11/2023, 2:30:00 AM
9/12/2023, 2:30:00 AM
9/13/2023, 2:30:00 AM
9/14/2023, 2:30:00 AM
9/15/2023, 2:30:00 AM
9/16/2023, 2:30:00 AM
9/17/2023, 2:30:00 AM
9/18/2023, 2:30:00 AM
9/19/2023, 2:30:00 AM
9/20/2023, 2:30:00 AM
9/21/2023, 2:30:00 AM
9/22/2023, 2:30:00 AM
9/23/2023, 2:30:00 AM
9/24/2023, 2:30:00 AM
9/25/2023, 2:30:00 AM
9/26/2023, 2:30:00 AM
9/27/2023, 2:30:00 AM
9/28/2023, 2:30:00 AM
9/29/2023, 2:30:00 AM
9/30/2023, 2:30:00 AM
10/1/2023, 2:30:00 AM
10/2/2023, 2:30:00 AM
10/3/2023, 2:30:00 AM
10/4/2023, 2:30:00 AM
10/5/2023, 2:30:00 AM
10/6/2023, 2:30:00 AM
10/7/2023, 2:30:00 AM
10/8/2023, 2:30:00 AM
10/9/2023, 2:30:00 AM
10/10/2023, 2:30:00 AM
10/11/2023, 2:30:00 AM
10/12/2023, 2:30:00 AM
10/13/2023, 2:30:00 AM
10/14/2023, 2:30:00 AM
10/15/2023, 2:30:00 AM
10/16/2023, 2:30:00 AM
10/17/2023, 2:30:00 AM
10/18/2023, 2:30:00 AM
10/19/2023, 2:30:00 AM
10/20/2023, 2:30:00 AM
10/21/2023, 2:30:00 AM
10/22/2023, 2:30:00 AM
10/23/2023, 2:30:00 AM
10/24/2023, 2:30:00 AM
10/25/2023, 2:30:00 AM
10/26/2023, 2:30:00 AM
10/27/2023, 2:30:00 AM
✘   (161 / 162)

   FAIL  "0 30 2 * * * with 365 iterations should return 365 days from now in Europe/Berlin"
    Expected values to be deeply equal:  (equal)

        ++true     (Expected)
        --false    (Actual)

March 26th 2023 is missing from the output, March 26th 2023 2:30 AM does not exist as we move our clocks from 2AM to 3AM.

Seeing that you did some further refactoring with minitz since then I also ran the same test with the current release croner v5.3.1

~/p/croner (master)> npm run test

> [email protected] test
> uvu test test.croner.js

node/js/test.croner.js
• • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • 10/27/2022, 2:30:00 AM
10/28/2022, 2:30:00 AM
10/29/2022, 2:30:00 AM
10/30/2022, 2:30:00 AM
10/31/2022, 2:30:00 AM
11/1/2022, 2:30:00 AM
11/2/2022, 2:30:00 AM
11/3/2022, 2:30:00 AM
11/4/2022, 2:30:00 AM
11/5/2022, 2:30:00 AM
11/6/2022, 2:30:00 AM
11/7/2022, 2:30:00 AM
11/8/2022, 2:30:00 AM
11/9/2022, 2:30:00 AM
11/10/2022, 2:30:00 AM
11/11/2022, 2:30:00 AM
11/12/2022, 2:30:00 AM
11/13/2022, 2:30:00 AM
11/14/2022, 2:30:00 AM
11/15/2022, 2:30:00 AM
11/16/2022, 2:30:00 AM
11/17/2022, 2:30:00 AM
11/18/2022, 2:30:00 AM
11/19/2022, 2:30:00 AM
11/20/2022, 2:30:00 AM
11/21/2022, 2:30:00 AM
11/22/2022, 2:30:00 AM
11/23/2022, 2:30:00 AM
11/24/2022, 2:30:00 AM
11/25/2022, 2:30:00 AM
11/26/2022, 2:30:00 AM
11/27/2022, 2:30:00 AM
11/28/2022, 2:30:00 AM
11/29/2022, 2:30:00 AM
11/30/2022, 2:30:00 AM
12/1/2022, 2:30:00 AM
12/2/2022, 2:30:00 AM
12/3/2022, 2:30:00 AM
12/4/2022, 2:30:00 AM
12/5/2022, 2:30:00 AM
12/6/2022, 2:30:00 AM
12/7/2022, 2:30:00 AM
12/8/2022, 2:30:00 AM
12/9/2022, 2:30:00 AM
12/10/2022, 2:30:00 AM
12/11/2022, 2:30:00 AM
12/12/2022, 2:30:00 AM
12/13/2022, 2:30:00 AM
12/14/2022, 2:30:00 AM
12/15/2022, 2:30:00 AM
12/16/2022, 2:30:00 AM
12/17/2022, 2:30:00 AM
12/18/2022, 2:30:00 AM
12/19/2022, 2:30:00 AM
12/20/2022, 2:30:00 AM
12/21/2022, 2:30:00 AM
12/22/2022, 2:30:00 AM
12/23/2022, 2:30:00 AM
12/24/2022, 2:30:00 AM
12/25/2022, 2:30:00 AM
12/26/2022, 2:30:00 AM
12/27/2022, 2:30:00 AM
12/28/2022, 2:30:00 AM
12/29/2022, 2:30:00 AM
12/30/2022, 2:30:00 AM
12/31/2022, 2:30:00 AM
1/1/2023, 2:30:00 AM
1/2/2023, 2:30:00 AM
1/3/2023, 2:30:00 AM
1/4/2023, 2:30:00 AM
1/5/2023, 2:30:00 AM
1/6/2023, 2:30:00 AM
1/7/2023, 2:30:00 AM
1/8/2023, 2:30:00 AM
1/9/2023, 2:30:00 AM
1/10/2023, 2:30:00 AM
1/11/2023, 2:30:00 AM
1/12/2023, 2:30:00 AM
1/13/2023, 2:30:00 AM
1/14/2023, 2:30:00 AM
1/15/2023, 2:30:00 AM
1/16/2023, 2:30:00 AM
1/17/2023, 2:30:00 AM
1/18/2023, 2:30:00 AM
1/19/2023, 2:30:00 AM
1/20/2023, 2:30:00 AM
1/21/2023, 2:30:00 AM
1/22/2023, 2:30:00 AM
1/23/2023, 2:30:00 AM
1/24/2023, 2:30:00 AM
1/25/2023, 2:30:00 AM
1/26/2023, 2:30:00 AM
1/27/2023, 2:30:00 AM
1/28/2023, 2:30:00 AM
1/29/2023, 2:30:00 AM
1/30/2023, 2:30:00 AM
1/31/2023, 2:30:00 AM
2/1/2023, 2:30:00 AM
2/2/2023, 2:30:00 AM
2/3/2023, 2:30:00 AM
2/4/2023, 2:30:00 AM
2/5/2023, 2:30:00 AM
2/6/2023, 2:30:00 AM
2/7/2023, 2:30:00 AM
2/8/2023, 2:30:00 AM
2/9/2023, 2:30:00 AM
2/10/2023, 2:30:00 AM
2/11/2023, 2:30:00 AM
2/12/2023, 2:30:00 AM
2/13/2023, 2:30:00 AM
2/14/2023, 2:30:00 AM
2/15/2023, 2:30:00 AM
2/16/2023, 2:30:00 AM
2/17/2023, 2:30:00 AM
2/18/2023, 2:30:00 AM
2/19/2023, 2:30:00 AM
2/20/2023, 2:30:00 AM
2/21/2023, 2:30:00 AM
2/22/2023, 2:30:00 AM
2/23/2023, 2:30:00 AM
2/24/2023, 2:30:00 AM
2/25/2023, 2:30:00 AM
2/26/2023, 2:30:00 AM
2/27/2023, 2:30:00 AM
2/28/2023, 2:30:00 AM
3/1/2023, 2:30:00 AM
3/2/2023, 2:30:00 AM
3/3/2023, 2:30:00 AM
3/4/2023, 2:30:00 AM
3/5/2023, 2:30:00 AM
3/6/2023, 2:30:00 AM
3/7/2023, 2:30:00 AM
3/8/2023, 2:30:00 AM
3/9/2023, 2:30:00 AM
3/10/2023, 2:30:00 AM
3/11/2023, 2:30:00 AM
3/12/2023, 2:30:00 AM
3/13/2023, 2:30:00 AM
3/14/2023, 2:30:00 AM
3/15/2023, 2:30:00 AM
3/16/2023, 2:30:00 AM
3/17/2023, 2:30:00 AM
3/18/2023, 2:30:00 AM
3/19/2023, 2:30:00 AM
3/20/2023, 2:30:00 AM
3/21/2023, 2:30:00 AM
3/22/2023, 2:30:00 AM
3/23/2023, 2:30:00 AM
3/24/2023, 2:30:00 AM
3/25/2023, 2:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
3/26/2023, 1:30:00 AM
✘   (166 / 167)

   FAIL  "0 30 2 * * * with 365 iterations should return 365 days from now in Europe/Berlin"
    Expected values to be deeply equal:  (equal)

        ++true     (Expected)
        --false    (Actual)

I haven't yet been able to check how the calculation did change from 4.4.0 to 5.3.1, but this seems very broken to me. It seems croner is unable to get a next valid date after the invalid date March 26th 2023 1:30AM. I'd rather keep skipping a day than hanging indefinitely on DST start.
That being said, timezones and DST are challenging and I can not provide a better solution or even a suggestion how to make this reliably testable on all developer machines across different timezones. I hope we can somehow get to a better solution than the current one, in the meantime I'm just going to stick with our current croner release.

Programmatically stop croner

Thanks for the library!

Is there any way to programmatically stop croner? My Jest tests hangs until I manually kill tests process

Infinite loop with specific crontab

const cron = new Cron('0 * * * mon,tue,wed,fri,sat,sun', {
  legacyMode: true,
});
cron.next(new Date('2022-03-31 11:40:34'));

Results in an infinite loop.

Chore: Review code

Have a look at PR #15 , full repo review.

Pay special attention to src/*,

pay no attention to docs/* (as these are auto-generated files)

Inconsistent bug prone timezone argument order

Cron('2023-01-23T00:00:00', { timezone: 'Asia/Kolkata' }, () => { console.log('Yay') });

is inconsistent vs default usage such as

const job = Cron('*/5 * * * * *', () => {
	console.log('This will run every fifth second');
});

timezone should come as 3rd argument, not 2nd to minimize bugs:

Cron('2023-01-23T00:00:00', () => { console.log('Yay') }, { timezone: 'Asia/Kolkata' });

Alternatively, if wishing to follow callback as the last argument convention, make it two arguments only (object + callback), like:

Cron({time: '2023-01-23T00:00:00', timezone: 'Asia/Kolkata' }, () => { console.log('Yay') });

Running multiple times

My production env has 2 instances of application running (Running app over PM2 )
Currently, this cron running 2 times in my application how to limit to 1?

TypeError: croner_1.Cron is not a constructor

I tried run the following code in TypeScript (specifically in a Nest.js service):

import {Cron} from 'croner'

const job: Cron = new Cron('* * * * * *', () => {
	console.log('This will run every second.')
})

But it fails with TypeError: croner_1.Cron is not a constructor.

If I remove new keyword, it fails with TypeError: croner_1.Cron is not a function (obviously).

I have no idea why this fails.


MWE example Nest.js service:

import {Injectable, Logger} from '@nestjs/common'
import {Cron} from 'croner'

@Injectable()
export class CronService {
	private readonly logger = new Logger(CronService.name)
	private jobs: Cron[] = []

	createJob(time: string) {
		// time = '*/1 * * * * *'
		const job: Cron = new Cron(time, () => {
			this.logger.debug('This will run every second.')
		})

		this.jobs.push(job)
		return job
	}
}

In CommonJS, it works as expected.


This might not be an issue with croner, but with Nest.js and how it does in the background (see SO answer).


jmcdo29 at Nest.js Discord Channel found out that the issue is in croner’s typing, citing:

Looks like it's bad typings on croner's part. I had to do

import * as Cron from 'croner';
(Cron as any)('* * * * * *', () => console.log('run every second'));

To get it to work

Package subpath './package.json' is not defined by "exports"

Hello!
I encountered the error:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './package.json' is not defined by "exports" in /..../backend/node_modules/croner/package.json

Added line solved the problem:

"exports": {
"./package.json": "./package.json",
".": {
"import": "./src/croner.js",
"require": "./dist/croner.cjs",
"browser": "./dist/croner.min.js"
},

Got `Invalid Date` with timezone on JSC

croner version: v5.3.2

new Cron('* * * * *', { timezone: 'UTC' }).next()
// Got `Invalid Date`

I just confirm it happened in JSC with the following platforms:

  • Safari 16.1
  • React Native v0.69 iOS

It works fine on Chrome / Firebox.

Also, I'm using [email protected] in production and it works fine.

Proper types support for ESM exports when moduleResolution is set to NodeNext (^TS 4.7)

Hi,

first of all thanks for this package. I noticed that the package itself is already a module which is really nice. But unfortunately there are some minor things which needs to be updated to support NodeNext as moduleResolution.

The problem is easy to reproduce:

  • Create a TypeScript project
  • Have a tsconfig in place and set the moduleResolution to NodeNext
  • Install croner
  • import croner in index.ts

As you will notice, the types cannot be resolved.

This configuration in TypeScript is really strict about some settings and cannot detect types in a fallback manner (types field). Therefore you need to update the package.json like documented here: https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-rc/#package-json-exports-imports-and-self-referencing

This would mean the package.json should look like this:

 {
  "type": "module",
  "main": "./dist/croner.cjs",
  "browser": "./dist/croner.min.js",
  "module": "./src/croner.js",
  "types": "types/croner.d.ts",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "import": {
        "types": "./types/croner.d.ts",
        "default": "./src/croner.js"
      },
      "require": "./dist/croner.cjs",
      "browser": "./dist/croner.min.js"
    },
    "./minified": {
      "import": "./dist/croner.min.mjs",
      "require": "./dist/croner.min.js",
      "browser": "./dist/croner.min.js",
      "types": "./types/croner.d.ts"
    }
  }
}

So instead of relying on the old field types we need to define the types explicit for the import mapping. This way the types can be properly resolved (but only when defined as first prop in the import object).

I tested the changes locally and they work well. Hope it can be fixed pretty soon, but for now I just add the types via include in the tsconfig file as workaround.

Skipping 1 day

I downloaded the project and ran the test before making any change and I found an issue with the following test:

test("0 0 0 * * * with 40 iterations should return 40 days from now", function () {

   FAIL  "0 0 0 * * * with 40 iterations should return 40 days from now"                             
    Expected values to be deeply equal:  (equal)                                                     

        ++1664506800000    (Expected)
        --1664593200000    (Actual)

    at assert (D:/Development/WebStorm/cronerRaw/node_modules/uvu/assert/index.js:33:8)
    at Object.equal (D:/Development/WebStorm/cronerRaw/node_modules/uvu/assert/index.js:45:2)
    at Object.handler (D:/Development/WebStorm/cronerRaw/test/node/js/src/suites/basics.cjs:529:10)
    at Number.runner (D:/Development/WebStorm/cronerRaw/node_modules/uvu/dist/index.js:78:16)
    at async Module.exec (file:///D:/Development/WebStorm/cronerRaw/node_modules/uvu/dist/index.mjs:141:33)
    at async Module.run (file:///D:/Development/WebStorm/cronerRaw/node_modules/uvu/run/index.mjs:12:2)
    at async D:/Development/WebStorm/cronerRaw/node_modules/uvu/bin.js:26:5

After running the test with many different initial dates and changing the iteration number to 1460 (4 years) I found out it is skipping specific dates: The first Sunday of September after September first

Skipping Sun Sep 5, 2021
image

Skipping Sun Sep 4, 2022
image

Skipping Sun Sep 3, 2023
image

Skipping Sun Sep 8, 2024
image

Skipping Sun Sep 7, 2025
image

Cron.next() returned incorrect calculation sometimes depending on the system time.

Supposed the system time is 12:35:42 for the following examples, the date is irrelevant in this case

  • * 17 * * * expected 17:00:00 but 17:36:00 was returned
  • */5 * 15 * * * expected 15:00:00 but 15:35:45 was returned
  • * * 15 * * * expected 15:00:00 but 15:35:00 was returned
  • 42 * * * * returned 12:42:00 as expected.
  • */5 * 11 * * * returned 11:00:00 in the next day as expected.
  • * 17 * 1 * returned 17:00:00 as expected

Generally, the miscalculation occurred when you used the * pattern in the second, minute, or hour slot but completely went away when the minute, day, or month slot has number literal or when the resulting date sits at least tomorrow in the future.

This expression is not callable

Thanks for your efforts. Here replacing cron package with croner.
I saw also PM2 recently migrated to croner. Congrats.

Using croner 4.3.6, with VSCode 1.65.2, I noted this messages in Problems panel:

This expression is not callable.
  Type 'typeof import("[...]/node_modules/croner/types/croner.single")' has no call signatures.

A function with a name starting with an uppercase letter should only be used as a constructor.
const Cron = require("croner");

Cron("1 25 5 * * *", {
    timezone: "Europe/Rome"
}, () => {
    run();
});

Ok, eslint error depends on new-cap setting.
The surprise was about ts lint message

Of course I can suppress the messages with the following directives:

// @ts-ignore
Cron("1 25 5 * * *", { // eslint-disable-line
    timezone: "Europe/Rome"
}, () => {
    run();
});

Any hint?

Duplicated jobs

Hello!
I created a cron job to send a POST request to my backend with axios.
I have the following cron setting:
image

There is my function content:
image

Still get duplicated tasks:
image

Could you please help that is going on?

Providing invalid date results in an infinite loop

const cron2 = new Cron('0 * * * *', {
  legacyMode: true,
});
cron2.next(new Date('pizza'));

I think I also had an infinite loop with a valid date, but I'm still working on a concise way to reproduce it.

Feature: Support for minimum interval

Croner should allow you to set a minimum interval. This would as an example allow triggering each 90th second, while still allowing to restrict runs as usual.

Example:

Cron(
  "* * * 1 * *", 
  {
    interval: 90
  },
  () => { 
    console.log('This will run every 90th second, but only at the first day of every month');
  }
};

Question: Named schedules

Hey I am trying to create multiple schedules for a discord bot.
The user can choose on which date they want to schedule the command, that will get saved in the database if the bot turns off cause of an update or what ever reason.
The database will then get requested every 10 minutes to check if a new schedule has been add to the database.
So I try to call it and create multiple schedules based on that but your croner seems like it can only run if I say that it has a variable to it,
or did I missunderstood the documentation.

Croner & PM2: Croner job is stopping at 00:00

Hello,
I'm using pm2 to keep my services up and running and centralize logs.

I'm running actually a really simple piece of code in a docker container (basically theses commands appears in my Dockerfile):

FROM node:16.15-bullseye-slim
COPY release /opt/src/publisher
CMD pm2 start /opt/src/bot/main.js --time  -o /var/log/publisher/publisher.log -e /var/log/publisher/publisher.log ; pm2 logs

Here is my NodeJS code:

const dayjs = require('dayjs');
const Cron = require("croner");

//Simple match wildcard function like: "aa" will match "a*" or "*a" or "**" but not "bb" or "b*"
function matchWildcard(str1, str2) {
    if (typeof str1 != "string" || typeof str2 != "string")
        return false;
    if (str1.length != str2.length)
        return false;
    for (let i = 0; i < str1.length; i++) {
        if (str1.at(i) != "*" && str2.at(i) != "*" && str1.at(i) != str2.at(i)) {
            console.debug(`${str1} matchs ${str2} ?: FALSE`);
            return false;
        }
    }
    console.debug(`${str1} matchs ${str2} ?: TRUE`);
    return true;
}

//My cron task running every minute
const job = Cron("0 */1 * ? * *", async () => {
    let now = dayjs();
    let dayKey = now.format("HH:mm.DD-MM");
    matchWildcard("12:00.27-**", dayKey);
});

So the croner job is starting but stops at 00:00 and I do not know why this append:
image

Problem with timezone override and question mark

If using question mark in expression, and a timezone different from local, question mark is substituted with local value instead of target timezone value. An example:

Cron("0 0 ? * * *", {timezone: "UTC"}) will be evaluated to Cron("0 0 0 * * *", {timezone: "UTC"}) if time is 00:xx:yy in local timezone (but something other in UTC).

Type error

node_modules/croner/types/croner.d.ts:59:35 - error TS2304: Cannot find name 'date'.

Code size vs readability

It has been a few months since I was last able to keep track of the developments in croner. Reading up on the changes (and there have been a lot of nice improvements!) I'd like to debate whether the changes to variable names in 390ebca are really a good trade-off.
While it certainly makes the code size smaller, I think it also makes the code less readable. I always thought of croner as a very well written, easy to understand library. So I'm afraid this change will introduce some tribal knowledge to the code which makes it harder for future contributors to get up to speed. Being a backend developer, I do not usually care that much about binary size, so maybe it is worth the reduction in readability and I'm just wrong. Still I'm wondering if there would not be a better solution for this (uglify, etc.) which provides small code size for shipping the code while keeping the code nice and clear for development. At least we could make sure that all those shorthand property names have solid doc comments.

Enumerate does not care about option maxRuns

job.enumerate(10); is expected to list next 10 run times, which it does.

However - if option maxRuns is set to 5, the job is only triggered five times. So in this case job.enumerate(10) should only list the first five runs.

test.ts

import { Cron } from "croner";
// Or in deno: import { Cron } from "https://deno.land/x/[email protected]/src/croner.js";

const job = new Cron("* * * * * *", { maxRuns: 5 }, () => {
  console.log("This will run five times.");
});

console.log(job.enumerate(10));

Output:

[
  2022-09-21T18:54:04.000Z,
  2022-09-21T18:54:05.000Z,
  2022-09-21T18:54:06.000Z,
  2022-09-21T18:54:07.000Z,
  2022-09-21T18:54:08.000Z,
  2022-09-21T18:54:09.000Z,
  2022-09-21T18:54:10.000Z,
  2022-09-21T18:54:11.000Z,
  2022-09-21T18:54:12.000Z,
  2022-09-21T18:54:13.000Z
]
This will run five times.
This will run five times.
This will run five times.
This will run five times.
This will run five times.

Can not specify range from any weekday to sunday

A pattern like * * * * FRI-SUN will not be valid because SUN is replaced with 0 and thus the following error is thrown

TypeError: CronPattern: From value is larger than to value: '5-0'

Since alphabetical weekdays are non-standard syntax anyway, would you be willing to have a fix for this edge case added?

A likely candidate could be here

croner/src/pattern.js

Lines 315 to 324 in 9f23fae

CronPattern.prototype.replaceAlphaDays = function (conf) {
return conf
.replace(/sun/gi, "0")
.replace(/mon/gi, "1")
.replace(/tue/gi, "2")
.replace(/wed/gi, "3")
.replace(/thu/gi, "4")
.replace(/fri/gi, "5")
.replace(/sat/gi, "6");
};

I guess something like this should work, but did not run all tests yet

CronPattern.prototype.replaceAlphaDays = function (conf) {
	return conf
		.replace(/-sun/gi, "-7")
		.replace(/sun/gi, "0")
		.replace(/mon/gi, "1")
		.replace(/tue/gi, "2")
		.replace(/wed/gi, "3")
		.replace(/thu/gi, "4")
		.replace(/fri/gi, "5")
		.replace(/sat/gi, "6");
};

This way we keep 0 as the standard for sundays, but automatically choose 7 for upper values of ranges.

The following would be an even cleaner alternative, but lookahead and lookbehind in regex are sometimes unsupported in older systems, so depending on what should still be supported that is not an option

CronPattern.prototype.replaceAlphaDays = function (conf) {
	return conf
		.replace(/(?<=-)sun/gi, "7")
		.replace(/sun/gi, "0")
		.replace(/mon/gi, "1")
		.replace(/tue/gi, "2")
		.replace(/wed/gi, "3")
		.replace(/thu/gi, "4")
		.replace(/fri/gi, "5")
		.replace(/sat/gi, "6");
};

Duplicate executing

Hello, i have duplicate executing issue with croner,
image
importing
image
code
image
results
as i understand right, i place maxRuns:1 and interval in options, but same result its sending a lot of request, but needed to send only one
same situation with cron, node-schedule

Feature request: onComplete callback

This is more of a question and/or possible feature request. Does Croner have an onComplete type callback ability? Essentially I'm looking to run this job x amount of times using maxRuns however on the last run if my tasks are not complete I will need to perform other tasks. So without keeping an internal count of the runs and then running if it hits its max I would like to use the maxRuns option and then pass another callback function to on if maxRuns has reached it's end. Thanks!

Wrong evaluation of dayOfWeek AND dayOfMonth

Expression 0 0 0 13 * 5 does not generate right dates.
This is basically expression for https://en.wikipedia.org/wiki/Friday_the_13th.

Croner generates (for 2023 - source)

0: Fri Jan 06 2023
1: Fri Jan 13 2023
2: Fri Jan 20 2023
...
6: Mon Feb 13 2023

but expected would be (for 2023 - source)

0: Fri, Jan 13 2023
1: Fri, Oct 13 2023
2: Fri, Sep 13 2024
3: Fri, Dec 13 2024
4: Fri, Jun 13 2025
...

As you can see from examples dayOfMonth and dayOfWeek combination behaves as OR instead of AND.

Incorrect future day calculation when using weekdays

"croner": "~4.1.96"

const cron = new Cron('0 0 * * sun');
const previousRun = new Date('2022-02-17');
const nextRun = cron.next(previousRun);

Expected: Sun Feb 20 2022 00:00:00
Actual: Sun Jan 01 2023 00:00:00

4.3.12 timing is wrong on some schedule steps

OS: RHEL 8/7
Node: 16.15.0

Running the below on 4.3.11 gives expected results (1 run per 5 seconds)

const cron = new Cron('*/5 * * * * *');
cron.schedule(() => console.log(`running: ${new Date()}:${Date.now()}`));

Output:

running: Wed Jun 15 2022 15:00:25 GMT-0500 (Central Daylight Time):1655323225001
running: Wed Jun 15 2022 15:00:30 GMT-0500 (Central Daylight Time):1655323230001
running: Wed Jun 15 2022 15:00:35 GMT-0500 (Central Daylight Time):1655323235001
running: Wed Jun 15 2022 15:00:40 GMT-0500 (Central Daylight Time):1655323240000
running: Wed Jun 15 2022 15:00:45 GMT-0500 (Central Daylight Time):1655323245000
running: Wed Jun 15 2022 15:00:49 GMT-0500 (Central Daylight Time):1655323249999
running: Wed Jun 15 2022 15:00:50 GMT-0500 (Central Daylight Time):1655323250001
running: Wed Jun 15 2022 15:00:55 GMT-0500 (Central Daylight Time):1655323255001
running: Wed Jun 15 2022 15:00:59 GMT-0500 (Central Daylight Time):1655323259999
running: Wed Jun 15 2022 15:01:05 GMT-0500 (Central Daylight Time):1655323265000

However, 4.3.12
gives the below output, this seems to happen with all schedules at some random frequency.

running: Wed Jun 15 2022 14:56:40 GMT-0500 (Central Daylight Time):1655323000001

---- 14:56:45 is ran almost 5 seconds late immediately followed by the 14:56:50 run
**running: Wed Jun 15 2022 14:56:49 GMT-0500 (Central Daylight Time):1655323009997**
**running: Wed Jun 15 2022 14:56:50 GMT-0500 (Central Daylight Time):1655323010001**

running: Wed Jun 15 2022 14:56:55 GMT-0500 (Central Daylight Time):1655323015000
running: Wed Jun 15 2022 14:57:00 GMT-0500 (Central Daylight Time):1655323020000
running: Wed Jun 15 2022 14:57:05 GMT-0500 (Central Daylight Time):1655323025001
running: Wed Jun 15 2022 14:57:10 GMT-0500 (Central Daylight Time):1655323030001
running: Wed Jun 15 2022 14:57:15 GMT-0500 (Central Daylight Time):1655323035000

---- 14:57:20 is ran almost 5 seconds late immediately followed by the 14:57:25 run
**running: Wed Jun 15 2022 14:57:24 GMT-0500 (Central Daylight Time):1655323044999**
**running: Wed Jun 15 2022 14:57:25 GMT-0500 (Central Daylight Time):1655323045000**

Croner & PM2: try to "date set" make the callback be called 2 times

Hello,
I'm using pm2 to keep my services up and running and centralize logs.

I'm running actually a really simple piece of code in a docker container (basically theses commands appears in my Dockerfile):

FROM node:16.15-bullseye-slim
COPY main.js /opt/src/publisher/main.js
COPY package-lock.json /opt/src/publisher/package-lock.json
COPY package.json /opt/src/publisher/package.json
WORKDIR /opt/src/publisher
RUN npm ci --only=prod && npm install -g pm2
CMD pm2 start /opt/src/publisher/main.js --time  -o /var/log/publisher/publisher.log -e /var/log/publisher/publisher.log ; pm2 logs

Here is my NodeJS code:

const dayjs = require('dayjs');
const Cron = require("croner");

//Simple match wildcard function like: "aa" will match "a*" or "*a" or "**" but not "bb" or "b*"
function matchWildcard(str1, str2) {
    if (typeof str1 != "string" || typeof str2 != "string")
        return false;
    if (str1.length != str2.length)
        return false;
    for (let i = 0; i < str1.length; i++) {
        if (str1.at(i) != "*" && str2.at(i) != "*" && str1.at(i) != str2.at(i)) {
            console.debug(`${str1} matchs ${str2} ?: FALSE`);
            return false;
        }
    }
    console.debug(`${str1} matchs ${str2} ?: TRUE`);
    return true;
}

//My cron task running every minute
const job = Cron("0 */1 * ? * *", async () => {
    let now = dayjs();
    let dayKey = now.format("HH:mm.DD-MM");
    matchWildcard("12:00.27-**", dayKey);
});

So I met this really strange error where if I run a bash in my docker and try to change the date (unsuccessfully), the croner job is going to start to execute the callback at 32:00 min and 32:59min (like one ate 00sec and one at 59 sec) or starting to has 1sec late.

# date -s "23:55:00"
date: cannot set date: Operation not permitted
Mon May 30 23:55:00 +14 2022

image

I know this is a really specific case but I was wondering if it could hide something more deep.

Incorrect handling of comma-separated values with ranges and/or stepping

Hi, I'm currently evaluating cron-related libraries for usage in my project, including reviewing the code of any candidates before deciding which one to use.
Let me first say that I'm very pleased with the quality of your code and that I'm very likely to choose this project to handle cron schedules in my application.

During code review I stumbled upon a few lines regarding the handling of a pattern which as far as I know reject patterns that would be perfectly valid in cron syntax and would also successfully parse by just removing those checks. These lines were introduced with 62c0289

croner/src/pattern.js

Lines 141 to 163 in 12cc992

const split = conf.split(",");
if( split.length > 1 ) {
for( let i = 0; i < split.length; i++ ) {
this.partToArray(type, split[i], valueIndexOffset, true);
}
// Handle range with stepping (x-y/z)
} else if( conf.indexOf("-") !== -1 && conf.indexOf("/") !== -1 ) {
if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,");
this.handleRangeWithStepping(conf, type, valueIndexOffset);
// Handle range
} else if( conf.indexOf("-") !== -1 ) {
if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,");
this.handleRange(conf, type, valueIndexOffset);
// Handle stepping
} else if( conf.indexOf("/") !== -1 ) {
if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,");
this.handleStepping(conf, type, valueIndexOffset);

As you can see in the snippet above, in the function CronPattern.partToArray() you first split parts of the current pattern part by comma, but then reject any ranges or steppings if they are evaluated after this split, in the recursion. Contrary to the statement of the thrown error, comma-separated values can perfectly coexist with ranges and stepping.

I entered the examples from your tests for Cronitor and it parses well:
https://crontab.guru/#1,2/5__**
https://crontab.guru/#1-15/5,6__**
https://crontab.guru/#1-15,17__**

As you can see from the website's explanation these patterns are perfectly fine and if you'd just remove those checks they would indeed parse as intended. Actually you could get rid of the recursed parameter altogether.

Is there any reason I didn't see yet why you reject such patterns? If you want to accept these patterns as Cronitor does, I can submit a PR as I already made some tests locally. Just want to hear your opinion on it first.

Incorrect next date when using legacy mode

const cron = new Cron('15 9 * * mon', {
  legacyMode: true,
});
const borked = cron.next(new Date('2022-02-28 23:59:00'));
console.log(borked);

Expected: Something in March.
Actual: Mon Feb 28 2022 09:15:00

Cron job every five months, next not calculated correctly

Description

Hi
I'm trying to run a cron every 5 months at 10 am. But the next date is not calculated correctly.

const job = new Cron(`0 10 1 */5 *`, {timezone: "Europe/Rome"}, () => {
// My Job
});
console.log((new Date()).toISOString(), job.next()?.toISOString());

Output:
Now: 2022-02-15T09:05:05.829Z
next: 2022-05-01T08:00:00.000Z

I expect the next is: 2022-07-01T08:00:00.000Z

My tests:
0 10 1 */1 * -> (next: 2022-03-01T09:00:00.000Z)

0 10 1 */2 * -> (next: 2022-04-01T08:00:00.000Z)

0 10 1 */3 * <- Not work (next: 2022-03-01T09:00:00.000Z)

0 10 1 */4 * <- Not work (next: 2022-04-01T08:00:00.000Z)

0 10 1 */5 * <- Not work (next: 2022-05-01T08:00:00.000Z )

I've also tried setting the startAt option, but it doesn't change anything:

{
  startAt: new Date(),
  timezone: "Europe/Rome"
}

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.