
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
quartzcron
Advanced tools
[](https://codecov.io/gh/fedeghe/quartzcron)  [
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
]
*/
Constructor can handle:
0 0 0 * * ? *
const qc = new Quartzcron();
const qc = new Quartzcron('0 0 12,22 L * ? 2025/5');
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.
Invoke out() ƒunction on the quartzcron instance to get the related expression
qct.out(); // -> "0 0 12,22 L * ? 2025/5"
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:[?]}
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 monthdow sets target days referencing the weekOne of the two must hold something valid different from ?
and the other one must just contain ?.
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']`.
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.
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.
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.
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 daysn: with n in [1,31]n,m,...: comma separated values all in [1,31]n/c: every c starting from nn-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 betweenMonthDaysqtc.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', ...}
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.
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.
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:
thus, calculate the occurrences of an already existing expression (for example given back from and endpoint) one could unwisely
override manually one or more elements in the expression s,i,h,dom,m,dow,y
inst.elements.s = 1
OR use better
updateExp(exp)
updates the current instance expression, handles
{
s:0, i: 0, h: 0,
dom:'*', m:'*', dow: '?', y: '*'
}
throws an exception when the resulting expression is not valid
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.
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.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()
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.
FAQs
[](https://codecov.io/gh/fedeghe/quartzcron)  [
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.