Originally published in Japanese on
[Qiita]. This is the English edition.
curl
/ fetch
/ requests
.Rokuyo (六曜) is a six-day cycle (Taian / Tomobiki / Sensho / Senbu / Butsumetsu / Shakko) still used across Japan to decide auspicious dates — weddings, funerals, ground-breakings, store openings. If you build anything for the Japanese market that recommends or scores dates, you will eventually need it. It looks trivial. It is not.
Ask a coding assistant for "a function that tells me whether a given date is Taian" and you'll usually get something shaped like this:
// The "naive Rokuyo" an LLM tends to propose
function getRokuyo(gregorianDate: Date): string {
// (1) Convert Gregorian -> lunar (year / month / day)
const lunar = convertToLunar(gregorianDate); // <- this is where accidents live
// (2) Rokuyo = (lunar month + lunar day) mod 6
const index = (lunar.month + lunar.day) % 6;
return ["Taian", "Shakko", "Sensho", "Tomobiki", "Senbu", "Butsumetsu"][index];
}
Step (2) is genuinely correct — that mapping is right. The bug is always in (1), convertToLunar
.
convertToLunar
is written (all wrong)
lunar-javascript
.This is the silent-failure class — the most common kind of bug in the era of letting an LLM write the code.
The Rokuyo rule (lunar month + lunar day) mod 6
is deterministic and always correct once the lunar date is right. The hard part is the lunar date. Japan's old calendar (Tenpō) requires:
Doing this properly means computing solar and lunar ecliptic longitude at astronomical precision (VSOP87 and friends). Use an approximation and, on days where the new moon falls near midnight, you shift by one day — and every subsequent lunar date that year dominoes by one day.
A widely cited reference implementation is QREKI (by Hideaki Takano): hundreds of lines of AWK computing apparent ecliptic longitudes via series expansion. Hand-porting that is, both in volume and in principle, not the kind of task you should hand to an LLM.
Drop the self-implementation and hand it to an API designed for AI agents — you win on accuracy, maintenance cost, and ease of AI integration. The examples below use Shirabe Calendar API. The free tier is 10,000 calls/month and you can try it without an API key. The fetch
/ requests
examples below still pass X-API-Key
— it's optional, used to track usage and raise limits.
curl https://shirabe.dev/api/v1/calendar/2026-04-15
The JSON looks like:
{
"date": "2026-04-15",
"rokuyo": {
"name": "大安",
"reading": "Taian",
"timeSlots": { "morning": "good", "noon": "good", "afternoon": "good", "evening": "good" }
},
"rekichu": [
{ "name": "一粒万倍日", "type": "auspicious" }
],
"context": {
"wedding": { "judgment": "excellent", "score": 9,
"note": "Taian x Ichiryu-manbai-bi. An excellent day for a wedding." }
},
"summary": "Reiwa 8 (2026)-04-15 (Wed) Taian / Ichiryu-manbai-bi. Excellent for weddings and openings."
}
The key fields are ** context and summary**. Instead of a bare "Taian" label, you get a per-purpose judgment (8 categories, each scored 1–10) and a one-line summary. An AI agent can drop that
summary
straight into its answer to the user.
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); // "大安" (Taian)
console.log(data.summary); // "Reiwa 8 (2026)-04-15 (Wed) Taian ..."
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"])
You're not limited to a single date.
curl "https://shirabe.dev/api/v1/calendar/range?start=2026-04-01&end=2026-04-30&filter_rokuyo=大安,友引"
curl "https://shirabe.dev/api/v1/calendar/best-days?purpose=wedding&start=2026-04-01&end=2026-12-31&limit=5"
best-days
is built so an AI agent can externalize the judgment itself. Pass a date range and a purpose (
wedding
/ moving
/ business
and 8 categories total) and you get a ranked list of top-scoring days with the reasoning (e.g. Taian × Tensha-bi).Publish an OpenAPI 3.1 spec and this kind of API can be called in one shot from ChatGPT GPTs Actions, Claude tool use, Gemini function calling, LangChain, or LlamaIndex. Shirabe serves it directly at https://shirabe.dev/openapi.yaml.
In the GPT Builder, "Create new action" → paste the OpenAPI URL into Import URL:
https://shirabe.dev/openapi.yaml
Choose API Key auth (header name X-API-Key
). That's all it takes for a custom GPT to call Shirabe automatically.
Feed the same URL to an OpenAPI and it's turned into tools. operationId
becomes the function name by design, so no glue code is needed.
| Dimension | Self-implementation / LLM-generated code | Japanese calendar API (Shirabe etc.) |
|---|---|---|
| Lunar accuracy | Approximations slip by a day at new-moon boundaries | Astronomical precision |
| Rekichu coverage | Implementing the ~13 auspicious-day types is a separate big job | 13+ types out of the box |
| Per-purpose judgment | Hard to spec and to build | Provided via context.wedding.score etc. |
| Maintenance | Versioning the saku-calculation library, etc. | Zero on the caller side |
| AI integration | Write your own tool/function definitions | One OpenAPI URL |
| Initial cost | Looks like zero — but a single wrong-date liability sits behind it | Free tier, 10,000/month |
"Free because I built it myself" is only superficially free. Priced with the business risk of a failure, it usually doesn't pay off in this domain.
fetch
.Sample code is also collected under the examples
in https://shirabe.dev/openapi.yaml. The same URL works whether you're calling from ChatGPT GPTs Actions, Claude tool use, or Gemini function calling.