日本語 · English(未訳)

10 — 代替暦の算術形——Outlook / OS が焼き込んでいるものを、開いた定義で書く

発端(2026-08-11・設計者照会): RRULE が旧暦を算出できないことは検証済み(03・spec §1.2) だが、Outlook は旧暦・ヒジュラ暦・ヘブライ暦の繰り返し予定を作れる——どうやっているのか。 そして「定期的なスケジュールの設定で、Outlook にできて Kairos にできないこと」は無いか。 本ページは (a) Outlook/OS の実装様式の調査、(b) タブラー・ヒジュラ暦をデータゼロの規則 premise として書く実行検証、(c) 方式の構造対比と「できないこと」の正面回答、を一枚に固定する。

10.1 調査——Outlook はどうやっているか

一次資料(Microsoft Learn・2026-08-11 参照)から、様式は三点に集約される。

(1) 暦変換は OS/ランタイムへの焼き込み。Windows NLS(CALID: CAL_HIJRI・CAL_HEBREW 等)と .NET System.Globalization のカレンダークラス群が変換を担う。実装様式は暦ごとに違う:

.NET の実装様式 有効範囲(Min/MaxSupportedDateTime)
中国旧暦(ChineseLunisolarCalendar) 事前計算テーブル(朔・置閏の決め打ち) 1901-02-19 〜 2101 年頃(テーブルの端がそのまま範囲)
ヒジュラ暦(HijriCalendar) 純算術(タブラー暦)+ HijriAdjustment(±日数の観測ずれ調整ノブ) 622-07-18 〜 9999-12-31(算術なので事実上無限)
ヘブライ暦(HebrewCalendar) 算術(メトン 19 年 7 閏・molad 計算) 1583 〜 2239(実装制限)

範囲の狭さがそのまま様式の証拠になっている——中国旧暦だけ 200 年しかないのは、朔と置閏を 計算せずテーブルで持っているからである(Kyureki が NAOJ の朔 38 点で 3 年を張るのと構造は 同じ。違いは後述 §10.3)。

(2) 繰り返し「規則」は内部形式にはあるが、境界を越えられない。Outlook 内部(MAPI・ [MS-OXOCAL] の RecurrencePattern 構造)には CalendarType フィールドがあり「ヒジュラ暦の 毎年 X 月 Y 日」を規則として保持できる。しかし:

つまり Outlook の中では規則、境界を越えると展開済みの点列(あるいは不可視)になる。 「定義がシステムの境界を越えて規則のまま運べない」——第 4 弾ブログ以来の「コードに埋まった if 文」問題の、カレンダー製品版である。

(3) 有効範囲は例外で守るMinSupportedDateTime の外は ArgumentOutOfRangeException—— 範囲は API 脚注には在るが、答えに併走しない。テーブルがいつ時点の何に基づくか(出所・ 鮮度)を運ぶ器も無い。

10.2 実行検証——タブラー・ヒジュラ暦をデータゼロで書く

.NET HijriCalendar(adjustment 0)と同じタブラー暦(型 II・civil epoch)は、規則が 3 行で 言い切れる: ① 30 年周期に閏 11 回(周期内 2,5,7,10,13,16,18,21,24,26,29 年目= (11y + 14) mod 30 < 11)、② 月長は 30/29 の交互(奇数月 30)、③ 閏年だけ 12 月が 30 日。 30 年周期 = 10,631 日。紀元は AH 1-1-1 = ユリウス 622-07-16(金)、1970-01-01 = AH 1389-10-22(周知の換算・リファレンス計算で照合済み)。

premise 層の窓生成語 span(§1.11=月長を値式で返すボトムアップ集約)がそのまま器になる。 1970-01-01 直後の月初は AH 1389-11-01 = 1970-01-09(phase: 8)、生成側の窓序数 n に対する 通算月は M = 16666 + n(AH 1-1 月 = 0):

# eval: 2025-01-01..2027-01-01
premise TabularHijri = Gregorian with {
  hMonth = day span (n =>
      ((16666 + n) mod 12 + 1) mod 2 == 1 ? 30
    : (((16666 + n) mod 12 + 1 == 12 and (11 * ((16666 + n) div 12 + 1) + 14) mod 30 < 11) ? 30 : 29)
  ) phase: 8

  hijriMonth = d => (16665 + epochOrdinal(hMonth, d)) mod 12 + 1
  hijriYear  = d => (16665 + epochOrdinal(hMonth, d)) div 12 + 1
  hijriDay   = d => ordinalIn(day, hMonth, d)
}
premise JPH { calendar-system: TabularHijri; tz: "Asia/Tokyo"; wkst: Mon }

@JPH
hMonth |> first |> filter(d => hijriMonth(d) == 1)
#=> 2025-06-27 2026-06-17

AH 1447 年・1448 年の元日——リファレンス計算(30 年周期の積み上げ)と一致。ラマダーン (9 月)の初日:

# eval: 2026-01-01..2027-01-01
premise TabularHijri = Gregorian with {
  hMonth = day span (n =>
      ((16666 + n) mod 12 + 1) mod 2 == 1 ? 30
    : (((16666 + n) mod 12 + 1 == 12 and (11 * ((16666 + n) div 12 + 1) + 14) mod 30 < 11) ? 30 : 29)
  ) phase: 8

  hijriMonth = d => (16665 + epochOrdinal(hMonth, d)) mod 12 + 1
}
premise JPH { calendar-system: TabularHijri; tz: "Asia/Tokyo"; wkst: Mon }

