npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

quartzcron

v0.1.0

Published

version: `0.1.0` [CHANGELOG](https://github.com/fedeghe/quartzcron/blob/master/CHANGELOG.md)

Readme

Coverage Status NPM License CircleCI

GitHub top language Static Badge

track

quartzcron

version: 0.1.0
CHANGELOG

Quartz scheduler offers way more flexibility compared to traditional cron tool.
That additional freedom reflects into less trivial composition for the cron strings, this library aims to

  • help to programmatically create cron expressions
  • validate cron expressions
  • get the n next precise occurrences

A quartz cron expression is characterized by the following structure:

s  i  h dom m dow y  
⎜  ⎜  ⎜  ⎜  ⎜  ⎜  ⎜  
'--+--+--+--+--+--+--> 1st: seconds
   '--+--+--+--+--+--> 2nd: minutes
      '--+--+--+--+--> 3rd: hours
         '--+--+--+--> 4th: days of month ---\
            '--+--+--> 5th: months           | mutually exclusive
               '--+--> 6th: days of week  ---/
                  '--> 7th: years

sample usage

const QuartzCron = require('quartzcron'),
    qct = new QuartzCron();
let exp = qct.out(); // ->  0 0 0 * * ? *
// thus the default is
// at midnight of everyday
// but default values can be changed when calling the constructor
qct.atHour(12)
    .atHourAdd(22)
    .onLastMonthDay()
    .everyNYears(5, 2025);

exp = qct.out(); //-> 0 0 12,22 L * ? 2025/5
/*
alternatively the cron expression is also returned
as the instance _toString_ invokation
*/

const next = qct.next({
    date: new Date('00:00:00 01-01-2024'),
    n: 3
})
/*
[
  2025-01-31T12:00:00.000Z,
  2025-01-31T22:00:00.000Z,
  2025-02-28T12:00:00.000Z
]
*/

API

constructor 👷🏽‍♂️

Constructor can handle:

  • 0 parameters
    default used 0 0 0 * * ? *
    const qc = new Quartzcron();
  • 1 valid expression string
    when invalid throws and exception
    const qc = new Quartzcron('0 0 12,22 L * ? 2025/5');
  • 1 object corresponding to a valid expression
    const exp = {
        s:0, i: 0, h: 0,
        dom:'*', m:'*', dow: '?', y: '*'
    }
    const qc = new Quartzcron(exp);

throws an exception when the resulting expression is not valid.

get the quartz cron expression 🧊

Invoke out() ƒunction on the quartzcron instance to get the related expression

qct.out(); // -> "0 0 12,22 L * ? 2025/5"

validation API ✅

Validation can be done on the quartzcron instance just invoking the validate ƒunction. Pass the string to be evaluated as parameter.

When nothing is passed it will validate the expression it would get from out (as useful as expect(true).toBe(true)).

qct.validate('0 0 12,22 L * ? 2025/5')
// -> { valid: true, errors:[]}

returning an object shaped like follows:

{ valid: Boolean, errors:[String]}

Alternatively a static method is available:

QuartzCron.validate(yourExp)
// -> { valid: ?, errors:[?]}

composition API 🧱

Almost all 7 fields composing the final cron expression are independent.
The only exception is represented by the "days of month" (4th field) and the "days of week" (6th field) cause they cannot coexsist.
Within the involved months/years:

  • dom sets target days referencing the month
  • dow sets target days referencing the week

One of the two must hold something valid different from ?
and the other one must just contain ?.

week days and month names aliases

For weeekdays one can use seamlessly:

[1,2,3,4,5,6,7]
// OR 
['SUN','MON','TUE','WED',
    'THU', 'FRI', 'SAT']

similarly for months:

[1,2,3,4,5,6,7,8,9,10,11,12]
// OR 
['JAN','FEB','MAR','APR',
 'MAY','JUN','JUL','AUG',
 'SEP','OCT','NOV','DEC']`.  

seconds ⏱️

  • everySecond()
    no explanation needed; still one should consider that this command will only update the s to *.
    Which minute/s will actually be part of the target depends on how the instance was constructed.
    If no other command is executed the target will be from 0-th to 59-th second of the first minutes of the first hour of the following day and this is cause the default values are 0 0 0 * * ? *.
    This clearly applies similarly also for almost all other commands.

  • everyNSeconds(x, start = 0)
    every x seconds (starting from start)

  • atSecond(sec, cad = false)
    overrides any previous value set there;
    when cadence is not passed can be called passing multiple comma separated values within [0, 59]

  • atSecondAdd(sec, cad = false)
    additive version of the previous setter.

  • betweenSeconds(from, to, every)
    all seconds from from to to seconds; optionally set the cadence passing an every integer.

  • betweenSecondsAdd(from, to, every)
    additive version of the previous setter.

minutes ⏱️

  • everyMinute()
    no explanation needed

  • everyNMinutes(x, start = 0)
    every x minutes (starting from start)

  • atMinute(min, cad = false)
    overrides any previous value set there;
    when cadence is not passed can be called passing multiple comma separated values within [0, 59]

  • atMinuteAdd(min, cad = false)
    additive version of the previous setter.

  • betweenMinutes(from, to, every)
    all minutes from from to to minutes; optionally set the cadence passing an every integer.

  • betweenMinutesAdd(from, to, every)
    additive version of the previous setter.

hours ⏱️

  • everyHour()
    no explanation needed

  • everyNHours(x, start = 0)
    every x hours (starting from start)

  • atHour(h, cad = false)
    no explanation needed;
    overrides any previous value set there;
    when cadence is not passed can be called passing multiple comma separated values within [0, 23]

  • atHourAdd(h, cad = false)
    adds h to the list of already set hours (0 there by default); as in the previous can pass multiple values comma separated (when cadence is not passed).

  • betweenHours(from, to, every)
    all hours from from to to hours; optionally set the cadence passing an every integer.

  • betweenHoursAdd(from, to, every)
    additive version of the previous setter.

day of month / day of week 📆

  • everyDay()
    no explanation needed

    qct.everyDay()
    // { dom: '*', dow:'?', ...}
  • everyNDays(x, y)
    every x [1-31][1-31] days starting from yth day [1-31] of the target months.

    qct.everyNDays('13, 10) 
    // { dom: `10/13`, dow: '?', ...}
  • atWeekDay(wd, cad = false)
    overrides any previous value set there; when cadence is not passed even here more than one comma separated value can be passed.

    qtc.atWeekDay(4)
    // { dom: `?`, dow: 4, ...}
    qtc.atWeekDay(4, 3)
    // { dom: `?`, dow: '4/3', ...}
    qtc.atWeekDay('4,5')
    // { dom: `?`, dow: '4,5', ...}
    qtc.atWeekDay('2-5', 2) // or qtc.atWeekDay('2-5/2')
    // { dom: '?`, dow: '2-5/2', ...}
  • atWeekDayAdd(wd, cad = false)
    every wd in [1,7] or (...and corresponding to) {SUN,MON,TUE,WED,THU,FRI,SAT}; adds one more weekday in the current (default empty) list.

    qtc.atWeekDayAdd('MON')
    // { dom: `?`, dow: 'MON', ...}
    qtc.atWeekDayAdd('WED', 2)
    // { dom: `?`, dow: 'MON,WED/2', ...}
    qtc.atWeekDayAdd('FRI-SAT')
    // { dom: '?`, dow: 'MON,WED/2,FRI-SAT', ...}
  • everyWeekDay()
    shortcut to set Saturnday and Sunday

    qtc.everyWeekDay()
    // { dom: `?`, dow: '2-6', ...}
  • everyWeekEnd()
    shortcut to set Saturnday and Sunday

    qtc.everyWeekEnd()
    // { dom: `?`, dow: '7-1', ...}
  • atMonthDay(dom, cad = false)
    sets the target day of month, can be:

    • *: all days
    • n: with n in [1,31]
    • n,m,...: comma separated values all in [1,31]
    • n/c: every c starting from n
    • n-m: from n to m (in [1,31])
    • n-m/c: from n to m (in [1,31]) with c cadence for the last two examples there's also an on purpose method named betweenMonthDays
    qtc.atMonthDay('*') //same as qtc.everyDay()
    // { dom: '*', dow: '?', ...} 
    qtc.atMonthDay(10)
    // { dom: '10', dow: '?', ...} 
    qtc.atMonthDay('10,20')
    // { dom: '10,20', dow: '?', ...} 
    qtc.atMonthDay('10-20')
    // { dom: '10-20', dow: '?', ...} 
    qtc.atMonthDay('10-20/2')
    // { dom: '10-20/2', dow: '?', ...} 
    qtc.atMonthDay('10/2')
    // { dom: '10/2', dow: '?', ...} 
  • atMonthDayAdd(dom, cad = false)
    allows to add one or more days to the existing target

    qtc.atMonthDayAdd('10')
    // { dom: '10', dow: '?', ...} 
    qtc.atMonthDayAdd('13/3')
    // { dom: '10,13/3', dow: '?', ...} 
    qtc.atMonthDayAdd('23')
    // { dom: '10,13/3,23', dow: '?', ...} 
  • betweenMonthDays(from, to, every)
    set target days from from to to with, if passed > 1, a cadence bigger than 1

    qtc.betweenMonthDays(10, 20) 
    // { dom: '10-20', dow: '?', ...} 
    qtc.betweenMonthDays(10, 20, 2) 
    // { dom: '10-20/2', dow: '?', ...} 
  • onLastMonthDay
    set the target day to the last day of the target months

    qtc.onLastMonthDay()  // { dom: 'L', dow: '?', ...} 
  • onFirstMonthWeekDay
    set as target day the first weekday of the month (working day)
    (same as qtc.onClosestWorkingDayToTheNMonthDay(1))

    qtc.onFirstMonthWeekDay() // { dom: '1W', dow: '?', ...} 
  • onLastMonthWeekDay
    set as target day the last weekday of the month (working day)

    qtc.onLastMonthWeekDay() // { dom: 'LW', dow: '?', ...} 
  • onLastMonthNWeekDay(x)
    set as target day the last selected week day of the month

    qtc.onLastMonthNWeekDay(2)
    // last monday of the month 
    // { dom: '?', dow: '2L', ...} 
  • onNDayBeforeTheEndOfTheMonth(n)
    set as target the X-th day before the end of the month

    qtc.onNDayBeforeTheEndOfTheMonth(9)
    // nine days before the end of the month 
    // { dom: 'L-9', dow: '?', ...} 
  • onClosestWorkingDayToTheNMonthDay(x)
    set as target the nearest weekday (working day) to the x-th day of the month

    qtc.onClosestWorkingDayToTheNMonthDay(15)
    // the closest woring day (mon->fri) to the 15th 
    // { dom: '15W', dow: '?', ...} 
  • onNWeekDayOfTheMonth(n, wd)
    set as target the n-th week day of the month

    qtc.onNWeekDayOfTheMonth(4, 2)
    // the 4th tuesday 
    // { dom: '?', dow: '2#4', ...} 

