2026 年の祝日は 18 日ある。官報に載るのは 16 日ぶんだ
祝日テーブルを自前で持っている人は、毎年こういう作業をしている。内閣府の一覧から祝日を転記して——5 月 6 日と 9 月 22 日を、手で足す。
この 2 日は法定祝日ではない。祝日法が定める計算の結果だ:
- 振替休日: 祝日が日曜に当たるとき、「その日後においてその日に最も近い『国民の祝日でない日』」が休日になる(2026 年は 5/3 憲法記念日が日曜→ 5/4 も 5/5 も祝日なので、 滑って 5/6 が振替になる)
- 国民の休日: 前日と翌日の両方が祝日である平日は、休日になる(2026 年は敬老の日 9/21 と秋分の日 9/23 に挟まれた 9/22)
規則が分かっているのに、テーブルには結果だけを書く。だから毎年、誰かが計算して、転記して、ときどき忘れる。
規則をそのまま式にする
Kairos は列の代数なので、条文の構造をだいたいそのまま書ける。法定祝日 16 日を statutory として:
nonHoliday = everyDay \ statutory substitutes = statutory |> filter(d => weekday(d) == Sun) |> roll(Following, on: nonHoliday) sandwiched = ((statutory |> shift(+1, unit: day)) & (statutory |> shift(-1, unit: day))) \ statutory holidays = statutory | substitutes | sandwiched
3 行それぞれが条文に対応する。
substitutes(振替休日)——日曜の祝日を選び、roll(Following, on: nonHoliday) で「その後における最初の非祝日」へ倒す。条文の「最も近い国民の祝日でない日」が roll の軸そのもので、5/3 → 5/4(祝日)→ 5/5(祝日)→ 5/6 と滑る連鎖を、式は何も特別扱いしていない。軸が「非祝日」だから勝手に滑る。
sandwiched(国民の休日)——「前日も翌日も祝日」を、祝日列を ±1 日ずらした二つの列の積で書く。statutory |> shift(+1) は「祝日の翌日」の列、shift(-1) は「祝日の前日」の列。両方に属する日=挟まれた日。最後に \ statutory で祝日自身を除けば、9/21〜9/23 の連なりから 9/22 だけが残る。
動かす
16 日を入れて評価すると、18 点が出る:
2026-01-01 2026-01-12 2026-02-11 2026-02-23 2026-03-20 2026-04-29 2026-05-03 2026-05-04 2026-05-05 2026-05-06 ← 振替休日(導出) 2026-07-20 2026-08-11 2026-09-21 2026-09-22 ← 国民の休日(導出) 2026-09-23 2026-10-12 2026-11-03 2026-11-23 # ⚠ 範囲外 2026-01-01..2026-01-02(statutory covering 2026-01-01..2026-12-31) # ⚠ 範囲外 2026-12-31..2027-01-01(statutory covering 2026-01-01..2026-12-31) # 被覆サマリ # statutory covering 2026-01-01..2026-12-31 残走路 0 日
(← は筆者注。末尾の ⚠ と被覆サマリは前回書いた「データの鮮度」の器がここでも生きているだけ——2026 年分しか主張していないデータで年境界を評価しているので、端に註釈が付き、残走路は 0 日=「そろそろ 2027 年分を」と言っている。)
導出分だけ取り出すなら holidays \ statutory——5/6 と 9/22 の 2 点だけが出る:
2026-05-06 2026-09-22 # ⚠ 範囲外 2026-01-01..2026-01-02(statutory covering 2026-01-01..2026-12-31) # ⚠ 範囲外 2026-12-31..2027-01-01(statutory covering 2026-01-01..2026-12-31) # 被覆サマリ # statutory covering 2026-01-01..2026-12-31 残走路 0 日
前回の話とつながる
前回、祝日テーブルは黙って腐るという話を書いた。腐るのはデータだからで、データの覆域(covering)と残走路で鮮度を監視する、というのが前回の答えだった。
今回の話はその前段にある。「祝日テーブル」と呼んでいたものの中に、データと法則が混ざっていた。法定祝日 16 日は官報由来のデータで、これは覆域管理の対象。だが振替と国民の休日は法則で、一度式に書けば毎年ついてくる。分けると、覚えて更新すべきものが 16 行に減り、「5/6 を足し忘れる」という事故のクラスが構造ごと消える。
cron にはこの区別を書く場所がない。データも法則も、結局は誰かの頭の中と転記作業に畳み込まれる。式で書けるということは、法則を法則のままレビューできるということだ——この 3 行が祝日法の条文と一致しているかは、コードレビューで(あるいはこの記事のコメント欄で)確認できる。テーブルの 18 個の日付が正しいかを目視するのとは、確認している内容が違う。
(コード例は Playground でそのまま動く。定義の全文は 公開リポジトリの例 ——言語仕様の代表例 §7.5 でもある。現在 RC・1.0 準備中。)

コメント