cd /news/artificial-intelligence/stop-hand-coding-the-japanese-rokuyo… · home topics artificial-intelligence article
[ARTICLE · art-23055] src=dev.to pub= topic=artificial-intelligence verified=true sentiment=↓ negative

Stop hand-coding the Japanese Rokuyo calendar: LLM-generated lunar logic silently breaks

A developer has identified a critical flaw in LLM-generated code for the Japanese Rokuyo calendar: while the lunar date calculation formula is correct, the underlying lunar date conversion is frequently wrong, causing silent date shifts that cascade through an entire year. The developer recommends abandoning hand-coded or LLM-generated lunar conversion functions in favor of the Shirabe Calendar API, which provides accurate Rokuyo, rekichu, and per-purpose date judgments with a free tier of 10,000 calls per month.

read5 min publishedJun 6, 2026

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.

── more in #artificial-intelligence 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/stop-hand-coding-the…] indexed:0 read:5min 2026-06-06 ·