months 📆

  • everyMonth()
    no explanation needed

  • everyNMonths(freq, start)
    every freq months (starting from start)

  • atMonth(m, cad = false)
    overrides any previous value set there;
    when cadence is not passed can be called passing multiple comma separated values within [1, 12] or [JAN -> DEC]

  • atMonthAdd(m, cad = false)
    adds m to the list of already set months; as in the previous can pass multiple values comma separated (when cadence is not passed).

  • betweenMonths(from, to, every)
    all months from from month to to month; optionally set the cadence passing an every integer.

years 📆

  • everyYear()
    no explanation needed

  • everyNYears(freq, start)
    every x years (starting from start)

  • atYear(y, cad = false)
    overrides any previous value set there;
    when cadence is not passed can be called passing multiple comma separated values within [1970, 2099]

  • atYearAdd(y, cad = false)
    adds min to the list of already set minutes; as in the previous can pass multiple values comma separated (when cadence is not passed), within [1970, 2099]

  • betweenYears(from, to, every)
    all years from from year to to year; optionally set the cadence passing an every integer.

more ➕

This library was primarily designed to achieve a quite simple task: compose the expression through methods; then validation, occurrences, ..
The occurrences composition depends 100% on two data:

  1. the current expression
  2. the fererence date

thus, calculate the occurrences of an already existing expression (for example given back from and endpoint) one could override manually one or more elements in the expression s,i,h,dom,m,dow,y

