毎週火曜の習い事、8 月と年末年始は休み——「除く」を 1 本の式で書く

Kairos

1. カレンダーアプリは「この回だけ削除」を繰り返す

子どもの習い事は毎週火曜の 17 時、ただし 8 月はお休みで、年末年始も休み。町内の資源ごみは第 1・第 3 水曜だが、年末年始は収集がない。ジムは毎週月曜が定休で、年末年始とお盆は休館。「毎週」「毎月」に「ただし」が付く予定は、暮らしの中にいくらでもある。

スマホのカレンダーに「毎週火曜 17 時」を入れるところまではよい。8 月の 4 回と年末年始の 1 回は、その回を開いて「この予定のみ削除」を 5 回繰り返す。来年になれば、また 5 回。cron で書く人も同じで、「翌年が来る前に手でコメントアウトする」運用が定番になっている。

どちらも、「除く」という言葉が道具の側に無いことが人間側に波及した姿である。除く言葉があれば、定義の中で閉じる。

2. 火曜の列から、8 月と年末年始の列を引く

# 毎週火曜 17 時の習い事。ただし 8 月と年末年始は休み
premise JP { calendar-system: Gregorian; tz: "Asia/Tokyo"; wkst: Mon }

@JP
tue = everyDay |> filter(d => weekday(d) == Tue)
august = everyDay |> filter(d => month(d) == 8)
yearEnd = [2026-01-01..2026-01-03, 2026-12-29..2026-12-31] covering: 2026..2026
(tue \ (august | yearEnd)) |> at(T17:00)
  • tue は毎週火曜の列。august は 8 月の毎日の列。yearEnd は年末年始の表。
  • | が和、\ が差。「8 月と年末年始を束ねて、火曜の列から引く」。
  • 引いてから at(T17:00) で 17 時を付ける(目覚ましの回と同じ器)。
図 1: 2026 年の火曜 52 回を月ごとに並べた帯。8 月の 4 回と 12 月 29 日が白抜きで、残る 47 回が式の返す点。
図 1: 2026 年の火曜 52 回を月ごとに並べた帯。8 月の 4 回と 12 月 29 日が白抜きで、残る 47 回が式の返す点。

夏休みの前後を聞いてみる:

$ kairos list --from 2026-07-20 --to 2026-09-10 lesson.kairos
2026-07-21T17:00
2026-07-28T17:00
2026-09-01T17:00
2026-09-08T17:00
# 被覆サマリ
#   yearEnd covering 2026-01-01..2026-12-31 残走路 113 日

8 月の火曜 4 回(8/4・11・18・25)が消え、7 月末と 9 月頭はそのまま残る。2026 年の全体では、火曜 52 回から47 回になる。「この回だけ削除」を 5 回する代わりに、\ を 1 回書いた。

3. 「8 月」は規則で書ける、「年末年始」は表が要る

2 つの除外の書き方が違うのには理由がある。

  • 8 月は規則で決まる。「月が 8 なら」は毎年同じなので、month(d) == 8 と書けば表は要らない。
  • 年末年始は人が決める。12 月 29 日から 1 月 3 日までなのか、28 日から 4 日までなのかは、教室や自治体の案内で 決まる。規則ではなく事実なので、表に書く。表には covering: 2026..2026——「この表は 2026 年の分」——を添える。

祝日も同じで、習い事が祝日も休みなら、祝日の表を引く列に足す。2026 年は 5 月 5 日(こどもの日)・9 月 22 日(国民の休日)・11 月 3 日(文化の日)が火曜に当たるので、tue \ (august | yearEnd | national) と書けば 47 回から 44 回になる。祝日表の書き方は「月 2 回」の回の §3 にある。

4. 表の外に出ると、鳴らさずに言う

年末年始の表は 2026 年分しか書いていない。年を跨いで聞いてみる:

$ kairos list --from 2026-12-01 --to 2027-02-01 lesson.kairos
2026-12-01T17:00
2026-12-08T17:00
2026-12-15T17:00
2026-12-22T17:00
# ⚠ 範囲外 2027-01-01..2027-02-01(yearEnd covering 2026-01-01..2026-12-31)
# 被覆サマリ
#   yearEnd covering 2026-01-01..2026-12-31 残走路 -31 日

12 月の 4 回は出て、12 月 29 日は消える。2027 年 1 月の火曜は 1 つも出ない。代わりに「2027 年 1 月は表の外だ」という1 行が出る。2027 年の年末年始がいつ休みかは表に無いので、言語は 1 月 5 日に 17 時を付けない。「鳴る」のでも「鳴らない」のでもなく、「表が無い」と言う。

カレンダーアプリは黙って 1 月 5 日に通知を出す。去年「この回だけ削除」した記憶は、今年の 1 月には効かない。Kairos では、表の外は黙らない(祝日表は黙って腐る・目覚ましの回の §5)。直し方は、表に 2027 年の年末年始を足して covering: を 2026..2027 に延ばすだけである。

5. 落とし穴

  • 引くのは、時刻を付ける前。tue \ august は日の列どうしの引き算で、同じ日なら消える。17 時を付けた後に 日の列を引いても、17 時の点と日の点は別物なので消えない。「引いてから at」の順を守る。
  • 「8 月」の境界は暦の月。8 月 1 日から 31 日まで。夏休みが 7 月 21 日から 8 月 31 日なら、それは規則ではなく 事実なので、年末年始と同じく表に書く([2026-07-21..2026-08-31])。
  • 評価範囲は定義の外。「今年の 4 月から始めた習い事」は、定義に 4 月を書くのではなく、聞くときの --from を 4 月にする。定義は「いつでも成り立つ規則」、範囲は「いつからいつまで聞くか」。半開区間の回の [from, to) がここで効く。

6. 動かす

毎朝「今日は点があるか」を聞いて、あれば通知する型は月末最終営業日の自動実行まで通すの型 1、Windows なら「今日は Windows Update の日です」の §6 のガイドがそのまま使える。スマホのカレンダーに入れるなら --ics で書き出す(.ics 取り込みの事件簿)。47 回ぶんの予定が、「この回だけ削除」なしで入る。

まとめ

  • 「ただし 8 月は休み」を、カレンダーアプリは「この回だけ削除」の繰り返しで表す——除く言葉が無いから
  • Kairos では列を引く: (tue \ (august | yearEnd)) |> at(T17:00)。2026 年は 52 回から 47 回
  • 規則で決まる除外(8 月)は filter、人が決める除外(年末年始)は表+covering:
  • 表の外の月は、時刻を付けずに「表の外」と言う。黙って通知が来るより、ずっといい
  • 引くのは時刻を付ける前。「いつから聞くか」は定義でなく --from で

▶ Playground で定義を実行する——2026 年の火曜 47 回が出る。コメントにある「祝日も休み」の行と入れ替えると 44 回になる。ブラウザ内で完結し、何も送信しない。


Kairos はスケジュール定義言語です。仕様と参照実装はGitHub で公開しています。

コメント

タイトルとURLをコピーしました