ontime

a human-readable cron

JavaScript

ontime is a cron-like job scheduler with readable time expressions.

For example, the following code invokes the given function on 4:30AM and 9AM every day (i.e., twice a day).

var ontime = require('ontime')

ontime({
    cycle: [ '04:30:00', '9:00:00' ]
}, function (ot) {
    // do your job here
    ot.done()
    return
})

It supports:

but does not yet support:

Options

Options to ontime control the cycle of a job, choose between a local time and UTC, enable to keep track of the last day of a month and so on. In explaining options, each section header shows the option it explains and its default value in parentheses.

Time expressions (cycle: '')

ontime determines the cycle of a job based on the format of time expressions. The time expression basically has the form of an ISO-8601 Date Format, YYYY-MM-DDThh:mm:ss where YYYY indicates a year, MM a month, DD a day of the month, hh an hour, mm a minute and ss a second, except that:

The time expression is given to ontime through the cycle option. You can give a single expression of the string type like '01-01T12:00:00' or multiple ones as an array of strings like [ '01-01T12:00:00', '7-1T0:0:0' ].

Yearly jobs

The year part(YYYY-) should be omitted to specify yearly jobs.

ontime({
    cycle: '2-9T00:00:00'
}, function (ot) {
    console.log('my birthday!')
    ot.done()
    return
})

This code prints on February 9 every year.

Note how the last day of February is handled on a leap year. If you set the time expression to February 29 as in '2-29T00:00:00', the job will be triggered only in leap years. See the keepLast option to change this behavior.

Monthly jobs

The year and month parts(YYYY-MM-) should be omitted for monthly jobs.

ontime({
    cycle: [ '1T12:00:00', '15T12:00:00' ]
}, function (ot) {
    console.log('review the project')
    ot.done()
    return
})

This code prints on the 1th and 15th days of each month.

Note how the last day of a month is handled. If you set the time expression to the 31th day as in 31 23:59:59, the job will run only on January, March, May, July, August, October and December since others have no 31th day. Use the keepLast option to change this behavior.

Daily jobs

The whole date part(YYYY-MM-DDT) should be omitted for daily jobs; note that the separator T should be also dropped.

ontime({
    cycle: '12:00:00'
}, function (ot) {
    console.log('lunch time!')
    ot.done()
    return
})

This code prints on noon every day.

Weekly jobs

Weekly jobs have a different format to specify a day of a week.

ontime({
    cycle: [ 'Sunday 12:00:00', 'sat 12:00:00' ]
}, function (ot) {
    console.log('weekend!')
    ot.done()
    return
})

This code prints on Saturday and Sunday every week.

Hourly jobs

The date and hour parts(YYYY-MM-DDThh:) should be omitted for hourly jobs.

ontime({
    cycle: [ '00:00', '30:00' ]
}, function (ot) {
    console.log('30 mins to next run')
    ot.done()
    return
})

This code prints every 30 minutes (twice an hour).

Jobs on every minute

By omitting all units except for seconds, a job can be invoked every minute.

ontime({
    cycle: [ '10', '30', '50' ]
}, function (ot) {
    console.log('20 secs to next run')
})

This code prints on the 10th, 30th and 50th seconds of every minute.

Jobs on every second

An empty string denotes jobs that get started every second.

var count = 0

ontime({
    cycle: '',
}, function (ot) {
    console.log(++count)
    ot.done()
    return
})

This counts up every second.

Jobs on specified times

You can trigger your job on explicitly specified times.

ontime({
    cycle: [ '2100-1-9 9:00:00',
             '2200-1-9 9:0:0' ]
}, function (ot) {
    console.log('what is this day?')
    ot.done()
    return
})

This prints on 9AM of 9 January 2100 and the same time of 2200 if you're using node.js until then.

Mixing different cycles

In order to keep the model and the interface simple, a single type of job cycle is allowed for each invocation of ontime. Mixing different cycle types can be achieved by introducing multiple invocations to ontime as in:

ontime({
    cycle: [ '01-09 11:30:00',        // yearly
             'Saturday 12:00:00' ]    // weekly
}, job)    // mixed types of cycle result in error

ontime({
    cycle: '01-09 11:30:00'    // yearly
}, job)
ontime({
    cycle: 'Sat 12:0:0'    // weekly
}, job)

Skipping steps (step: 1)

The step option enables a job to be skipped periodically. Setting it to n forces ontime to skip a given job n-1 times after a run, which leads to launching the job eveny n cycles.

ontime({
    cycle:    '31T00:00:00',
    keepLast: true,
    step:     3
}, function (ot) {
    console.log('every 3 months')
    ot.done()
    return
})

This prints on the last day of a month every three months.

Note how this option interacts with the single option.

A local time vs. UTC (utc: false)

Setting the utc option to true changes ontime to interpret the time expressions as UTC.

This is useful

Preserving a single instance (single: false)

ontime launches a job on its scheduled time. If the job takes longer than the time interval of the cycle, more than one instance of the job may run at the same time. The single option keeps another instance of a job from starting if there is already a running one.

To be precise, with single set to false, ontime schedules the next run at the start of the current run. Changing that to true has the next run scheduled when the ot.done() method is invoked by a user.

The following two diagrams show the difference, where labelled | and + denote time spots to start new instances, and * indicates their execution.

ontime({
    cycle:  [ A, B ],
    single: false    // default
}, function (ot) {
    // ...
    ot.done()
    return
})

     A          A    B     A         BA
- - -|----------|----+-----|---------+|- - -
     *************
                *********
                     ****
                           *****

ontime({
    cycle:  [ A, B ],
    single: true
}, function (ot) {
    // ...
    ot.done()
    return
})

     A          A    B     A         BA
- - -|----------|----+-----|---------+|- - -
     *************   *********       ****

Exclusiveness of job execution is guaranteed only within a single invocation to ontime. Two difference invocations to ontime cannot interpose with each other.

Keeping the last day of a month (keepLast: false)

It is sometimes necessary to run a job on the last day of each month, which have been replaced with doing it on the first day of each month instead. By setting the keepLast option to true, ontime automatically adjusts the date part(DD) to the last day according to the value of the month part(MM) if necessary.

ontime({
    cycle:    '31T10:00:00',
    keepLast: true
}, function (ot) {
    console.log('the last day')
    ot.done()
    return
})

This code prints on the 31th day of a month when the month has the 31th day, on the 28th or 29th when February, or on the 30th day otherwise. Another example goes for yearly jobs:

ontime({
    cycle:    '2-29T10:00:00',
    keepLast: true
}, function (ot) {
    console.log('the last day of Feb')
    ot.done()
    return
})

This code prints on 29 February on a leap year and on 28 February otherwise.

Logging messages (log: false)

ontime has a very simple form of logging that is useful when checking if your configuration works as intended. It can be turned on by setting the log option to true.

Methods

A job function should be defined as to accept at least one argument that is referred to as ot in this document. The argument contains these methods:

How to install

This package does not provide an automated way to build or install the library except using npm because ontime is intended to runs on top of node.js. If you have node.js in your system,

npm install ontime

brings the latest version of ontime and installs it with its all depending packages.

Repository

ontime uses git as its repository to publish on github. You can browse the repository through the web, or clone it as follows:

git clone https://github.com/mycoboco/ontime.git

If you want to contribute to the project, simply fork it and send me pull requests.

Recent commits are:and more.

Issue tracker

ontime employed github for its issue tracker. If you'd like to file a bug or ask a question, do not hesitate to post a new issue; a self-describing title or a short text to deliver your idea would be enough. Even if issues I have posted are all written in English, nothing keeps you from posting in Korean.

License

Copyright (C) 2014 by Jun Woong.

This package is a cron-like job scheduler.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

Questions or suggestions?

I'd be glad to hear your opinion. Do not hesitate to contact me via email (woong.jun at gmail.com).