05 — 天文由来の暦法の横断検証(旧暦以外)
発端はレビューの問い(2026-07-08):「天文現象から導き出される暦法は旧暦に限らない。それらを Kairos で 表現するために足りないものは何か。仕様のどこかが決定的に食い違って表現できないのか」。
仮説: 旧暦(../../stdlib/kyureki.md)で確立した形——天文瞬間列(権威データ)→ snapTo(day) →
segmentBy → 射影で読む——が他の天文暦法にも延長できるなら、決定的な食い違いは無く、欠落は
データ供給の運用面(既知の宿題)に集約されるはず。最も際どい一件(ヒンドゥー暦のティティ=代表点が
真夜中でない規約)を実行検証し、残りは机上で構造を同定する。
本ページの ```kairos ブロックは doctest(impl/test/doctest.test.ts)で実行検証される。
5.1 ヒンドゥー暦のティティ(実行検証・合成データ)
(1) 仕様+期待値: ティティ(tithi)は月と太陽の離角 12° ごとの「太陰日」。長さは約 19〜26 時間で 不規則(天文現象由来の瞬間列)。パンチャーンガの規約は「各市民日の日の出時点に進行中のティティが、 その日のティティ」。日の出を一度も含まないティティは欠日(kshaya——その番号の日が暦から飛ぶ)。 合成データ(日の出 06:51 固定・ティティ境界 7 点)で、ティティ 3 の日=1/3、ティティ 6=欠日を期待値とする。
(2) premise 定義+本体式:
# eval: 2026-01-01..2026-01-07
premise Astro = Gregorian with {
tz: "Asia/Tokyo"
source: "synthetic-ephemeris" # 実運用は暦・天文台の告示(地点依存=出所に地点を含める)
sunrises = [2026-01-01T06:51, 2026-01-02T06:51, 2026-01-03T06:51,
2026-01-04T06:51, 2026-01-05T06:51, 2026-01-06T06:51]
tithiB = [2026-01-01T03:00, 2026-01-02T01:00, 2026-01-02T22:30, 2026-01-03T19:00,
2026-01-04T14:30, 2026-01-05T09:00, 2026-01-06T02:30]
tithiW = sunrises |> segmentBy(tithiB, edges: drop, empties: keep,
labels: [1, 2, 3, 4, 5, 6, 7])
}
premise Panchanga { calendar-system: Astro; tz: "Asia/Tokyo"; wkst: Mon }
@Panchanga
# 「ティティ 3 の日」=その市民日の日の出がティティ窓 3 に入る日
sunrises |> within(day) |> first |> filter(s => tithiW(s) == 3) |> snapTo(day)
#=> 2026-01-03
#~> 範囲外 2026-01-01..2026-01-02(Astro.sunrises covering 2026-01-01T06:51..2026-01-06T06:51)
#~> 範囲外 2026-01-06..2026-01-07(Astro.sunrises covering 2026-01-01T06:51..2026-01-06T06:51)
#~> 範囲外 2026-01-06..2026-01-07(Astro.tithiB covering 2026-01-01T03:00..2026-01-06T02:30)
欠日(kshaya)——ティティ 6 の窓(01-05T09:00〜01-06T02:30)はどの日の出も含まないので、 「ティティ 6 の日」は空になる(期待値なし=空列。空は正当な値・ADR-15):
# eval: 2026-01-01..2026-01-07
premise Astro = Gregorian with {
tz: "Asia/Tokyo"
source: "synthetic-ephemeris"
sunrises = [2026-01-01T06:51, 2026-01-02T06:51, 2026-01-03T06:51,
2026-01-04T06:51, 2026-01-05T06:51, 2026-01-06T06:51]
tithiB = [2026-01-01T03:00, 2026-01-02T01:00, 2026-01-02T22:30, 2026-01-03T19:00,
2026-01-04T14:30, 2026-01-05T09:00, 2026-01-06T02:30]
tithiW = sunrises |> segmentBy(tithiB, edges: drop, empties: keep,
labels: [1, 2, 3, 4, 5, 6, 7])
}
premise Panchanga { calendar-system: Astro; tz: "Asia/Tokyo"; wkst: Mon }
@Panchanga
sunrises |> within(day) |> first |> filter(s => tithiW(s) == 6) |> snapTo(day)
#~> 範囲外 2026-01-01..2026-01-02(Astro.sunrises covering 2026-01-01T06:51..2026-01-06T06:51)
#~> 範囲外 2026-01-06..2026-01-07(Astro.sunrises covering 2026-01-01T06:51..2026-01-06T06:51)
#~> 範囲外 2026-01-06..2026-01-07(Astro.tithiB covering 2026-01-01T03:00..2026-01-06T02:30)
(3) core との差: 糖衣なし(すべて core と確定射影のみ)。
(4) 判定: 書ける(要外部データ=日の出・ティティ境界の瞬間列)。
(5) 綻びログ: 新しい綻びは出なかった。鍵は次の等価変形——「点→その日の日の出」という
点→点のデータ関数(sunriseOf(d)。F28 で保留した持ち上げの一種)が要るように見えるが、
「窓ごとの代表点は選択子で取れる」(sunrises |> within(day) |> first)ので不要。ISO 週番号の
F57(label: 不要の等価変形)と同じパターンで、F28 の保留(射影を先に・持ち上げは後回し)の
主要動機がまた一つ消えた。設計上の観察:
empties: keepが本質——欠ティティの窓を落とすと番号が黙って詰まる(labels:ではempties: dropはそもそも静的エラー=ADR-39。ティティ番号はlabels: [1..7]の並行リストが担い、リスト長=窓数の 同長検査が効く)。かつてのedges: clip+epochOrdinal形は「頭の擬似窓が番号 0 を占める偶然」に 番号付けが依存していた——edges: drop+labels:への移行(期待値不変)はこの偶然依存の除去を兼ねる。- 等価変形の前提は「各市民日にちょうど一つの日の出」。白夜・極夜圏では規約自体が別物になる—— データ(地点)側の問題であって言語の穴ではない。
- 位置依存: 日の出は地点依存。現状は地点ごとのデータ premise(
source:に地点を含める)で 表せる。location:メンバーの第一級化は将来の糖衣(90-open-questions に追記・追加拡張)。
5.2 机上検証: 他の天文暦法の還元(構造の同定)
| 暦法 | 天文の核 | Kairos での形 | 判定 |
|---|---|---|---|
| イスラム暦(ウンム・アルクラー等の計算暦) | 月初判定(メッカでの月没・朔条件) | 月初日列=朔と同型 → segmentBy で月を切る(旧暦パターンそのまま) |
書ける(要外部データ) |
| イスラム暦(観測ベース) | ヒラール(新月の初視認)の観測で月初が確定 | 同上+確定が遅延到着する版付きデータ——asof: の版管理と、未確定域の範囲外出自(F61)が受け皿 |
書ける(F61 の活性化で運用が立つ) |
| バハーイー暦 | テヘランの春分でナウルーズ(年初)が決まる | segmentBy(equinoxes \|> snapTo(day)) で年を切る——春分データ(95-reference-data と同種)と同型 |
書ける(要外部データ) |
| ヘブライ暦・イスラム暦の「日没起点の日」 | 日は日没に始まる | 日没瞬間列で segmentBy——§5.1 と同型の独自窓。tz 市民日(ADR-33)とは別の窓として立ち、衝突しない |
書ける(要外部データ) |
| 潮汐・天文薄明のスケジュール | 満潮・薄明の瞬間そのもの | 瞬間列テーブルがそのまま発報列(射影・窓も不要) | 書ける(要外部データ) |
| 火星暦(sol) | 別スケールの均等日 | strideBy(24h39m35.244s, from:)・epoch:——ADR-19/31/33 で確定済み |
書ける |
5.3 まとめ——足りないものは何か
決定的な食い違いは無い。 天文暦法の共通構造(不規則な瞬間列が窓の切れ目を決める)は、 データ駆動の窓(ADR-26「暦法純粋 I8 が禁じるのはカレンダー依存であってデータ依存ではない」・ F13「規則とデータの境界は premise の中を走る」)が最初から狙っていた形であり、旧暦が要 NAOJ 暦要項で あるのは欠陥ではなく設計上の境界(権威の告示が正・真正性判定のスコープ外と同じ線引き)。 言語内で天文計算そのもの(黄経・朔・日の出の算出)を行う層は意図的に持たない——連続量の 閾値イベントは事前計算済みの瞬間列として持ち込む。
欠落はすべて既知の宿題に還元される(新 F 番号なし):
- F61(範囲外出自・
covering:の活性化)——天文データは必ず尽きる(暦要項は年次告示)。 データ地平線の外の統治が、天文暦法の運用の最大の欠落(bizDay 標準導出の安全性=ADR-35 とも直結)。 - socket(外部供給宣言)——年次更新の差し込み口。器は整列の主張を含む(ADR-36 帰結)。
追記(2026-07-14):
externalとして確定(ADR-46——整列の主張はkind:が担う)。 - F62(並行値リスト同長性)——
monthNosイディオムは天文暦法の常用形。 - location の第一級化——現状
source:で表せるため糖衣候補(90-open-questions・追加拡張)。 - F24/F37(隣接窓参照)——再帰的命名はデータ側へ倒す確定判断のまま(権威が解決済みの名を告示する)。
ADR-36(整列)との接続: 天文瞬間列は整列「なし」なので、日次列との結合には snapTo の明示が
強制される——F22 で旧暦の朔日規約に使った作法が、言語全体の統治に昇格した。