DEV Community

David Webb
David Webb

Posted on

I tried to put a 2,000-year-old calendar into one boundary rule

Title: I tried to put a 2,000-year-old calendar into one boundary rule

I once tried to fit different traditional calendar schools into one boolean truth table. The disagreements looked like logic bugs until I saw the structural assumptions underneath.

An ancient astronomical cycle's boundary depends on which solar coordinate or epoch anchor you choose as the primary axis. Each school has a self-consistent coordinate system and its own edge definitions; one truth table can't reconcile them without flattening those differences.

Now my schema keeps core graph processing separate from boundary parsing. I made the parsing strategy configurable by calendar convention instead of baking one convention in as the absolute truth.

Early on, I expected that translating a birth time into four pillars (year, month, day, and hour) would follow a single standard. When comparing outputs across different BaZi tools, I noticed they frequently disagreed on edge cases. For instance, the year pillar boundary is not uniformly set at Lunar New Year across all schools. Many conventions anchor the year transition to the solar term Lichun instead. A person born in early February can land on completely different year labels depending on which year-boundary convention the software enforces.

Month pillars present similar branch points. The calendar divides the year into 12 solar months, anchoring month transitions to specific solar terms rather than lunar months. Meanwhile, the day pillar relies on its own 60-day position, with competing rules turning the day over at 23:00 or at midnight. Because the hour stem assignment is linked to the day stem, changing the day rollover boundary cascade-updates the hour pillar too.

Longer ten-year phases, or decade pillars, add another layer of configuration. Derived from the month pillar, their direction and starting age depend on conventions involving the birth year and the distance to a nearby solar term. Each phase advances through the stem-branch sequence every ten years. This works like a state machine where the birth chart provides the initial state and the decade pillar updates at calculated transition points, though the machine merely models label shifts under stated rules.

Attempting to hardcode all these rules into one rigid conditional block caused constant refactoring. The underlying modulo lookup is perfectly consistent; the divergence happens entirely during boundary parsing and input normalization.

To clean up the system, I refactored the pipeline. Step one parses the timestamp, location, and timezone context. Step two converts the timestamp to working local time. Step three identifies the year and solar-term month. Step four calculates the sexagenary day index. Step five divides the day into two-hour intervals and derives the hour stem.

I separated core cycle calculations from the boundary parsing rules. By keeping the schema modular and making boundary strategies configurable by calendar convention, the engine avoids declaring one school's edge definitions as universal truth.

I am still unsure how much different schools' boundary choices alter charts across large population datasets, especially for births occurring near midnight or solar-term transitions. Accepting that distinct coordinate systems exist made the code far more maintainable.

Not medical, legal, or financial advice. A cultural and cognitive framework for self-reflection; outputs vary by individual.

Top comments (0)