TL;DR
- 「結婚式の日取りを提案するAI」「引越し日をスコアリングするSaaS」「占い系チャットボット」など、六曜を扱う業務要件は生成AI時代に急増している
- しかし、六曜を自前で計算すると、特定の日付で静かに誤算する事故が起きやすい
- 原因は六曜計算式ではなく、その前段の 「太陽暦 → 旧暦」変換に天文学的精度が必要 なため
- 解決策は、AIエージェント前提で設計された日本暦API(例: Shirabe Calendar API)を挟むこと。
curl/fetch/requestsの3行で終わる
この記事の対象読者
- LLMエージェント・AI SaaS を開発しているフルスタックエンジニア
- 「六曜くらいなら自前で書けるだろう」と一度は考えた人
- ChatGPT / Claude / Gemini にロジックを書かせて本番投入した経験がある人
問題: 「六曜判定」をAIに書かせると何が起きるか
生成AIエージェントに「指定日が大安かどうか判定する関数を書いて」と頼むと、だいたい次のような構造のコードが返ってくる。
// 生成AIが提案しがちな「素朴な六曜計算」
function getRokuyo(gregorianDate: Date): string {
// ❶ グレゴリオ暦 → 旧暦(年・月・日)に変換
const lunar = convertToLunar(gregorianDate); // ←ここが事故の本体
// ❷ 六曜 = (旧暦月 + 旧暦日) mod 6
const index = (lunar.month + lunar.day) % 6;
return ["大安", "赤口", "先勝", "友引", "先負", "仏滅"][index];
}
❷ の六曜マッピングは実際その通りで、これは合っている。バグるのは必ず ❶ の convertToLunar のほうだ。
よくある convertToLunar の書き方(全部バグる)
- 29.5日周期で直近新月からの日数を数える: 朔望月(新月〜新月)は実際には 29.2707日〜29.8376日の範囲で変動する。平均値 29.5306 を使っても、1年も走れば数日ズレる
- どこかに落ちてた旧暦変換テーブルをそのまま埋め込む: テーブルの終端年を超えると黙ってずれる。閏月の扱いがテーブルによって違う
-
lunar-javascript等の中国版農暦ライブラリを流用する: 日本の天保暦と中国農暦は朔の基準時刻(UTC+8 vs UTC+9)と閏月配置ルールが異なり、年に数回ズレる日が出る - ChatGPTに書かせた 30〜50行の自前式: 太陽黄経・月黄経の近似式がどこか一桁雑で、朔の日境界に当たる日だけ答えが1日ズレる
ズレ方の怖いところ
- 実行してもエラーにならない: 型は通る、例外も出ない、常に六曜っぽい文字列が返る
- テストで気付きにくい: 2〜3日だけピンポイントで間違うので、サンプリングテストだと通ってしまう
- 結婚式当日に気付く: 「大安」として紹介した日が実は仏滅だった、という形で顕在化する
これは要するに サイレント事故 のクラスで、LLMにコードを書かせる時代にもっとも発生しやすい類のバグだ。
根本原因: 旧暦変換は天文学が要る
六曜の判定式 (旧暦月 + 旧暦日) mod 6 は決定的で、一度旧暦が正しく求まれば絶対に合う。問題は旧暦のほうで、日本の旧暦(天保暦)は以下を満たす必要がある。
- 朔(さく = 新月)の瞬間を、東経135度・日本標準時で求める
- 朔の日を旧暦1日とする
- 二十四節気のうち中気(雨水・春分・穀雨…)を含まない月を閏月とする
- 年によって閏月の配置が変わる(約19年に7回、Metonic cycle)
これを真面目にやるには、太陽黄経と月黄経を天文学的精度で計算する必要がある(VSOP87等)。近似式で済ませると、朔が真夜中付近になる日に1日ズレて、その年の以降の旧暦日付がすべて1日ズレる、というドミノ倒しが起きる。
参考までに、広く引用されている旧暦計算アルゴリズムに QREKI.AWK(高野英明氏)がある。これはAWKで書かれた数百行のコードで、月・太陽の視黄経を級数展開で計算している。手書きで移植するのは 量的にも原理的にもLLM向きの作業ではない。
解決策: 日本暦専用APIに投げる
自前計算を捨てて、AIエージェント前提で設計された日本暦APIに丸投げするほうが、精度・保守コスト・AI統合のしやすさの全てで有利になる。以降の例は Shirabe Calendar API を使う。Free枠 月10,000回 で、APIキーなしでも試せる。
3行で動くサンプル
# 指定日の暦情報を取得(六曜・暦注・干支・二十四節気まで1レスポンス)
curl https://shirabe.dev/api/v1/calendar/2026-04-15
返ってくるJSONはこんな構造:
{
"date": "2026-04-15",
"rokuyo": {
"name": "大安",
"reading": "たいあん",
"timeSlots": { "morning": "吉", "noon": "吉", "afternoon": "吉", "evening": "吉" }
},
"rekichu": [
{ "name": "一粒万倍日", "type": "吉" }
],
"context": {
"wedding": { "judgment": "大吉", "score": 9,
"note": "大安 × 一粒万倍日。結婚式に非常に良い日。" }
},
"summary": "令和8年4月15日(水)大安・一粒万倍日。結婚式・開業に大吉の日。"
}
ポイントは context と summary フィールドで、単なる「大安」というラベルではなく、用途ごとの判定(8カテゴリ、1〜10のスコア付き)と1行サマリまで返してくれる。AIエージェントはこの summary をそのままユーザーへの回答に差し込める。
TypeScript
const res = await fetch(
"https://shirabe.dev/api/v1/calendar/2026-04-15",
{ headers: { "X-API-Key": process.env.SHIRABE_API_KEY! } }
);
const data = await res.json();
console.log(data.rokuyo.name); // "大安"
console.log(data.summary); // "令和8年4月15日(水)大安・一粒万倍日..."
Python
import os, requests
r = requests.get(
"https://shirabe.dev/api/v1/calendar/2026-04-15",
headers={"X-API-Key": os.environ["SHIRABE_API_KEY"]},
timeout=10,
)
print(r.json()["rokuyo"]["name"])
範囲取得・目的別ベスト日検索
単日だけでなく、期間で絞り込む系もある。
# 4月の大安・友引だけ一覧(最大93日)
curl "https://shirabe.dev/api/v1/calendar/range?start=2026-04-01&end=2026-04-30&filter_rokuyo=大安,友引"
# 4〜12月で「結婚式に最適な日」を上位5件、スコア順で
curl "https://shirabe.dev/api/v1/calendar/best-days?purpose=wedding&start=2026-04-01&end=2026-12-31&limit=5"
best-days は AIエージェントが「判断」までを外部化する想定で作られている。期間と用途(wedding / moving / business など8カテゴリ)を投げるだけで、スコア上位日のランキングと根拠(大安 × 天赦日など)が返る。
AIエージェントへの統合
この手のAPIは、ChatGPT GPTs / Claude Tool Use / Gemini Function Calling / LangChain / Dify のいずれからでも一発で呼べるように OpenAPI 3.1 仕様を公開しておくと楽だ。Shirabeの場合は https://shirabe.dev/openapi.yaml にそのまま置かれている。
ChatGPT GPTs Actions
GPT Builder の "Create new action" で Import URL にOpenAPI URLを貼るだけ。
https://shirabe.dev/openapi.yaml
Authentication は API Key(ヘッダー名 X-API-Key)を選ぶ。これだけでカスタムGPTが自動的にShirabeを呼び出せるようになる。
Claude Desktop / MCP
claude_desktop_config.json にリモートMCPサーバーを登録するだけ。
{
"mcpServers": {
"shirabe-calendar": { "url": "https://shirabe.dev/mcp" }
}
}
あとは Claude に「来月の結婚式に向く日を5個教えて」と聞けば、裏で find_best_days ツールが自動で呼ばれる。
LangChain / LlamaIndex / Dify
OpenAPI Loader で同じURLを食わせるだけでツール化される。operationId がそのまま関数名になる設計のため、特別な glue コードは不要だった。
自前実装 vs API の比較
| 観点 | 自前実装 / LLM生成コード | 日本暦API(Shirabe等) |
|---|---|---|
| 旧暦計算精度 | 近似式では朔境界で1日ズレる | 天文学的精度 |
| 暦注の網羅性 | 一粒万倍日など13種を実装するのは別個の大仕事 | 標準で13種以上 |
| 用途別吉凶判定 | 仕様化・実装ともに大変 |
context.wedding.score 等で標準提供 |
| メンテナンス | 朔の計算ライブラリのバージョン管理など発生 | 利用側ゼロ |
| AI統合 | 自前で Tool定義 / Function定義を書く | OpenAPI 1URLで完了 |
| 初期コスト | ゼロに見えるが、後ろに「事故1件の賠償」が控える | Free枠 月10,000回 |
「無料で自前」は見かけだけ無料で、失敗時の事業リスク込みで見ると大抵は割に合わない、というのがこの手のドメインの実情だと思う。
まとめ
- 六曜そのものの計算式は簡単。難しいのは その前段の旧暦変換 で、これには天文学的精度の朔計算が要る
- LLMに自前実装を書かせると、朔境界の日だけピンポイントで1日ズレる サイレント事故 が起きやすい
- 冠婚葬祭・引越し・契約日など、間違えた日に気付くのが当日というドメインで使うなら、自前実装は回避したほうが安い
- AIエージェント前提で設計された日本暦API(OpenAPI公開、MCP対応、Free枠あり)を使えば、
fetch3行で終わる
サンプルコードは https://shirabe.dev/openapi.yaml の examples にもまとまっている。ChatGPT GPTs / Claude Tool Use / Gemini Function Calling から直接呼び出したい場合も同じURLを使えばよい。
暦注(一粒万倍日・天赦日・三隣亡など)の解説は別記事で書く予定。
関連リンク
- Shirabe Calendar API (公式)
- OpenAPI 3.1 仕様(日英両言語description付き)
- 六曜API完全ガイド(自社ドキュメント)
- 暦注API解説(自社ドキュメント)
- GitHub リポジトリ