If you have ever built calendar algorithms or localized software for East Asian markets, you know that calculating
traditional Chinese lunisolar calendars is notoriously tricky.
Most open-source tutorials reduce Chinese metaphysics (such as BaZi / Four Pillars of Destiny and Ziwei Dou Shu)
to simple lookup tables. In reality, modern astrology engines require astronomical precision: calculating instantaneous
solar longitude down to the arcminute, handling historical leap months, and resolving centuries-old sectarian debates on
midnight rollovers.
In this article, I want to walk through how we engineered a 100% client-side, zero-telemetry calendar engine for
FunTestHub, how we resolved the three most painful astronomical edge cases, and why we
open-sourced a verification suite of 266 benchmark test cases.
1. The Core Architectural Philosophy: 100% Client-Side Privacy
Chinese metaphysics calculations require sensitive personal inputs: exact birth date, exact birth minute, and gender.
Most modern platforms send this data to a backend server or a generative AI API to compute the chart and stream an
interpretation. We took the opposite architectural path:
[User Input: Date/Time/Gender]
│
▼
[Browser Local Execution] ──► Astronomical Solar Terms (VSOP87 / Ecliptic Longitude)
│ ──► Four Pillars Mathematical Derivations (Wu Shu Dun)
│ ──► Ziwei Dou Shu 12-Palace Matrix Mapping
▼
[Deterministic Results Rendered in DOM] ──► (Zero persistence, zero cloud transmission)
By packaging the ephemeris calculations inside lightweight JavaScript (running with zero server roundtrips), user birth
data never leaves the client's device.
However, running pure front-end calculations brings zero tolerance for algorithmic drift. Here are the three hardest
computational traps we had to solve.
2. Challenge 1: The Solar-Term (Jie Qi) Drift
In Western astrology, zodiac signs change on roughly fixed Gregorian calendar dates (e.g., Aries starts on March 21).
In Chinese Four Pillars of Destiny (BaZi), the Year and Month Pillars do NOT change on January 1st, nor on the Lunar New
Year. They change at the exact astronomical second of Li Chun (Beginning of Spring), when the apparent ecliptic
longitude of the Sun reaches exactly $315^\circ$.
Gregorian: Feb 03 -------------------- Feb 04 -------------------- Feb 05
Solar Year: [Astronomical Li Chun: 315°]
│
Previous Year │ New Year Begins
(Bing Wu / Fire) │ (Ding Wei / Fire Goat)
If an engine hardcodes Feb 4 as the year changeover, babies born just minutes before the actual solar transit will be
assigned the wrong Year Pillar, throwing off their entire life chart.
Solution: Continuous Solar Longitude Tracking
Instead of static date tables, our engine dynamically queries solar term timestamps computed from planetary ephemeris
tables (VSOP87 series):
import { Solar, Lunar } from "lunar-javascript";
export function getAstronomicalYearPillar(solarDate: Date): string {
const solar = Solar.fromDate(solarDate);
const lunar = solar.getLunar();
// Year Pillar changes strictly at Li Chun (Spring Beginning), not Jan 1 or Lunar New Year
return `${lunar.getYearInGanZhi()} (${lunar.getYearShengXiao()})`;
}
plaintext
────────────────────────────────────────────────────────────────────────────────
- Challenge 2: The "Late-Zi Hour" Rollover Debate (Sect 1 vs. Sect 2)
A traditional Chinese day is divided into 12 double-hours (Shi Chen), starting with the Zi Hour (子时): from 23:00 to
01:00.
This introduces a mathematical paradox:
- The hour starts at 23:00, but
- The calendar date changes at 00:00 (midnight).
For someone born at 23:30 on October 10:
- Sect 1 (Early/Late Zi distinction): The Day Pillar rolls over to October 11 at 23:00.
- Sect 2 (Midnight cutoff): The Day Pillar remains October 10 until 00:00 midnight, but the Hour Pillar calculates using the Zi stem formulas.
Time: 22:59 (Hai Hour) ──► 23:00 (Late Zi) ──► 00:00 (Early Zi)
Sect 1 Day: Day N │ Day N+1 │ Day N+1
Sect 2 Day: Day N │ Day N │ Day N+1
typescript
In our production engine, we strictly adhere to Sect 2 (midnight rollover), which aligns with modern civil timekeeping and
classical Qing Dynasty astronomical treaties (San Ming Tong Hui), using the standard Wu Shu Dun (Five Rats Formula) to
compute hour stems deterministically.
────────────────────────────────────────────────────────────────────────────────
- Challenge 3: Explainable Results vs. AI Hallucinations
With the rise of generative AI, the web has been flooded with "AI Fortune Telling" tools that pass birth dates into prompt
templates like:
▏ "Analyze this birth chart: 1990-05-15 10:00..."
LLMs are probabilistic token predictors; they hallucinate stems, invert element relationships, and invent conflicting
rules.
In our newly released 2027 Annual Fortune Report (https://funtesthub.com/en/2027-horoscope/), we enforced a strict
separation of concerns:
- The Math & Chart Generation: 100% deterministic TypeScript engine.
- "Why This Result?" Explanations: A 5-step transparent deduction chain displaying exact classical rule origins:
// Deterministic reasoning step template (No LLM black box)
export interface BaziReasoningStep {
step: number;
title: string;
deduction: string;
}
// Example deterministic output:
// Step 1: Day Master [Jia Wood]
// Step 2: 2027 Year Pillar [Ding Wei]
// Step 3: Five Elements Dynamic [Ding Fire is Hurting Officer to Jia Wood]
// Step 4: Classical Canon Applied [San Ming Tong Hui, Chapter 14]
// Step 5: Action Directive [Focus on intellectual innovation and strategic scaling]
────────────────────────────────────────────────────────────────────────────────
- Open-Sourcing the Benchmark: 266 Edge Cases
To ensure complete mathematical integrity and help fellow developers build reliable tools, we curated and open-sourced our
verification suite:
👉 GitHub Repository: fds2003/funtesthub-astronomy-benchmarks (https://github.com/fds2003/funtesthub-astronomy-benchmarks)
The repository includes 266 verified test fixtures covering:
- 110 Solar-Term Boundary Transitions: Testing births 1 minute before vs. 1 minute after Li Chun across 55 consecutive years (1980–2034).
- 40 Late-Zi Rollovers: Verifying hour stem and day stem transitions at 22:50, 23:30, and 00:30.
- 40 Leap-Month (Run Yue) Edge Cases: Ensuring lunar intercalation does not corrupt solar month pillars.
- 36 Transit Mutations for Year 2027: Verifying the Ding Stem Si Hua transit mappings (Tai Yin Lu, Tian Tong Quan, Tian Ji Ke, Ju Men Ji).
- 40 Historical Charts: Imperial reference charts (Emperor Qianlong, Su Shi, Zeng Guofan).
### Running the Suite Locally
git clone https://github.com/fds2003/funtesthub-astronomy-benchmarks.git
cd funtesthub-astronomy-benchmarks
npm install
npm test
Result:
✓ solar-term-boundary: 110/110 passed (100.0%)
✓ late-zi-rollover: 40/40 passed (100.0%)
✓ leap-month-transit: 40/40 passed (100.0%)
✓ year-2027-ding-wei: 36/36 passed (100.0%)
✓ classical-historical: 40/40 passed (100.0%)
Benchmarks: 266/266 passed (100% accuracy)
────────────────────────────────────────────────────────────────────────────────
Conclusion & Live Demos
Building metaphysical tools shouldn't mean sacrificing modern software engineering standards. By treating ancient
calendrical systems as astronomical state machines and testing them with modern CI/CD pipelines, we can give users
verifiable, private, and deterministic insights.
You can inspect the live implementations across our tools:
- 2027 Fire Goat Annual Fortune Report (https://funtesthub.com/en/2027-horoscope/)
- Online BaZi (Four Pillars) Calculator (https://funtesthub.com/en/bazi/)
- Methodology & Conventions (https://funtesthub.com/en/about/)
If you're working on calendar algorithms, astronomical calculations, or cultural computing, check out the GitHub repo
(https://github.com/fds2003/funtesthub-astronomy-benchmarks), submit edge cases via issues, or let me know your thoughts
in the comments below!
Top comments (0)