inst.elements.s = 1

OR use

  • updateExp(exp)
    updates the current instance expression, handles

    • an expression string
      when invalid throws and exception
    • an object corresponding to an expression
      {
          s:0, i: 0, h: 0,
          dom:'*', m:'*', dow: '?', y: '*'
      }

    throws an exception when the resulting expression is not valid

range resolvers 🛸

Range resolvers might be useful, initially only part of an internal utility, now are exposed in a static object.

qct.out()// -> 45/2 1,2,3 15-19 L 1/2 ? *
Quartzcron.solvers.solve_0_59_ranges(qtc.elements.s)
// -> [45,47,49,51,53,55,57,59]
Quartzcron.solvers.solve_0_59_ranges(qtc.elements.i)
// -> [1,2,3]
Quartzcron.solvers.solve_hours_ranges(qtc.elements.h)
// -> [15,16,17,18,19]
Quartzcron.solvers.solve_dom(2025, 2, qtc.elements.dom)
// -> [28]
Quartzcron.solvers.solve_month_ranges(qtc.elements.m)
// [1,3,5,7,9,11]
Quartzcron.solvers.solve_dow(2025, 2, qtc.elements.dow)
// -> []
Quartzcron.solvers.solve_year_ranges(qtc.elements.y)
// -> [2025, ..., 2099] // here supposing we are in 2025

Clearly enough the two about dom and dow are function since they strictly depend on the year and the month.
Solvers come quite in hand when from an expression one needs the actual targets.

Months and week days will be always resolved numerically, thus [1,12] and [1,7] respectively.

Occurrences 🔭

From an instance call the next ƒunction:

const nextOccurrence = qct.next().map(s=>s.toString())
// ["Mon Jan 01 2024 02:00:00 GMT+0100"],

this ƒunction accepts three options:

  • n: the number of occurrences needed (1 is the default)
  • date: a reference date (js Date object) to be used as present date (default is the current date). No dates before the present date will be returned.

🩼 Limitations and plans 💡

⏱️ Timezones 🗺️

If the plan to use that utility on a browser the chances the client and server run on different timezones is quite high.

A workaround would be so set the timezone to UTC on the server and in the UI explicitly inform the user that all dates & times are UTC.

For the moment this library relies 100% on UTC

The plan is to provide 2 static methods to allow to set the timezones for the client and the server.

QuartzCron.useClientLocalTimezone() // auto (e.g -2)
// or useClientUTCTimezone()

QuartzCron.setServerTimezone("America/Los_Angeles"); // +6
// or setServerUTCTimezone()

Descriptions 📜

Having a quick way to get a user readable internationalized description out of an expression is quite useful.
Meanwhile give a try to the awesome cronstrue npm package.