来年の干支は何か——年に 12 個のラベルを貼る

Kairos

1. 年賀状の季節に

11 月になると、年賀状の売り場に来年の干支が並ぶ。2027 年は未(ひつじ)。市販のカレンダーには十干を添えて「丁未(ひのと・ひつじ)」と書いてある。この名前は誰かが毎年決めているのではなく、12 個の名前を年に順に貼って12 年で一巡するという規則の結果である。十干は 10 個で 10 年で一巡する。

前回は月の中の日番号で旬を書いた。今回は年に貼るラベルの話で、4 勤 4 休の回で 8 個のラベルを日に貼って 8 日周期を書いたのと同じ仕組みが、貼る先を日から年に変えるだけで、そのまま干支になる。

2. 年に 12 個のラベルを貼る

premise Eto = Gregorian with {
  tz: "Asia/Tokyo"
  branch = year cycle [子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥] anchor: 2020-01-01
}
premise JP { calendar-system: Eto; tz: "Asia/Tokyo"; wkst: Mon }

@JP
everyDay |> within(year) |> first |> filter(d => branch(d) == 午)
  • year cycle [...] が、年に貼る 12 個の反復ラベル。4 勤 4 休では day cycle だった。貼る先が違うだけで、 リストが巡回の順を与え、先頭に「開始」の意味が無いのも同じ。
  • anchor: 2020-01-01 が位相。「anchor の属する年が先頭ラベル」なので、2020 年が子になる。2020 年が子年である ことは年賀状を一度でも買った人なら知っている事実で、照合の要らない anchor である(§5 で、そうでない anchor の話をする)。
  • branch(d) は「日 d の属する年のラベル」。weekday(d) と同じ読み方で、premise に書いた束縛の名前が そのまま「点 → ラベル」の関数になる(本社の暦と、支社の暦で書いた前文の派生)。
  • everyDay |> within(year) |> first が各年の元日。そこに「午の年」の述語を掛ける。
$ kairos list --from 1970-01-01 --to 2050-01-01 eto.kairos
1978-01-01
1990-01-01
2002-01-01
2014-01-01
2026-01-01
2038-01-01

12 年おきに午年の元日が出る。== 午 を == 未 に替えれば 2027-01-01 が出る。

図 1: 2020 年から 2031 年の 12 年を帯に並べ、上の段に十二支(2020 年の子から亥まで)、下の段に十干(2020 年の庚から辛まで)を書く。2026 年の列は丙と午、2027 年の列は丁と未で、金で強調してある。(2026-10-25 図を追加)
図 1: 2020 年から 2031 年の 12 年を帯に並べ、上の段に十二支(2020 年の子から亥まで)、下の段に十干(2020 年の庚から辛まで)を書く。2026 年の列は丙と午、2027 年の列は丁と未で、金で強調してある。(2026-10-25 図を追加)

3. 十干を足して「丙午」「丁未」

十干(甲・乙・丙・丁・戊・己・庚・辛・壬・癸)は 10 個のラベルで、10 年で一巡する。1984 年が甲(きのえ)——1924 年の甲子(きのえね)の年に完成した甲子園球場の、60 年後である。もう 1 本、同じ形で足す:

premise Eto = Gregorian with {
  tz: "Asia/Tokyo"
  branch = year cycle [子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥] anchor: 2020-01-01
  stem   = year cycle [甲, 乙, 丙, 丁, 戊, 己, 庚, 辛, 壬, 癸] anchor: 1984-01-01
}

「丙午(ひのえうま)の年」は、2 本のラベルを and で合わせる:

everyDay |> within(year) |> first |> filter(d => stem(d) == 丙 and branch(d) == 午)
$ kairos list --from 1970-01-01 --to 2050-01-01 eto.kairos
2026-01-01

80 年の範囲で 1 点だけ。10 と 12 の最小公倍数は 60 なので、十干と十二支の同じ組は 60 年に 1 度しか現れない。それが還暦で、丙午は 1966 年の次が 2026 年、甲子は 1984 年の次が 2044 年である(stem(d) == 甲 and branch(d) == 子で 1984-01-01 と 2044-01-01 の 2 点が出る)。

60 個の名前を 1 本のラベルに書いてもよい:

  kanshi = year cycle [甲子, 乙丑, 丙寅, 丁卯, 戊辰, 己巳, 庚午, 辛未, 壬申, 癸酉, 甲戌, 乙亥,
                       丙子, 丁丑, 戊寅, 己卯, 庚辰, 辛巳, 壬午, 癸未, 甲申, 乙酉, 丙戌, 丁亥,
                       戊子, 己丑, 庚寅, 辛卯, 壬辰, 癸巳, 甲午, 乙未, 丙申, 丁酉, 戊戌, 己亥,
                       庚子, 辛丑, 壬寅, 癸卯, 甲辰, 乙巳, 丙午, 丁未, 戊申, 己酉, 庚戌, 辛亥,
                       壬子, 癸丑, 甲寅, 乙卯, 丙辰, 丁巳, 戊午, 己未, 庚申, 辛酉, 壬戌, 癸亥] anchor: 1984-01-01

