追記3
もろもろ作り直した
追記2
いろいろ間違っていた…
追記
1-10/3
とか MON-FRI/3
とかを忘れていたので作り直し…
cron式をパースするGolangのライブラリを書いた。
モチベーションとしては、Amazon EventBridgeのcron式を書くときに毎回JSTからUTCに変換するのがめんどくさくて「いつの日かterraformにユーザー定義関数が追加されるときに備えて(くるのか?)タイムゾーンをシフトするライブラリを書こう」という感じで始めた。
パーサー自体は alecthomas/participle でサクサク書けたが
時間をシフトする処理は難しくて諦めた。
たとえば 0 5-10 ? * FRI *
のようなJSTのcron式をUTCに直すと 0 20-23 ? * THU *
と 0 0-1 ? * FRI *
の2つに分かれる。cron式の分解と合成のうまいやり方が思いつかなかった。自分が無知なだけで良い方法があるのかも
EventBridgeの仕様を参考にしたが、いくつか知らない仕様があって勉強になった。
たとえば
0 0 ? * 3#2 *
: 毎月の第二水曜日の00:00に実行0 0 8W * ? *
: 毎月8日に一番近い平日に実行- 2022/10だと10/8は土曜日なので10/7に実行される
0 0 L * ? *
: 毎月の最終日の00:00に実行0 0 ? * L *
: 毎週の最終日、つまり土曜日の00:00に実行
など
オラクルのドキュメントでも同様の記号が書かれていたので、AWSだけの仕様ではない…ような気がする(オラクルのほうのC
の意味はわからなかった)
Day of weekの増分の仕様
時間のシフトを計算する処理は諦めたが、ASTだけあってもあまり有用ではなさそうなので、ついでとある時刻がcronのスケジュールにマッチするかどうか判別する機能も付けてみた。しかし曜日(Day of week)フィールドの処理で結構ハマってしまった…
増分の表現は、分子≒オフセット・分母≒増分と、分数のようなかたちで書く。(改めて書くほどのことでもないが)
- 分・時フィールドの場合
*/3
は0/3
と同じで、実行される時間は0 3 6 9 …
となる。1/3
の場合は1 4 7 10 …
- 日(Day of month)フィールドだと、
*/3
は1/3
と同じ。実行される日付は1 4 7 10 13…
- さらに年フィールドの場合は、
*/3
は1970/3
と同じになる模様(AWSコンソール調べ)
曜日(Day of week)フィールドの場合、*/3
は 1/3
と同じ結果になる。
曜日の数値 1-7 は MON-SUN にマップされているので MON(1) THU(4) SUN(7)…
となると思いきや、実はSUN始まりで SUM(0) WED(3) SAT(6)…
となる。
曜日(Day of week)を考えないと、時刻をnとしたときに n % (分母) == (分子)
という計算でその時刻がcronを実行する時刻なのか判別できるが、曜日(Day of week)は他と分子の扱いが違うので特別な処理を挟む必要があって、だいぶ実装に時間が取られてしまった。
これLinuxでも同様の動作なのかな
あと、EventBridgeだとMON-SUNが1-7に対して、プログラム上だとSUN-SATが0-6になっているので細々した変換が必要で、そちらもすこしめんどくさかった。