Astronomical Chinese calendar and 四柱八字 computation for TypeScript. Solar terms, lunar calendar, stem-branch cycles, and divination metadata — all from first principles.
import { computeFourPillars } from 'stembranch' ;
const pillars = computeFourPillars ( new Date ( 2024 , 1 , 10 , 14 , 30 ) ) ;
// → { year: {stem: '甲', branch: '辰'},
// month: {stem: '丙', branch: '寅'},
// day: {stem: '壬', branch: '午'},
// hour: {stem: '丁', branch: '未'} }
Zero production dependencies. Self-contained VSOP87D (2,425 terms) and Meeus Ch. 49 new moon algorithms with sub-second solar term precision and full lunar calendar computation.
import { computeFourPillars } from 'stembranch' ;
const pillars = computeFourPillars ( new Date ( 2024 , 1 , 10 , 14 , 30 ) ) ;
import { getSolarTermsForYear , findSpringStart } from 'stembranch' ;
const terms = getSolarTermsForYear ( 2024 ) ;
// → 24 SolarTerm objects with exact UTC moments
const springStart = findSpringStart ( 2024 ) ;
// → 2024-02-04T00:27:... UTC (立春, solar longitude 315°)
import { getLunarNewYear , gregorianToLunar , getLunarMonthsForYear } from 'stembranch' ;
// Compute Lunar New Year from first principles (no lookup table)
const lny = getLunarNewYear ( 2024 ) ;
// → 2024-02-10 (Beijing midnight, expressed as UTC)
// Convert a Gregorian date to a lunar date
const lunar = gregorianToLunar ( new Date ( 2024 , 1 , 10 ) ) ;
// → { year: 2024, month: 1, day: 1, isLeapMonth: false }
// Get all lunar months for a year (12 or 13 months)
const months = getLunarMonthsForYear ( 2023 ) ;
// → 13 months (contains 閏二月)
import { getChineseZodiac } from 'stembranch' ;
// 立春派 (default): year changes at 立春, used in 四柱八字
const a = getChineseZodiac ( new Date ( 2024 , 1 , 10 ) ) ;
// → { animal: '龍', branch: '辰', yearBoundary: 'spring-start', effectiveYear: 2024 }
// 初一派: year changes at Lunar New Year, used in popular culture
const b = getChineseZodiac ( new Date ( 2024 , 1 , 10 ) , 'lunar-new-year' ) ;
import { getTenRelation , getTenRelationForBranch } from 'stembranch' ;
getTenRelation ( '甲' , '庚' ) ; // → '七殺'
getTenRelationForBranch ( '甲' , '子' ) ; // → '正印'
import { getAlmanacFlags , getHeavenlyNoble , getTravelingHorse } from 'stembranch' ;
// All active flags for a date
const flags = getAlmanacFlags ( new Date ( 2024 , 5 , 15 ) ) ;
// → [{ name: '天乙貴人', english: 'Heavenly Noble', auspicious: true, ... }, ...]
// Individual derivations
getHeavenlyNoble ( '甲' ) ; // → ['丑', '未']
getTravelingHorse ( '寅' ) ; // → '申'
import { computeSixRenForDate } from 'stembranch' ;
const chart = computeSixRenForDate ( new Date ( 2024 , 5 , 15 , 14 ) ) ;
// → { plates, lessons, transmissions, method: '賊剋', generals, ... }
One call, everything at once — four pillars, lunar date, solar terms, zodiac, day fitness, flying stars, almanac flags, Six Ren chart, eclipses, and element analysis:
import { dailyAlmanac } from 'stembranch' ;
const almanac = dailyAlmanac ( new Date ( 2024 , 5 , 15 , 14 , 30 ) ) ;
// almanac.pillars → { year: {stem:'甲', branch:'辰'}, month: ..., day: ..., hour: ... }
// almanac.lunar → { year: 2024, month: 5, day: 10, isLeapMonth: false }
// almanac.dayFitness → { fitness: '成', auspicious: true }
// almanac.flyingStars → { year: {...}, month: {...}, day: {...}, hour: {...} }
// almanac.almanacFlags → [{ name: '天乙貴人', english: 'Heavenly Noble', ... }, ...]
// almanac.sixRen → { method: '賊剋', lessons: [...], transmissions: {...}, ... }
// almanac.dayElement → '水'
// almanac.dayStrength → '死'
Year boundary: 立春 (Start of Spring), not January 1
The 干支 year starts at 立春, the moment the sun reaches ecliptic longitude 315°. This falls around February 3-5 each year. A person born on January 20, 2024 belongs to the 癸卯 year (2023's stem-branch), not 甲辰 (2024's).
The library computes the exact 立春 moment using full VSOP87D planetary theory (2,425 terms) with DE405 correction and sxwnl-derived DeltaT.
子時 (Midnight Hour) crosses calendar days
子時 runs from 23:00 to 00:59, crossing the calendar midnight boundary. At 23:00+, the hour branch is 子 and the hour stem uses the next day's stem for the 甲己還加甲 rule. The day pillar itself does not advance until 00:00.
小寒 (Minor Cold) starts 丑月
The 12 month boundaries are defined by 節 (Jie) solar terms. 小寒 (~January 6) starts 丑月, and 立春 (~February 4) starts 寅月. Dates between 小寒 and 立春 belong to 丑月 of the previous stem-branch year.
Cross-validation against 寿星万年历 (sxwnl)
Validated against sxwnl , the gold standard Chinese calendar library by 許劍偉:
Test
Samples
Range
Result
Day Pillar (日柱)
5,683 dates
1583-2500
100% match
Year Pillar (年柱)
2,412 dates
1900-2100
100% match
Month Pillar (月柱)
2,412 dates
1900-2100
100% match
Solar Terms (節氣)
4,824 terms
1900-2100
avg 0.6s deviation
Lunar New Year (農曆)
61 dates
1990-2050
100% match
Intercalary Months (閏月)
10 years
2001-2025
100% match
Lunar calendar validated against Hong Kong Observatory / USNO data, including correct handling of the 二〇三三年問題 (2033 problem).
Solar term timing detail:
Percentile
Deviation
P50
0.5 seconds
P95
1.4 seconds
P99
2.0 seconds
Max
3.1 seconds
Within 1 min
100%
Full analysis with statistical charts: docs/accuracy.md
Component
Source
Method
Solar longitude
VSOP87D
Full 2,425-term planetary theory + DE405 correction
New moon
Meeus Ch. 49
25 periodic + 14 planetary correction terms
Lunar calendar
Computed
冬至-anchor algorithm with zhongqi month numbering
Julian Day
Meeus Ch. 7
Julian/Gregorian calendar conversion
DeltaT (ΔT)
Espenak & Meeus + sxwnl
Polynomial (pre-2016), sxwnl cubic table (2016-2050), parabolic extrapolation (2050+)
Nutation
IAU2000B
77-term lunisolar nutation series
Equation of Time
Spencer 1971 Fourier
Accurate to ~30 seconds
Eclipse dates
NASA Five Millennium Canon
23,962 eclipses (-1999 to 3000 CE)
Day pillar
Arithmetic
Epoch: 2000-01-07 = 甲子日
Stem/branch cycles
Lookup tables
Standard 10-stem, 12-branch sequences
Export
Description
dailyAlmanac(date)
Complete almanac: pillars, lunar date, solar terms, zodiac, day fitness, flying stars, almanac flags, Six Ren, eclipses, element analysis
Export
Description
computeFourPillars(date)
Compute year, month, day, and hour pillars
Export
Description
SOLAR_TERM_NAMES
24 term names (小寒 through 冬至)
SOLAR_TERM_LONGITUDES
Ecliptic longitudes (285° through 270°)
findSolarTermMoment(longitude, year, startMonth?)
Exact UTC moment for a solar longitude
getSolarTermsForYear(year)
All 24 terms with exact dates
findSpringStart(year)
Exact moment of 立春
getSolarMonthExact(date)
Which solar month a date falls in
Export
Description
getLunarMonthsForYear(lunarYear)
All lunar months for a year (12 or 13)
getLunarNewYear(gregorianYear)
Lunar New Year date (正月初一)
gregorianToLunar(date)
Convert Gregorian date to lunar date
Export
Description
ZODIAC_ANIMALS
['鼠','牛','虎','兔','龍','蛇','馬','羊','猴','雞','狗','豬']
ZODIAC_ENGLISH
Record<ChineseZodiacAnimal, string> (鼠→Rat, etc.)
getChineseZodiac(date, boundary?)
Zodiac with configurable year boundary (立春 or 初一)
Export
Description
equationOfTime(date)
EoT in minutes (Spencer 1971)
trueSolarTime(clockTime, longitude, standardMeridian?)
Corrected solar time with breakdown
Export
Description
getWesternZodiac(date)
Sign, symbol, Chinese name, Western element
Export
Description
STEMS
['甲','乙','丙','丁','戊','己','庚','辛','壬','癸']
BRANCHES
['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥']
STEM_ELEMENT
Record<Stem, Element> (甲乙=木, 丙丁=火, ...)
BRANCH_ELEMENT
Record<Branch, Element>
stemByIndex(n)
Get stem by index (mod 10)
branchByIndex(n)
Get branch by index (mod 12)
stemPolarity(stem)
'陽' or '陰'
branchPolarity(branch)
'陽' or '陰'
Export
Description
makeStemBranch(stem, branch)
Build a StemBranch string
stemBranchByCycleIndex(n)
Get pair at position n in the 60-cycle
stemBranchCycleIndex(stem, branch)
Reverse lookup (returns -1 for invalid parity)
parseStemBranch(str)
Parse two-character string into stem + branch
allSixtyStemBranch()
All 60 valid pairs in cycle order
Export
Description
GENERATIVE_CYCLE
金→水→木→火→土→金
CONQUERING_CYCLE
金→木→土→水→火→金
ELEMENT_ORDER
['金','木','水','火','土']
getElementRelation(from, to)
Returns '生', '剋', '被生', '被剋', or '比和'
Export
Description
HIDDEN_STEMS
Record<Branch, HiddenStem[]> — main, middle, residual stems
getHiddenStems(branch)
Hidden stems for a branch (main stem first)
Export
Description
STEM_COMBINATIONS
Five stem combinations with transformed elements
STEM_CLASHES
Four stem clash pairs
isStemCombination(a, b)
Check if two stems form a 五合
isStemClash(a, b)
Check if two stems clash
getCombinedElement(a, b)
Transformed element of a combination, or null
Branch Relations (地支六合/六衝/三合/刑/害/破)
Export
Description
HARMONY_PAIRS
六合: 子丑, 寅亥, 卯戌, 辰酉, 巳申, 午未
CLASH_PAIRS
六衝: 子午, 丑未, 寅申, 卯酉, 辰戌, 巳亥
THREE_HARMONIES
三合: 申子辰水, 寅午戌火, 巳酉丑金, 亥卯未木
SEASONAL_UNIONS
三會: 寅卯辰木, 巳午未火, 申酉戌金, 亥子丑水
HALF_HARMONIES
半合: pairs from three-harmony groups
PUNISHMENT_GROUPS
刑: 寅巳申無恩, 丑戌未恃勢, 子卯無禮
SELF_PUNISHMENT
自刑: 辰午酉亥
HARM_PAIRS
六害: 子未, 丑午, 寅巳, 卯辰, 申亥, 酉戌
DESTRUCTION_PAIRS
六破: 子酉, 丑辰, 寅亥, 卯午, 巳申, 未戌
isThreeHarmony(a, b, c)
Check three-harmony group
isPunishment(a, b)
Check punishment relationship
isSelfPunishment(branch)
Check self-punishment
isHarm(a, b)
Check harm pair
isDestruction(a, b)
Check destruction pair
Export
Description
HIDDEN_HARMONY_PAIRS
Pre-computed pairs where main hidden stems form 五合
isHiddenHarmony(a, b)
Check if two branches have 暗合
Export
Description
EARTH_BRANCHES
['辰','丑','戌','未']
isWetEarth(branch)
辰丑 are wet earth
isDryEarth(branch)
戌未 are dry earth
getStorageElement(branch)
庫/墓: 辰→水, 戌→火, 丑→金, 未→木
Export
Description
TEN_RELATION_NAMES
All 10 relation names
getTenRelation(dayStem, otherStem)
Derive the ten-relation
getTenRelationForBranch(dayStem, branch)
Ten-relation using main hidden stem
Twelve Life Stages (長生十二神)
Export
Description
TWELVE_STAGES
['長生','沐浴','冠帶','臨官','帝旺','衰','病','死','墓','絕','胎','養']
getLifeStage(stem, branch)
Life stage of a stem at a branch
Export
Description
STRENGTH
Record<Strength, string> mapping to moon phase emojis
getStrength(element, monthBranch)
Seasonal strength: 旺, 相, 休, 囚, or 死
Export
Description
CYCLE_ELEMENTS
Full 60-pair lookup table with element and poetic name
getCycleElement(sb)
納音 element for a stem-branch pair
getCycleElementName(sb)
納音 poetic name (e.g. 海中金, 爐中火)
Export
Description
computeVoidBranches(dayStem, dayBranch)
Two void branches for the current decade
Export
Description
DAY_FITNESS_CYCLE
['建','除','滿','平','定','執','破','危','成','收','開','閉']
DAY_FITNESS_AUSPICIOUS
Auspicious/inauspicious classification per fitness value
getDayFitness(dayBranch, monthBranch)
Fitness value from day and month branches
getDayFitnessForDate(date)
Fitness and auspicious flag for a date
Export
Description
FLYING_STARS
Nine stars: 一白 through 九紫 with element and color
getYearStar(date)
Year star (changes at 立春)
getMonthStar(date)
Month star (from year star group + solar month)
getDayStar(date)
Day star (continuous 9-day cycle)
getHourStar(date)
Hour star (from day star group + hour branch)
getFlyingStars(date)
All four stars (year, month, day, hour)
Export
Description
ALMANAC_FLAG_REGISTRY
Full registry of all recognized flags with metadata
getAlmanacFlags(date)
All active flags for a date
getAlmanacFlagsForPillars(pillars)
All active flags from pre-computed pillars
getHeavenlyNoble(stem)
天乙貴人: two noble branches per day stem
getSupremeNoble(stem)
太極貴人: supreme noble branches per stem
getLiteraryStar(stem)
文昌貴人: literary star branch per stem
getProsperityStar(stem)
祿神: prosperity branch per stem
getRamBlade(stem)
羊刃: ram blade branch per stem
getGoldenCarriage(stem)
金輿: golden carriage branch per stem
getTravelingHorse(branch)
驛馬: traveling horse from 三合 group
getPeachBlossom(branch)
桃花: peach blossom from 三合 group
getCanopy(branch)
華蓋: canopy from 三合 group
getGeneralStar(branch)
將星: general star from 三合 group
getRobberyStar(branch)
劫煞: robbery star from 三合 group
getDeathSpirit(branch)
亡神: death spirit from 三合 group
getRedPhoenix(branch)
紅鸞: red phoenix (year branch)
getHeavenlyJoy(branch)
天喜: heavenly joy (紅鸞 + 6)
getLonelyStar(branch)
孤辰: lonely star
getWidowStar(branch)
寡宿: widow star
isCommandingStar(stem, branch)
魁罡: check commanding star day pillar
isTenEvils(stem, branch)
十惡大敗: check ten evils day pillar
isYinYangError(stem, branch)
陰差陽錯: check yin-yang error day pillar
isHeavensPardon(stem, branch, season)
天赦日: check heaven's pardon
isMonthBreak(dayBranch, monthBranch)
月破: day branch clashes month branch
isYearBreak(dayBranch, yearBranch)
歲破: day branch clashes year branch
Export
Description
STEM_LODGING
日干寄宮: stem lodging branches (same as 祿)
HEAVENLY_GENERALS
十二天將 in traditional order
getMonthlyGeneral(date)
月將: shifts at each 中氣 boundary
buildPlates(monthlyGeneral, hourBranch)
Build 天地盤 (heaven/earth plate rotation)
buildFourLessons(dayStem, dayBranch, plates)
Derive 四課 (four lessons)
computeSixRen(dayStem, dayBranch, hourBranch, monthlyGeneral)
Full chart from the four parameters
computeSixRenForDate(date, hour?)
Full chart for a date (auto-derives all inputs)
Export
Description
getAllSolarEclipses()
All solar eclipses (-1999 to 3000 CE), sorted by date
getAllLunarEclipses()
All lunar eclipses (-1999 to 3000 CE), sorted by date
getEclipsesForYear(year)
All eclipses for a given year
getEclipsesInRange(start, end, kind?)
Eclipses in a date range, optionally filtered
findNearestEclipse(date, kind?)
Nearest eclipse to a given date
isEclipseDate(date)
Check if a UTC date has an eclipse
ECLIPSE_DATA_RANGE
{ min: -1999, max: 3000 }
Export
Description
newMoonJDE(k)
JDE of new moon for lunation number k (Meeus Ch. 49)
findNewMoonsInRange(startJD, endJD)
All new moon JDEs in a Julian Day range
Export
Description
julianDayNumber(year, month, day, calendar?)
JD for a calendar date (Julian, Gregorian, or auto)
jdToCalendarDate(jd, calendar?)
Convert JD back to calendar date
julianCalendarToDate(year, month, day)
Convert a Julian calendar date to a JS Date
Export
Description
deltaT(date)
ΔT in seconds for a Date (TT = UT + ΔT)
deltaTForYear(y)
ΔT in seconds for a decimal year
type Stem = '甲' | '乙' | '丙' | '丁' | '戊' | '己' | '庚' | '辛' | '壬' | '癸' ;
type Branch = '子' | '丑' | '寅' | '卯' | '辰' | '巳' | '午' | '未' | '申' | '酉' | '戌' | '亥' ;
type StemBranch = `${Stem } ${Branch } `;
type Element = '金' | '木' | '水' | '火' | '土' ;
type ElementRelation = '生' | '剋' | '被生' | '被剋' | '比和' ;
type Strength = '旺' | '相' | '休' | '囚' | '死' ;
type DayRelation = '生' | '剋' | '合' | '衝' | '比和' ;
type PunishmentType = '無恩' | '恃勢' | '無禮' ;
type EarthType = '濕' | '燥' ;
type TenRelation = '比肩' | '劫財' | '食神' | '傷官' | '偏財' | '正財' | '七殺' | '正官' | '偏印' | '正印' ;
type LifeStage = '長生' | '沐浴' | '冠帶' | '臨官' | '帝旺' | '衰' | '病' | '死' | '墓' | '絕' | '胎' | '養' ;
type DayFitness = '建' | '除' | '滿' | '平' | '定' | '執' | '破' | '危' | '成' | '收' | '開' | '閉' ;
type FlyingStar = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ;
type AlmanacCategory = 'noble' | 'academic' | 'romance' | 'travel' | 'wealth' | 'protection' | 'inauspicious' ;
type TransmissionMethod = '賊剋' | '比用' | '涉害' | '遙剋' | '昴星' | '別責' | '八專' | '返吟' | '伏吟' ;
type HeavenlyGeneral = '貴人' | '螣蛇' | '朱雀' | '六合' | '勾陳' | '青龍' | '天空' | '白虎' | '太常' | '玄武' | '太陰' | '天后' ;
interface HiddenStem { stem : Stem ; proportion : number ; }
interface Pillar { stem : Stem ; branch : Branch ; }
interface FourPillars { year : Pillar ; month : Pillar ; day : Pillar ; hour : Pillar ; }
interface SolarTerm { name : string ; longitude : number ; date : Date ; }
interface FlyingStarInfo { number : FlyingStar ; name : string ; element : Element ; color : string ; }
interface AlmanacFlagInfo { name : string ; english : string ; auspicious : boolean ; category : AlmanacCategory ; }
interface AlmanacFlagResult extends AlmanacFlagInfo { positions : ( 'year' | 'month' | 'day' | 'hour' ) [ ] ; }
interface SixRenLesson { upper : Branch ; lower : Branch ; }
interface SixRenChart { dayStem : Stem ; dayBranch : Branch ; hourBranch : Branch ; monthlyGeneral : Branch ; plates : Record < Branch , Branch > ; lessons : SixRenLesson [ ] ; transmissions : { initial : Branch ; middle : Branch ; final : Branch } ; method : TransmissionMethod ; generals : Record < Branch , HeavenlyGeneral > ; }
type EclipseKind = 'solar' | 'lunar' ;
type SolarEclipseType = 'T' | 'A' | 'P' | 'H' ;
type LunarEclipseType = 'T' | 'P' | 'N' ;
interface Eclipse { date : Date ; kind : EclipseKind ; type : SolarEclipseType | LunarEclipseType ; magnitude : number ; }
type CalendarType = 'julian' | 'gregorian' | 'auto' ;
interface LunarMonth { monthNumber : number ; isLeapMonth : boolean ; startDate : Date ; days : number ; }
interface LunarDate { year : number ; month : number ; day : number ; isLeapMonth : boolean ; }
interface DailyAlmanac { date : Date ; julianDay : number ; lunar : LunarDate ; pillars : FourPillars ; solarTerm : { current : { name : string ; date : Date } | null ; next : { name : string ; date : Date } } ; chineseZodiac : ChineseZodiacResult ; westernZodiac : WesternZodiacResult ; dayFitness : { fitness : DayFitness ; auspicious : boolean } ; flyingStars : { year : FlyingStarInfo ; month : FlyingStarInfo ; day : FlyingStarInfo ; hour : FlyingStarInfo } ; almanacFlags : AlmanacFlagResult [ ] ; sixRen : SixRenChart ; nearestEclipse : Eclipse ; isEclipseDay : boolean ; dayElement : Element ; dayStrength : Strength ; }
stembranch-almanac — daily Chinese almanac showcasing dailyAlmanac() with all modules
iching4d — interactive I Ching explorer with 3D hexagram visualization
MIT