@JPH
hMonth |> first |> filter(d => hijriMonth(d) == 9)
#=> 2026-02-18

そして Outlook の繰り返しパターンでは書けない合成——「ラマダーン月の金曜日」が filter 一段で立つ(暦の premise と曜日の値射影の直交):

# eval: 2026-01-01..2027-01-01
premise TabularHijri = Gregorian with {
  hMonth = day span (n =>
      ((16666 + n) mod 12 + 1) mod 2 == 1 ? 30
    : (((16666 + n) mod 12 + 1 == 12 and (11 * ((16666 + n) div 12 + 1) + 14) mod 30 < 11) ? 30 : 29)
  ) phase: 8

  hijriMonth = d => (16665 + epochOrdinal(hMonth, d)) mod 12 + 1
}
premise JPH { calendar-system: TabularHijri; tz: "Asia/Tokyo"; wkst: Mon }

@JPH
everyDay |> filter(d => hijriMonth(d) == 9 and weekday(d) == Fri)
#=> 2026-02-20 2026-02-27 2026-03-06 2026-03-13

照合の注: タブラー暦は算術の理想化で、ウム・アルクラ(サウジ公式・観測委員会の決定)とは ±1〜2 日ずれるのが暦の性質として正常(AH 1447 元日はタブラー 6/27・ウム・アルクラ 6/26)。 観測暦が要件なら、それはデータの世界——§10.3 の Kyureki 型で書く。この使い分け自体が 「朔はデータ・六曜は法則」(ブログ第 10 弾)の同型である。

紀元断面の注: 評価下限 1970-01-01 は AH 1389-10 月の途中なので、phase: 8 の頭に 8 日の切れ端窓(10 月の断片)が立つ。この断片窓では hijriDay(窓内序数)が月途中からの 数えになれない(切れ端内 1 起点)——月番号・年番号の読みは正しい。実務の評価範囲(現代)では 影響しない。あわせて生成側 f の序数と読み側 epochOrdinal の序数が切れ端 1 個ぶんずれる (式中の 16666 と 16665 の差がそれ)——span.md の「同じ座標」の記述との食い違いを F108 として記録(正本 90-findings・処置=文書の精密化)。

10.3 方式の構造対比——同じ構造・違うのは可視性

観点 Outlook / .NET Kairos
中国旧暦の朔・置閏 事前計算テーブルをランタイムに焼き込み(出所・鮮度は不可視) テーブルは定義の一部(Kyureki: NAOJ 朔 38 点・source:/asof:/covering: が明示・stdlib/kyureki.md)
算術ヒジュラ暦 HijriCalendar(コンパイル済み実装) 規則 premise 20 行(§10.2・読める・diff が取れる)
観測とのずれ調整 HijriAdjustment(レジストリ/プロパティの±日数・どの版か記録されない with 派生 or 供給差し替え——どの premise・どの asof か が定義に残る
有効範囲 MinSupportedDateTime(範囲外=例外・答えに併走しない) covering(範囲外=註釈が答えに併走・残走路が運用信号)
繰り返し規則の可搬性 内部形式のみ(RRULE・Graph へは展開点列に退化 定義はテキスト——境界を越えても定義のまま(CLI・–supply・CliReport)
暦×他条件の合成 繰り返しパターンの語彙内のみ(「ラマダーンの金曜」は不可) 全式が合成可能(§10.2 の filter 一段)

.NET の中国旧暦テーブルと Kairos の Kyureki は構造が同型である——違いはテーブルが 閉じたバイナリの中にあるか、開いた定義の中(出所・鮮度・覆域つき)にあるか、だけ。 これは実装の優劣ではなく、製品(表示のための変換)と言語(検証可能な定義)の役割の違いが 様式に出たものと読める。

10.4 ヘブライ暦——書ける・急がない(三段整理)

月相(03 §3.4)と同じ三段で据える。(1) 規則は値式の射程内——メトン 19 年 7 閏 ((7y + 1) mod 19 < 7 型)と molad 計算(1 時間 = 1080 chalakim の分数演算=div/mod の 整数算術で書ける)・ローシュ・ハッシャーナーの遅延 4 規則(曜日と時刻閾値の条件式)—— 新語彙は不要で、§10.2 と同じ span 形に載る。(2) ただし書き切りは長大(月長 6 変種 〈353〜355・383〜385 日〉が遅延規則に依存)で、需要が立ってからが正しい順序(F77 裁定の 分布論と同じ——教材・検証目的なら §10.2 のヒジュラで型は示せた)。(3) データ方式なら 今すぐ Kyureki 同型で書ける(イスラエルの公式暦データを labels: 付きテーブルで)。

10.5 判定——「Outlook にできて Kairos にできないこと」

結論: 「Outlook にできて Kairos にできないこと」は、定期的なスケジュールの定義に関する 限り無い。あるのは役割の線引き(表示=実装系・品揃え=供給)で、いずれも受け皿が明文化 済み。spec §1.2 の比較表に本ページへの導線を追加した。