LoginSignup
1
1

More than 5 years have passed since last update.

node-cronにスケジュール書式を渡しすぎると値がずれ想定外のスケジュールになる

Posted at

結論

書式が足りない部分はデフォルトの値が適用される。
簡単に言えば、書式設定が右にずれる。
多すぎた場合はこの少なかったとき用の計算が意図せず働き、設定が左にずれる。

例えば、

書式が5個と通常よりひとつ少なかった場合。
1 2 3 4 5

本来秒数指定の場所の左端の値(1)は、右にずれて分数指定の記述として扱われる。
(秒数指定にはデフォルト値の0が指定される)
0 1 2 3 4 5

書式が7個と通常よりひとつ多かった場合。
1 2 3 4 5 6 7
本来日数として指定された値は、左にずれて時数指定の記述として扱われる。
(週の指定にはデフォルト値の*が指定される)
2 3 4 5 6 7 *

Linuxなどのcrontabは値を5つ指定する(standard cron syntaxと表現)=node-cronではできる秒数指定ができない。
node-cronにstandard cron syntaxが指定されたとき、エラーと意図の相違なくパースさせる処理が、渡しすぎた場合にエラーなく意図と相違あるパースになってしまった。

ライブラリ

kelektiv/node-cron: Cron for NodeJS.

Cron Ranges

When specifying your cron values you'll need to make sure that your values fall
within the ranges. For instance, some cron's use a 0-7 range for the day of
week where both 0 and 7 represent Sunday. We do not.

  • Seconds: 0-59
  • Minutes: 0-59
  • Hours: 0-23
  • Day of Month: 1-31
  • Months: 0-11 (Jan-Dec)
  • Day of Week: 0-6 (Sun-Sat)

指定できる場所は6個あることが特徴です。

バージョン

"cron": "^1.4.0"

コード

node-cron/cron.js at 1ec1e81eba34fc0b2c034ea65c943ba62b3e16b4 · kelektiv/node-cron

    var timeUnits = [
        'second',
        'minute',
        'hour',
        'dayOfMonth',
        'month',
        'dayOfWeek'
];


    CronTime.parseDefaults = ['0', '*', '*', '*', '*', '*'];

    CronTime.prototype = {
        _parse: function() {
            var aliases = CronTime.aliases;
            var source = this.source.replace(/[a-z]{1,3}/gi, function(alias) {
                alias = alias.toLowerCase();

                if (alias in aliases) {
                    return aliases[alias];
                }

                throw new Error('Unknown alias: ' + alias);
            });
            var split = source.replace(/^\s\s*|\s\s*$/g, '').split(/\s+/);
            var cur;
            var i = 0;
            var len = timeUnits.length;

            for (; i < timeUnits.length; i++) {
                // If the split source string doesn't contain all digits,
                // assume defaults for first n missing digits.
                // This adds support for 5-digit standard cron syntax
                cur = split[i - (len - split.length)] || CronTime.parseDefaults[i];
                this._parseField(cur, timeUnits[i], CronTime.constraints[i]);
            }
        },

sourceが渡した書式です。

* * * * *
ならStandard cron syntax。左端はです。

* * * * * *
ならcron-node用のsyntax。左端はです。

0 0 0 * * * *
これが今回誤って渡していた書式です。毎日0時に実行してほしいつもりが、毎時実行になっていました。

cur = split[i - (len - split.length)] || CronTime.parseDefaults[i];
このコード、lenは6で固定です。

node-cronのsyntaxが渡された場合、(len - split.length)は常に0なので、そのままの値が順番に渡されます。


Standard cron syntaxが渡されたとき、i = 0、秒数のパースの際は、

cur = split[0 - (6 - 5)] || CronTime.parseDefaults[0];

split[-1]が参照されることになり、当然undefinedなのでparseDefaults[0]が適用されます。

以降、Standard cron syntaxで指定された分数指定、split[0]は、i = 1のとき、

cur = split[1 - (6 - 5)] || CronTime.parseDefaults[1];
となりsplit[0]がnode-cronのsyntaxでの分数([1])として入り、整合性がとれます。


0 0 0 * * * *
を渡した場合。
split.lengthは7で、(len - split.length)は常に-1
- (len - split.length)なので常に+1されることになります。

i = 0の場合、

cur = split[0 + 1] || CronTime.parseDefaults[0];
で秒数にsplit[1]、分と考えていたものがズレて入りました。

時を指定するi = 2では
cur = split[2 + 1] || CronTime.parseDefaults[2];
split[3] = *が時間の値として処理され、毎時実行になりました。


コードのコメントにあるように、この引き算は5-digit standard cron syntaxをサポートするためのものです。
なので、7-digit cron syntaxなどという不正な書式はエラーにしてしまうことが、本来は正しいのかもしれません。


CronTime day of week is non-standard (Sunday is 1, not 0) · Issue #36 · kelektiv/node-cron

サポートが入った経緯。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1