追記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になっているので細々した変換が必要で、そちらもすこしめんどくさかった。