I recently found myself in one of those classic developer rabbit holes. A friend asked me if I knew their Chinese zodiac sign, and instead of just Googling it like a normal person, I thought: "I could build a tool for this."
Because apparently I enjoy reinventing wheels.
The twist? I wanted it to work entirely in the browser. No API calls, no server, no database. Just a date input and some JavaScript logic. The challenge was figuring out how to accurately compute Chinese zodiac signs, the Chinese lunar calendar year, and the traditional Ganzhi (干支) system without pulling in a massive calendar library.
The Problem with Existing Solutions
My first instinct was to search for an API. There are plenty of Chinese calendar APIs out there, but they all had issues:
- Most require API keys and rate limiting
- Many are Chinese-language only, which is fine for me but not great for a broader audience
- They're overkill for what should be a simple calculation
- Some have questionable accuracy for historical dates
I also looked at JavaScript libraries like lunar-javascript and chinese-calendar. They're comprehensive, but they're also huge. For a simple "what's my zodiac sign" tool, pulling in a 100KB+ library felt like using a flamethrower to light a candle.
The Math Behind the Madness
Here's what I discovered: the Chinese zodiac and Ganzhi calculations are surprisingly straightforward if you understand the underlying math.
The Zodiac: Simple Modulo Arithmetic
The 12 Chinese zodiac animals follow a cycle that aligns with the 12-year Jupiter cycle. The calculation is embarrassingly simple:
const ZODIAC = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];
const zodiac = ZODIAC[(year - 4) % 12];
That's it. The year 4 AD was the first year of the Rat, so everything since then follows a simple modulo pattern.
The Ganzhi System: Two Interlocking Cycles
The Ganzhi (干支) system combines the 10 Heavenly Stems (天干) with the 12 Earthly Branches (地支). This creates a 60-year cycle. The calculation is similar:
const GAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const ZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
const ganzhi = GAN[(year - 4) % 10] + ZHI[(year - 4) % 12];
For 2024, this gives us 甲辰 (Jia-Chen), which is the Year of the Wood Dragon. The math checks out.
The Constellation: A Simple Date Range Check
Western zodiac constellations are just date ranges. No astronomical calculations needed:
const STAR = [
{ name: '摩羯', start: [12, 22], end: [1, 19] },
{ name: '水瓶', start: [1, 20], end: [2, 18] },
// ... and so on
];
The Tricky Part: Lunar vs. Solar Year
Here's where things got interesting. The Chinese zodiac and Ganzhi are based on the lunar calendar, where the new year starts with the Spring Festival (春节), not January 1st.
This creates an edge case: someone born on January 15, 2024, is technically still in the Year of the Rabbit (癸卯), not the Dragon (甲辰), because Spring Festival 2024 was on February 10th.
I had two options:
- Include a full lunar calendar conversion table — accurate but requires a massive dataset with leap month information
- Compute by solar year and add a caveat — simpler but technically incorrect for dates before Spring Festival
I went with option 2, but I added a helpful note when the date falls before Spring Festival. The tool shows the solar year result but warns the user to verify against the actual Spring Festival date.
This is a classic "good enough for 95% of use cases" decision. The full lunar conversion would require a comprehensive leap month table that spans decades, which felt like overkill for a tool that's primarily for entertainment and cultural reference.
Building It: The AI Collaboration
I built this tool with heavy assistance from AI, and I want to be honest about how that went.
What AI Got Right
The initial structure came together quickly. I described the requirements — a date input, zodiac/constellation/Ganzhi calculations, and a clean display — and the AI generated a working implementation with proper styling and i18n support in one shot.
The i18n setup was particularly impressive. I asked for both Chinese and English support, and the AI created a clean translation object with fallback handling. It also generated proper SEO meta tags and structured data (JSON-LD) that I would have spent too much time on myself.
Where It Stumbled
The first iteration had a bug in the constellation calculation. The date range logic didn't handle the year-boundary case correctly — someone born on January 5th was getting Capricorn instead of Sagittarius. Classic off-by-one error.
I had to explain the issue and provide the corrected date boundaries. The AI got it right on the second try, but it highlighted something important: AI is great at generating code, but you still need to understand the domain to catch logical errors.
Another issue: the AI initially used a switch statement for the constellation lookup. It worked, but it was verbose. I asked for a more elegant array-based approach, which it handled well.
The Back-and-Forth
The most productive part of the collaboration was the iterative refinement. I'd say things like:
- "The date input needs a min and max range to prevent invalid dates"
- "Add a disclaimer about the lunar year issue"
- "Make the result display cleaner with better visual hierarchy"
Each request was handled well, and the final result felt polished. But I'd be lying if I said it was effortless — I had to review every calculation and edge case myself.
What I Learned
Don't Over-Engineer
It's tempting to implement the full lunar calendar conversion, but for this use case, it wasn't necessary. The tool serves a specific purpose: quick, approximate zodiac information. Users who need precise lunar dates will use a dedicated calendar app.
Domain Knowledge Still Matters
AI can write the code, but it can't tell you that the Chinese zodiac follows the lunar year. That's cultural knowledge that requires human understanding. If I hadn't caught this nuance, the tool would be technically wrong for millions of people born in January and early February.
Edge Cases Are Where Bugs Hide
The constellation year-boundary issue was subtle. It's easy to write date range logic that works for most cases but fails at the boundaries. This is a reminder to always test with dates at the start and end of each range.
The Result
The final tool is a single HTML file that works entirely in the browser. No dependencies, no API calls, just vanilla JavaScript and CSS. It handles the date input, performs the calculations, and displays results with proper localization for both Chinese and English users.
You can try it out here: Zodiac & Constellation Calculator
Final Thoughts
Building this tool was a reminder that sometimes the simplest solution is the best one. A full lunar calendar library would have been more "correct," but it would have added complexity without meaningful benefit for the use case.
And on the AI collaboration front: it's a powerful partner, but it's not a replacement for understanding the problem domain. The best results came from a combination of AI's speed and my domain knowledge to catch the subtle cultural and logical issues.
Now if you'll excuse me, I need to figure out what my actual zodiac sign is, because apparently I've been telling people the wrong one for years. Classic "works on my machine" situation, but for my birth date.
Tags: javascript, webdev, algorithms, ai, productivity
Top comments (0)