Verification corpus
34 cases the engine is tested against on every build. If any pillar below stops reproducing, the site does not deploy. The same rules are applied to entire birth years on the birth-year pages — 1987 and 1988 carry the mainland daylight-saving run tested in the dst cases below.
The point is not to claim we are right. It is to show where implementations diverge and why — so that when a chart from the BaZi calculator disagrees with another site, the disagreement can be located instead of disputed. See the conventions for the choices behind them, what each pillar is derived from, or how to read your own Day Master off the day pillar the day-boundary cases below are testing.
Solar-term boundaries
A birth minutes either side of Li Chun changes the year pillar. These are the cases where feeding true solar time into the term comparison — the intuitive implementation — returns the wrong year.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −13.86m = −37.18m | 2024-02-04 15:48:49 | 癸卯 乙丑 戊戌 庚申 | One minute before Li Chun 16:27:07 CST |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −13.86m = −37.18m | 2024-02-04 15:50:49 | 甲辰 丙寅 戊戌 庚申 | One minute after — year pillar must advance A naive true-solar chain returns year 癸卯. |
America/New_York · -74.00° | lon +4.00m EoT −13.86m = −9.86m | 2024-02-04 03:20:08 | 甲辰 丙寅 戊戌 甲寅 | 03:30 EST = 16:30 CST, past Li Chun. A naive TST chain returns the wrong year A naive true-solar chain returns year 癸卯. |
Europe/London · -0.13° | lon −0.52m EoT −13.86m = −14.38m | 2024-02-04 08:15:37 | 甲辰 丙寅 戊戌 丙辰 | 08:30 GMT = 16:30 CST — same trap, different offset A naive true-solar chain returns year 癸卯. |
Australia/Sydney · 151.21° | lon −55.16m EoT −13.86m = −69.02m | 2024-02-04 18:20:59 DST in force | 甲辰 丙寅 戊戌 辛酉 | Southern hemisphere, ahead of CST |
Historical time zones
Singapore ran UTC+7:30 until 1982. Hong Kong observed summer time. China observed daylight saving from 1986 to 1991, which affects an entire cohort of mainland births. These are resolved from the IANA database rather than assumed.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Singapore · 103.82° | lon −34.72m EoT −9.28m = −44.00m | 1970-01-15 11:15:59 | 己酉 丁丑 乙未 壬午 | Singapore ran UTC+7:30 before 1982 |
Asia/Singapore · 103.82° | lon −64.72m EoT −9.28m = −74.00m | 1990-01-15 10:45:59 | 己巳 丁丑 庚辰 辛巳 | Same city after the 1982 shift to UTC+8 |
Asia/Hong_Kong · 114.17° | lon −83.32m EoT −5.81m = −89.13m | 1965-07-15 10:30:52 DST in force | 乙巳 癸未 庚午 辛巳 | Hong Kong observed summer time — UTC+9 |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −6.03m = −29.35m | 2024-07-15 11:30:39 | 甲辰 辛未 庚辰 壬午 | Same city, no DST — control for the above |
Asia/Shanghai · 75.99° | lon −236.04m EoT −0.30m = −236.34m | 1990-06-15 08:03:39 DST in force | 庚午 壬午 辛亥 壬辰 | China observed DST 1986-1991; far-west longitude compounds it |
America/Los_Angeles · -118.24° | lon −52.96m EoT −5.93m = −58.89m | 1990-07-15 11:01:06 DST in force | 庚午 癸未 辛巳 甲午 | Western hemisphere DST, negative longitude |
Daylight-saving edges
A birth time inside the spring-forward gap never occurred; inside the autumn overlap it occurred twice. Both are detected and warned about rather than silently resolved. These are the hand-verified hard cases behind that detection.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
America/New_York · -71.06° | lon +15.76m EoT −10.17m = +5.59m | 2024-03-10 02:00:35 | 甲辰 丁卯 癸酉 癸丑 | [HAND-VERIFIED] Correction interval crosses the spring-forward transition (02:00 EST -> 03:00 EDT). The wall-clock correction must land near 02:00-02:10, not jump to 03:0x — see scripts/run-fixtures.ts for the structural assertion. |
America/New_York · -74.00° | lon −56.00m EoT −10.16m = −66.16m | 2024-03-10 02:23:50 DST in force | 甲辰 丁卯 癸酉 癸丑 | [HAND-VERIFIED] 02:30 on this date never occurred in America/New_York — the gap. Must carry the nonexistent-time warning. |
America/New_York · -74.00° | lon −56.00m EoT +16.49m = −39.51m | 2024-11-03 00:50:29 DST in force | 甲辰 甲戌 辛未 戊子 | [HAND-VERIFIED] 01:30 on this date occurred twice in America/New_York — the fall-back overlap. Must carry the ambiguous-time warning. |
Equation of time
The correction most calculators omit. It reaches roughly ±14 to ±16 minutes, which is enough to move an hour branch.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −14.30m = −37.62m | 1990-02-11 11:22:22 | 庚午 戊寅 丁未 丙午 | EoT annual minimum ~-14.2 min |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT +3.69m = −19.63m | 1990-05-14 11:40:22 | 庚午 辛巳 己卯 庚午 | EoT local maximum ~+3.7 min |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −6.48m = −29.80m | 1990-07-26 11:30:11 | 庚午 癸未 壬辰 丙午 | EoT local minimum ~-6.5 min |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT +16.46m = −6.86m | 1990-11-03 11:53:08 | 庚午 丙戌 壬申 丙午 | EoT annual maximum ~+16.4 min |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.16m = −23.48m | 1990-04-15 11:36:31 | 庚午 庚辰 庚戌 壬午 | EoT near zero crossing — sign errors hide here |
The day boundary
Where the two schools genuinely disagree. Both columns are shown; neither is presented as the error.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.41m = −23.73m | 1990-06-15 22:36:16 | 庚午 壬午 辛亥 己亥 | Clock 23:00 is NOT the start of zi at this longitude — TST 22:36 is still the 亥 hour. Guards against reading the zi boundary off clock time |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.41m = −23.73m | 1990-06-15 23:06:16 | 庚午 壬午 辛亥 庚子 | Mid late-zi — the canonical school-difference case Other school gives day 壬子. |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.42m = −23.74m | 1990-06-15 23:35:15 | 庚午 壬午 辛亥 庚子 | Last minute before midnight Other school gives day 壬子. |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.42m = −23.74m | 1990-06-15 23:37:15 crosses calendar day | 庚午 壬午 辛亥 庚子 | Clock is past midnight but TST falls back to the previous day — crossedDayBoundary must be true and the day pillar must stay on the 15th Other school gives day 壬子. |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.42m = −23.74m | 1990-06-16 00:06:15 | 庚午 壬午 壬子 庚子 | Genuine early zi: day pillar advances to the 16th while the hour stays 子 |
Extreme longitudes
Places far from their own zone meridian, where the correction runs to hours rather than minutes.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Shanghai · 75.99° | lon −236.04m EoT −0.27m = −236.31m | 1990-06-15 04:03:41 DST in force | 庚午 壬午 辛亥 庚寅 | Largest ordinary correction in the corpus (~-236 min) |
Asia/Shanghai · 91.14° | lon −175.44m EoT −0.26m = −175.70m | 1990-06-15 04:04:18 DST in force | 庚午 壬午 辛亥 庚寅 | Tibet on Beijing time |
Pacific/Auckland · 174.76° | lon −20.96m EoT −0.28m = −21.24m | 1990-06-15 11:38:45 | 庚午 壬午 辛亥 甲午 | Near the date line, large positive longitude |
Pacific/Kiritimati · -157.47° | lon −29.88m EoT −0.50m = −30.38m | 2024-06-15 11:29:37 | 甲辰 庚午 庚戌 壬午 | [HAND-VERIFIED] Civil offset UTC+14 but geographic longitude ~-157° — sign mismatch across the international date line. Unwrapped arithmetic gives a ~1470-minute (24.5h) correction; hand check: reference meridian 210° (=840min/4), longitude re-expressed as 202.53° (=-157.47+360) is 7.47° from it, so the correction must be (7.47°×4=)~29.9 minutes, not 24.5 hours. |
Solar-term frame vs. daylight saving
China observed daylight saving 1986–1991. Year and month pillars compare the birth instant against a solar-term table that assumes a constant UTC+8 — feeding it a DST-era wall-clock reading without first undoing the DST shift silently returns the wrong month for a birth near a term boundary.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Shanghai · 116.41° | lon −74.36m EoT −5.73m = −80.09m | 1988-08-07 14:39:54 DST in force | 戊辰 己未 甲午 辛未 | [HAND-VERIFIED] Mainland China DST era (1986-1991), 20 minutes before that year's 立秋 in true UTC terms — the solar-term-frame DST bug flips this to the wrong month. |
Control charts
Ordinary births used to compare against independent references.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.31m = −23.63m | 1990-06-15 11:36:22 | 庚午 壬午 辛亥 甲午 | Known chart: 庚午 壬午 辛亥 甲午 on clock time |
Asia/Shanghai · 116.41° | lon −14.36m EoT −2.91m = −17.27m | 1999-12-31 23:42:44 crosses calendar day | 己卯 丙子 丁巳 壬子 | Round date, reference-site comparison Other school gives day 戊午. |
Asia/Taipei · 121.56° | lon +6.24m EoT +2.51m = +8.75m | 1985-09-09 09:17:45 | 乙丑 乙酉 辛亥 癸巳 | Third reference-site comparison |
Unknown birth hour
When the hour is not known, the day pillar cannot use true solar time — it falls back to the birth’s own local calendar date, never a date converted through China’s time zone.
| Birth | Correction | True solar time | Pillars | Note |
|---|---|---|---|---|
America/New_York · -74.00° | lon −56.00m EoT −0.42m = −56.42m | 1990-06-15 11:03:34 DST in force | 庚午 壬午 辛亥 — | [HAND-VERIFIED] Hour unknown — day pillar must be 辛亥 (the local calendar date), not 壬子 (the China-time-converted date the pre-fix bug produced). |
Asia/Hong_Kong · 114.17° | lon −23.32m EoT −0.41m = −23.73m | 1990-06-15 23:06:16 | 庚午 壬午 辛亥 — | [HAND-VERIFIED] Hour unknown, sect 1, placeholder sits inside the 23:00-23:59 late-zi window — day pillar must stay 辛亥, not roll to 壬子. |
Independent engines
An engine agreeing with itself proves nothing, so every known-hour chart above was also recomputed by an independently written implementation (32 charts). One scope statement, honestly: both engines sit on the same open calendar kernel (lunar-javascript), so agreement on ordinary charts mostly certifies those shared tables. What this comparison isolates is the layer each project had to write separately — the true-solar-time integration — and there the two agree to within a rounding step on all 32 correction chains while splitting on exactly 3 charts, each split being a Li Chun case whose outcome equals the naive-chain prediction this corpus registered before any independent engine was ever run.
No BaZi package on npm ships an independent calendar kernel — finntang, shunshi, bazi.js, soul-atelier and cantian all wrap lunar-* or tyme4ts (the same 6tail lineage), which is why the comparison above is scoped the way it is. bazi-calculator.com was probed live instead and is recorded as a point-in-time snapshot with its own documented behaviour; iwzbz exposes no public API. The independent implementations this page runs are public — @openfate/bazi-engine and @openfate/true-solar-time on npm, both built on the open lunar-javascript kernel — and the full register, raw numbers and evidence hashes are committed in fixtures/references.json. What this cannot test is the solar-term table itself, since both engines share it — that table is checked against NASA's planetary ephemeris in the section below.
| Birth | This engine | @openfate/bazi-engine | Result |
|---|---|---|---|
Asia/Hong_Kong | 庚午 戊寅 丁未 丙午 | 庚午 戊寅 丁未 丙午 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 辛巳 己卯 庚午 | 庚午 辛巳 己卯 庚午 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 癸未 壬辰 丙午 | 庚午 癸未 壬辰 丙午 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 丙戌 壬申 丙午 | 庚午 丙戌 壬申 丙午 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 庚辰 庚戌 壬午 | 庚午 庚辰 庚戌 壬午 | Agrees on all four pillars. |
Asia/Singapore | 己酉 丁丑 乙未 壬午 | 己酉 丁丑 乙未 壬午 | Agrees on all four pillars. |
Asia/Singapore | 己巳 丁丑 庚辰 辛巳 | 己巳 丁丑 庚辰 辛巳 | Agrees on all four pillars. |
Asia/Hong_Kong | 乙巳 癸未 庚午 辛巳 | 乙巳 癸未 庚午 辛巳 | Agrees on all four pillars. |
Asia/Hong_Kong | 甲辰 辛未 庚辰 壬午 | 甲辰 辛未 庚辰 壬午 | Agrees on all four pillars. |
Asia/Shanghai | 庚午 壬午 辛亥 壬辰 | 庚午 壬午 辛亥 壬辰 | Agrees on all four pillars. |
America/Los_Angeles | 庚午 癸未 辛巳 甲午 | 庚午 癸未 辛巳 甲午 | Agrees on all four pillars. |
America/New_York | 甲辰 丁卯 癸酉 癸丑 | 甲辰 丁卯 癸酉 癸丑 | Agrees on all four pillars. |
America/New_York | 甲辰 丁卯 癸酉 癸丑 | 甲辰 丁卯 癸酉 癸丑 | Agrees on all four pillars. |
America/New_York | 甲辰 甲戌 辛未 戊子 | 甲辰 甲戌 辛未 戊子 | Agrees on all four pillars. |
Asia/Hong_Kong | 癸卯 乙丑 戊戌 庚申 | 癸卯 乙丑 戊戌 庚申 | Agrees on all four pillars. |
Asia/Hong_Kong | 甲辰 丙寅 戊戌 庚申 | 癸卯 乙丑 戊戌 庚申 | Diverges — The reference engine folds the birth time through true solar time before comparing against the Li Chun instant, which pushes this birth back across the boundary. Its year 癸卯 is exactly the value this corpus records as the naive-chain prediction (yearIfTstUsedNaively), and its month 乙丑 follows from the same shifted frame instead of 丙寅. Day and hour still agree. |
America/New_York | 甲辰 丙寅 戊戌 甲寅 | 癸卯 乙丑 戊戌 甲寅 | Diverges — Same mechanism as lichun-hk-after: the reference compares the solar-time-shifted clock reading against the term instant rather than comparing absolute instants, landing on the pre-registered naive prediction 癸卯/乙丑 where the absolute-instant comparison gives 甲辰/丙寅. New York sits west of the term instant in local wall time but east of it in absolute time — the frame choice decides the answer here, which is why the case exists in the corpus. |
Europe/London | 甲辰 丙寅 戊戌 丙辰 | 癸卯 乙丑 戊戌 丙辰 | Diverges — Same mechanism as lichun-hk-after: the solar-time-shifted comparison lands on the pre-registered naive prediction 癸卯/乙丑 instead of the absolute-instant result 甲辰/丙寅. London’s longitude happens to sit near its zone meridian, so nearly the whole correction is equation of time — isolating the term-comparison frame as the sole cause. |
Australia/Sydney | 甲辰 丙寅 戊戌 辛酉 | 甲辰 丙寅 戊戌 辛酉 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 辛亥 己亥 | 庚午 壬午 辛亥 己亥 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 辛亥 庚子 | 庚午 壬午 辛亥 庚子 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 辛亥 庚子 | 庚午 壬午 辛亥 庚子 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 辛亥 庚子 | 庚午 壬午 辛亥 庚子 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 壬子 庚子 | 庚午 壬午 壬子 庚子 | Agrees on all four pillars. |
Asia/Shanghai | 庚午 壬午 辛亥 庚寅 | 庚午 壬午 辛亥 庚寅 | Agrees on all four pillars. |
Asia/Shanghai | 庚午 壬午 辛亥 庚寅 | 庚午 壬午 辛亥 庚寅 | Agrees on all four pillars. |
Pacific/Auckland | 庚午 壬午 辛亥 甲午 | 庚午 壬午 辛亥 甲午 | Agrees on all four pillars. |
Pacific/Kiritimati | 甲辰 庚午 庚戌 壬午 | 甲辰 庚午 庚戌 壬午 | Agrees on all four pillars. |
Asia/Shanghai | 戊辰 己未 甲午 辛未 | 戊辰 己未 甲午 辛未 | Agrees on all four pillars. |
Asia/Hong_Kong | 庚午 壬午 辛亥 甲午 | 庚午 壬午 辛亥 甲午 | Agrees on all four pillars. |
Asia/Shanghai | 己卯 丙子 丁巳 壬子 | 己卯 丙子 丁巳 壬子 | Agrees on all four pillars. |
Asia/Taipei | 乙丑 乙酉 辛亥 癸巳 | 乙丑 乙酉 辛亥 癸巳 | Agrees on all four pillars. |
America/New_York | 庚午 壬午 辛亥 — | not comparable | Reference requires an hour and declines none; this engine casts three pillars for unknown hours rather than estimate. |
Asia/Hong_Kong | 庚午 壬午 辛亥 — | not comparable | Reference requires an hour and declines none; this engine casts three pillars for unknown hours rather than estimate. |
SOURCE REGISTER
- @openfate/bazi-enginepillars-reference
Independent self-written true-solar-time layer over a SHARED calendar kernel: like this engine it wraps lunar-javascript, so agreement on ordinary charts mostly reflects the common kernel. What the comparison actually tests is the independently implemented solar-time integration — precisely the layer this project had to write itself.
Method — Run offline against the version-pinned npm package; each fixture replayed with the effective UTC offset this engine resolved from IANA plus the raw longitude.
@openfate/bazi-engine@1.1.2 · @openfate/true-solar-time@4.0.2
- @openfate/true-solar-timechain-anchor
MIT, zero-dependency astronomy (Meeus equation of time, longitude compensation, IANA resolution) — no shared lineage with either engine kernel.
Method — Correction-chain numbers recomputed offline for every known-hour fixture.
@openfate/true-solar-time@4.0.2
- bazi-calculator.comlive-web-reference (point-in-time snapshot)
Separate implementation and author; applies its own equation-of-time correction (branded "RST") to the clock time.
Method — Form POST replaying each fixture’s wall-clock time; pillar cells parsed from response HTML; evidence excerpts plus full-response SHA-256 committed under fixtures/reference-evidence/. Inherently non-reproducible: third-party engine, bot-rate-limited (~15 fast requests), silent changes possible.
- Applies an equation-of-time correction but NO longitude compensation: the server ignores the longitude form field (POSTs differing only in longitude return identical charts — verified 2026-08-26).
- Year-pillar boundary behaviour fit the "wall clock treated as UTC, EoT-subtracted, compared against absolute term instants" model across three Li Chun probes — the exact naive chain documented in the Solar-term boundaries group.
- Timezone dropdown spans UTC−12…+12 in whole/half steps only; UTC+13/+14 unrepresentable.
- A blank hour field falls back to an arbitrary preset time rather than declining.
- cantian.aidisqualified
Disqualified before fetching: cantian’s bazi-mcp depends on tyme4ts, whose author (6tail) also wrote lunar-javascript — the shared kernel beneath both engines. Agreement would prove nothing about either.
- iwzbzunreachable
A web paipan exists at app.iwzbz.com/paipan/ but exposes no public programmatic endpoint (probed 2026-08-26); apps are closed. Recorded as unavailable rather than approximated.
- NASA-JPL-Horizonsephemeris-api
NASA JPL's DE441 planetary ephemeris shares no lineage with lunar-javascript, tyme4ts or any BaZi package. This is an external check on the solar-term TABLES themselves — the one layer no engine-to-engine comparison can certify, since every engine sits on the same tables.
Method — scripts/fetch-solar-terms.ts queries ssd.jpl.nasa.gov/api/horizons.api for the sun's apparent geocentric ecliptic longitude OF DATE around each term instant our kernel predicts, and locates the crossing of the term's defining multiple of 15° by linear interpolation between bracketing samples (sub-second). Scope: Li Chun every year 1930–2050, all 24 terms for 1950/1970/1990/2010 and 2024–2027. Raw records committed in fixtures/solar-term-audit.json.
https://ssd.jpl.nasa.gov/api/horizons.api
fetched 2026-08-26T00:08:11.451Z
The solar-term table against NASA's ephemeris
Year and month pillars are assigned by comparing a birth instant against solar-term instants — the moments the sun's apparent geocentric ecliptic longitude reaches each term's defining multiple of 15°. Every engine above sits on those same tables, so no engine-to-engine comparison can certify them. This one can: each instant below was located independently in NASA JPL's DE441 planetary ephemeris via the public Horizons API, by finding where the apparent longitude of date crosses the defining degree between bracketing samples. Li Chun — the year-pillar boundary — is checked for every year 1930–2050, and all twenty-four terms are checked for 1950, 1970, 1990, 2010, 2024, 2025, 2026, 2027.
The worst deviation across all 305 checked instants is 3.97 seconds (立春 2049); the mean absolute deviation is 1.03 s for births since 1962. For earlier years the comparison crosses two honest wrinkles at once: Horizons reads pre-1962 input on the UT1 scale, and the small difference that remains (2.08 s mean) reflects different ways of modelling ΔT, the drift between atomic and rotation-based time — not a disagreement about where the sun was.
The build re-checks this table against the committed record on every deploy: if a dependency update ever shifts any term instant by more than two minutes, the site does not build. Raw records: fixtures/solar-term-audit.json; regenerate with scripts/fetch-solar-terms.ts.
| Year (all 24 terms) | Worst |Δ| | Mean |Δ| |
|---|---|---|
| 1950 | 2.89 s | 2.01 s |
| 1970 | 3.08 s | 1.10 s |
| 1990 | 2.51 s | 1.22 s |
| 2010 | 3.02 s | 0.97 s |
| 2024 | 2.25 s | 1.11 s |
| 2025 | 1.65 s | 0.88 s |
| 2026 | 2.69 s | 0.98 s |
| 2027 | 2.04 s | 0.98 s |
These tests are what the $39 report and the 2027 forecast are checked against on every build — read the complete free sample before paying anything, or see the conventions themselves on the methodology page.