Your birth time is lying to you: a time-zone rabbit hole in a Chinese astrology calculator

2026年8月22日2 次浏览来源:Dev.to阅读原文

I built a calculator for BaZi — Chinese "Four Pillars" birth charts.

Whatever you think of the interpretive tradition (and I'll get to that), the input math turned out to be a genuinely deep time-zone problem, and that's what this post is about.

If you've ever thought "time zones, how hard can it be" — this is a tour of exactly how hard, with working TypeScript.

The problem BaZi divides the day into twelve two-hour "branches", so your birth hour is one of the chart's four pillars.

Get the hour wrong and you get a different chart — not slightly different, categorically different.

Every calculator I could find feeds the system the wall-clock time from your birth certificate.

But the tradition predates time zones by about two thousand years; it obviously means solar time — where the sun actually was over your birthplace.

Clock time and solar time differ by more than most people think, and the difference decomposes into exactly three parts:

1.

Daylight saving time — and it's historical.

You need the DST rules in force on the birth date, not today's.

China ran a now-forgotten DST experiment from 1986–91; Harbin kept its own zone before

1949.

If you were born in Beijing in July 1988, your certificate is an hour ahead of standard time and no modern-day lookup will tell you that.

2.

Longitude.

Solar time shifts 4 minutes per degree from your zone's standard meridian.

China spans five geographic zones but uses one clock — born in Ürümqi, your clock runs about two hours ahead of the sun.

It's not just a China quirk: Vancouver sits at 123°W in a zone whose meridian is 120°W, so that's another 12 minutes, everywhere, always.

3.

The equation of time.

The sun itself runs up to ±16 minutes fast or slow over the year, thanks to orbital eccentricity and axial tilt.

NOAA publishes an approximation that's accurate to under a minute: Stack all three and a July birth in Vancouver needs ~78 minutes of correction.

That's easily a different hour branch — a different chart.

Getting historical offsets without shipping a tz database Here's the part that surprised me: you don't need to bundle tz data.

Node's is backed by ICU, which ships the full IANA tzdb — including the historical oddities.

The trick is that will happily format a UTC instant in any zone, and from the formatted parts you can recover the offset: Feed it and a 1988 timestamp and you get the 1986–91 DST hour back, for free.

No dependency, no data file, and it stays current with the runtime's ICU.

Going the other way — wall time to UTC — has the classic chicken-and-egg problem (you need the offset to compute the instant, but the offset depends on the instant).

Two fixed-point iterations settle it everywhere except inside the one-hour DST gap, where no exact answer exists anyway.

There's a subtler one hiding in "was DST active?".

JavaScript has no API, so I sample the zone's offset on Jan 1, Jul 1, and the birth instant, and take the minimum as the standard offset — DST always moves clocks forward, so the minimum is standard time in both hemispheres.

Sampling the birth instant too matters because of Morocco, which observes negative DST during Ramadan; without it, the heuristic reports a +60-minute DST that never happened.

The two edge cases I didn't see coming The date line.

The Chatham Islands sit at 176.5°W and use UTC+12:45.

Do the naive thing — longitude × 4 minutes from Greenwich — and the computed local mean solar time lands a full day off.

In a birth chart that silently corrupts the day pillar, which is the pillar the whole reading hangs on.

The fix is to normalize longitude to within ±180° of the zone's standard meridian, not Greenwich: Rounding that has to add up.

The UI shows the three components as an addition table: DST + longitude + equation of time = total.

Round each part independently and the table stops summing — off-by-one minutes that make the whole thing look broken.

So the rounded parts are forced to sum exactly to the rounded total, with the residual assigned to whichev

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools