The Great Wall of Manselyeok --- Neither Gregorian Nor Lunar, but Solar Terms
A Project That Needs Three Calendars
When people hear "Saju app," most assume you just take a birth date and crunch some numbers. I thought the same at first. But the moment I began implementation, the first wall appeared. Four Pillars of Destiny (Saju) does not use the Gregorian calendar. It does not use the Lunar calendar either. It runs on an entirely different system called the Solar Term calendar (Jeolgi-ryeok, 節氣曆).
The Gregorian calendar divides the year based on Earth's orbit around the sun. The Lunar calendar divides it based on the phases of the moon. The Solar Term calendar divides it based on the sun's ecliptic longitude. All three relate to the sun, but they slice dates by completely different criteria. Building a Saju app means taking a Gregorian input, converting it to Lunar, and then converting it again to Solar Terms.
This "three-layer calendar conversion" is the essence of the Ten-Thousand-Year Calendar (Manselyeok, 萬歲曆) module. And it is the single most technically challenging piece of a Saju app --- a fact I only truly appreciated once implementation was underway.
The Year Pillar: It Changes at the Start of Spring, Not January 1
In everyday life, the year flips on January 1 (Gregorian) or Lunar New Year. In Saju, the Year Pillar changes at the Start of Spring (Ipchun, 立春), a Solar Term that typically falls between February 3 and 5 --- but the exact date and time shift every year.
Here is where implementation difficulty becomes real. The 2024 Start of Spring falls on February 4 at 16:27. Based on this timestamp, a baby born at 16:26 on that day and a baby born at 16:28 have different Year Pillars. One minute separates Gap-Jin year (甲辰年) from Gye-Myo year (癸卯年). In zodiac terms, that is the difference between the Dragon and the Rabbit.
This means you cannot hardcode "February 4 = new year." You need the exact date and time of the Start of Spring for every year, and you must compare it to the birth time down to the minute. Not "around early February" but "February 4, 2024, 16:27." Precision-level data is required.
This single requirement reveals that a Saju app demands a level of calendar precision far beyond typical date arithmetic. And the precision demands do not stop at the Year Pillar.
The Month Pillar: 12 Solar Terms Mark the Boundaries
Implementing the Month Pillar is even trickier than the Year Pillar. Normally, "which month" changes on the 1st of each month, whether Gregorian or Lunar. In Saju, the month boundaries are defined by the 12 "Jie" (節, "node") Solar Terms out of the 24 total.
Specifically: from the Start of Spring (early February) until the Awakening of Insects is the first Saju month (In-wol, 寅月). From the Awakening of Insects to the Clear and Bright is the second month (Myo-wol, 卯月). And so on through 12 Solar Terms defining 12 months: Start of Spring, Awakening of Insects, Clear and Bright, Start of Summer, Grain in Ear, Minor Heat, Start of Autumn, White Dew, Cold Dew, Start of Winter, Heavy Snow, Minor Cold.
Looking at the same date through three calendars gives entirely different results. For example, March 3, 2024 is the 3rd month in Gregorian, the 23rd of the 1st month in Lunar, and the 2nd month (Myo-wol) in Saju. Gregorian month, Lunar month, and Saju month --- all different. Confusing these systems means the Month Pillar is wrong, and a wrong Month Pillar cascades into every analysis built on top of it.
The Month Pillar repeats the same problem as the Year Pillar: you need the precise time of each Solar Term. If the Awakening of Insects falls on March 5 at 10:23, then births at 10:22 and 10:24 on that day belong to different Saju months. Minute-level precision for each of the 12 boundary Solar Terms, every year.
The Day Pillar: The Endless Cycle of the 60 Stem-Branch Pairs
Unlike the Year and Month Pillars --- which depend on "variable boundaries" set by Solar Terms --- the Day Pillar follows a relatively simple rule. The 60 Stem-Branch pairs (Gap-Ja, Eul-Chuk, Byeong-In... Gye-Hae) cycle endlessly. After Gap-Ja day comes Eul-Chuk day, then Byeong-In day. Every 60 days, Gap-Ja comes around again.
The challenge is pinning down the "reference point" for this cycle. Historically, the 60-day cycle has reportedly never been broken --- it has continued uninterrupted since the Shang dynasty of ancient China. So if you know a particular date's Day Pillar, you can count forward or backward to find any other date's Day Pillar.
But direct implementation has pitfalls: leap year handling, date mismatches during Gregorian-Lunar conversion, and the Late Night Hour (Yajasi) problem that can shift the Day Pillar even within the same calendar date. The Late Night Hour issue is covered in detail in Part 8.
In practice, I used the Day Pillar data from the Manselyeok library but cross-verified it rigorously. I selected multiple reference dates and confirmed they matched professional Manselyeok websites. If even one date is off, every Day Pillar after it is wrong --- making this verification non-negotiable.
Building a Manselyeok in the JS Ecosystem
The longest deliberation during design was choosing the calendar library. In the JavaScript/TypeScript ecosystem, no single library fully supports a "Saju-grade Manselyeok." Gregorian-to-Lunar conversion, precise Solar Term times, 60-Cycle Day Pillar calculation, True Solar Time correction --- no single package covered all four.
I asked Claude to compare and analyze Korean Lunar calendar-related libraries on NPM. The AI organized each library's coverage, last update date, Solar Term support, precision level, and user count into a table. That single comparison chart dramatically shortened decision-making time.
The final strategy was a "combination approach." Instead of relying on one library, combine the best tool for each area. Gregorian-to-Lunar conversion is handled by the korean-lunar-calendar library, which ships with Lunar data from 1890 to 2050 --- more than sufficient range. Precise Solar Term times come from Korea Astronomy and Space Science Institute (KASI) data, sourced separately. True Solar Time and DST corrections are implemented in-house. And the Saju pillar calculation and analysis engine are built entirely from scratch based on the design documents.
The key principle of this combination strategy: "Use external libraries only as data sources, and keep all Saju logic under our own control." This minimizes the risk that a library bug or abandoned update could compromise Saju calculation accuracy.
Why No Single Library Suffices
The korean-lunar-calendar library is reliable for Gregorian-Lunar conversion, but it does not provide Solar Term times to the minute. Yet as we have seen, minute-level precision is mandatory for Saju. Knowing only the "date" of the Start of Spring is not enough to adjudicate a Year Pillar for someone born on that day.
Conversely, KASI data offers perfect Solar Term precision but includes no Gregorian-Lunar conversion or 60-Cycle Day Pillar calculation. Each tool has its own strengths and blind spots.
AI shone here. Analyzing each library's strengths and weaknesses individually is something a developer can do. But rapidly synthesizing multiple libraries' strengths into "combine them this way to cover everything" --- that synthesis was far more efficient through AI conversation.
In practice, 30 minutes of discussing candidates and architecting a combination plan with AI was faster than installing and testing libraries one by one. The final verification still required running actual code, but "where to start" was settled quickly, saving significant time.
The Complexity of Three-Layer Conversion
The Manselyeok module's actual pipeline looks like this: User enters a Gregorian birth date and time. Convert Gregorian to Lunar (korean-lunar-calendar). Simultaneously, look up precise Solar Term times for that year (KASI data). Compare the birth time to Solar Term times to determine the Year Pillar. Repeat for the Month Pillar. Calculate the Day Pillar via the 60-Cycle. Determine the Hour Pillar from the Day Stem and birth time, applying True Solar Time correction.
Each step in this pipeline depends on the one before it, and an error at any stage invalidates the final result. If the Year Pillar is wrong, the Month Pillar calculation is compromised. If the Day Pillar is wrong, the Hour Pillar and all Ten Gods analysis collapse. Manselyeok accuracy is app-wide trustworthiness.
Having diagrammed this pipeline in the design documents paid off during implementation. Each step could be implemented as an independent function with its own unit tests. Instead of "test the whole thing at once," the strategy became "verify each conversion step individually" --- a much more tractable approach.
What I Learned Along the Way
First, the core difficulty of Saju app development lies not in "analysis algorithms" but in "calendar conversion." Five Elements ratios and Ten Gods classification follow clear rules. But converting Gregorian dates into Solar Terms depends on astronomical data --- a fundamentally different kind of problem.
Second, a combination strategy --- not relying on any single library --- is the realistic choice for this domain. Waiting for a perfect library is slower than assembling the best of each area.
Third, AI-assisted library comparison is most efficient at the "narrowing options" stage. It does not replace hands-on testing, but it fast-tracks the decision of what to test.
Fourth, step-by-step separation of the Manselyeok pipeline is the key to testability. Building a complex system as one monolith makes it nearly impossible to locate "where things went wrong."
Next Up
The Manselyeok structure is in place. Now comes the more specific battle over Solar Term data "precision." How I sourced KASI data, why I chose a build-time strategy to pre-collect 130 years' worth into JSON, and real cases where minute-level precision changes someone's chart --- Part 7 covers it all.
댓글
댓글 쓰기