filter(d => kanshi(d) == 丙午) でも同じ 2026-01-01 が出る。2 本に分けると「午の年」「丙の年」を別々に聞けて、1 本にまとめると名前が 1 語で出る(§4)。どちらで書くかは、何を聞きたいかで決める。

4. 出口に名前が出る

二十四節気の回で、表に名前を付けると .ics の予定の題や TSV の label 列に名前が出ることを書いた。cycle のラベルも同じ出口に乗る。式が参照するラベル付きの束縛が 1 つのとき、各点にその名前が添えられる:

$ kairos list --tsv --from 1970-01-01 --to 2050-01-01 kanshi.kairos
expr	source	uid	date	time	epoch_ms	note	label
1	everyDay |> within(year) |> first |> filter(d => kanshi(d) == 丙午)	kairos-f78f966da443-series@kairos-lang.org	2026-01-01		1767193200000		丙午

stem と branch の 2 本を参照する式には label 列が付かない。どちらの名前を添えるかを言語は決めないので、1 語で出したいときは 60 個の 1 本で書く。

各年の元日に名前を添えて一覧にするなら、kanshi で区切る:

everyDay |> within(kanshi) |> first
$ kairos list --tsv --from 2026-01-01 --to 2030-01-01 kanshi.kairos | cut -f 4,8
date	label
2026-01-01	丙午
2027-01-01	丁未
2028-01-01	戊申
2029-01-01	己酉

kanshi は年に貼ったラベルなので、within(kanshi) は年で区切るのと同じで、first が各年の元日になる。式が kanshi を参照しているので、label 列にその年の名前が付く。

5. 落とし穴

  • 年の境は元日。within(year) はグレゴリオ暦の年で、1 月 1 日に干支が切り替わる。暦の流儀によっては立春で 年を切り替える読み方(節切り)があり、1 月生まれの人の干支が変わる。どちらで読むかは暦を編む側の判断で、 式はその判断を写す側にいる。節気で年を切る窓の作り方は、別の回にする。
  • 日の干支は anchor の照合が要る。土用の丑の日や酉の市は、日に貼った十二支で決まる。同じ day cycle [子, …, 亥] で書けるが、anchor には「この日は確かに子だった」と照合した実日を書く。年の干支は 2020 年が子と誰でも知っているので 照合が要らないが、日の干支はそうではない。この記事では日の干支を列挙しない。
  • 参照実装の評価範囲は 1970 年以降。1966 年の丙午は同じ規則で決まるが、参照実装は 1970 年より前を実体化しない。 還暦の相手を探すなら、60 を足すか引く。
  • cycle はデータを持たない。六曜の回では朔と月番号のデータが要った。干支は規則だけで 決まるので、被覆サマリも表の期限も出ない。表が腐る心配の無い、数少ない暦の名前である。

6. 置き換えではない

干支は、年賀状・市販のカレンダー・暦の出版が長く整えてきた名前である。式が写したのは「12 個と 10 個を順に貼る」という末端の規則だけで、どの年を子とするか、年の境をどこに置くかの判断は上流にある。上流の権威はこれからも上流にある。

7. 動かす

§4 の everyDay |> within(kanshi) |> first を、12 月のうちに来年の元日 1 日ぶんの範囲で聞く:

$ kairos list --tsv --from 2027-01-01 --to 2027-01-02 kanshi.kairos | cut -f 4,8
date	label
2027-01-01	丁未

元日にサイトの絵柄を切り替えるなら、月末最終営業日の自動実行まで通すの型 1 で毎朝走らせ、点がある日(元日)に label 列の名前で画像を選ぶ。丁未 と当てに行く式を、毎年書き換える必要はない。

まとめ

  • 干支は「12 個の名前を年に順に貼る」規則の結果。Kairos では year cycle [子, …, 亥] anchor: 2020-01-01 の 1 行
  • 貼る先が日から年に変わっただけで、4 勤 4 休と同じ仕組み。branch(d) は weekday(d) と同じ読み方
  • 十干はもう 1 本の year cycle。and で合わせると 60 年に 1 度の組になる。60 個の 1 本で書いてもよい
  • 名前は出口に出る。参照するラベルが 1 つなら TSV の label 列と .ics の題に名前が付く。within(kanshi) |> first で各年の元日に名前が添う
  • 年の干支の anchor は照合が要らない。日の干支は要る。年の境をどこに置くかは上流の判断

▶ Playground で定義を実行する——1970 年から 2049 年の午年の元日 6 点が出る。コメントにある未年・丙午・甲子の行と入れ替えて試せる。ブラウザ内で完結し、何も送信しない。


Kairos はスケジュール定義言語です——決めたら、いつでも。仕様と参照実装はGitHub で公開しています。

コメント

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