Skip to main content

Generate Chinese metaphysical flowing pillars — 流年 (yearly), 流月 (monthly), 流日 (daily), 流時 (hourly) — for both 八字 (BaZi / Four Pillars of Destiny) and 紫微斗數 (Zi Wei Dou Shu / Purple Star Astrology), including 四化 (Four Transformations) and 流曜 (Flowing Stars).

Project description

flowpillars 流柱

A Python package for generating Chinese metaphysical flowing pillars — annual (流年), monthly (流月), daily (流日), and hourly (流時) — using both the 八字 (BaZi) and 紫微斗數 (Purple Star Astrology) systems.


Introduction

In Chinese metaphysics, time is divided into pillars (柱) using the sexagenary 干支 (Ganzhi) cycle — a 60-combination cycle formed from the ten 天干 (Heavenly Stems) and twelve 地支 (Earthly Branches).

Heavenly Stems 甲 乙 丙 丁 戊 己 庚 辛 壬 癸
Earthly Branches 子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥

A complete "四柱" (Four Pillars) consists of:

Pillar 八字 rule 紫微斗數 (Purple Star) rule
年柱 (Year) Solar year, changes at 立春 (~4 Feb) Lunar year, changes at 農曆正月初一 (Chinese New Year)
月柱 (Month) Based on solar terms (節氣) Calculated via 五虎遁年起月法 from the lunar year stem
日柱 (Day) Sexagenary day cycle Identical to 八字
時柱 (Hour) 2-hour 時辰 periods Identical to 八字

立春 vs 農曆新年

This distinction is the main difference between the two systems' year pillars:

  • 八字 uses the solar term 立春 (Start of Spring, around 4 February) as the New Year boundary. A date in January or early February is still counted as the previous year.
  • 紫微斗數 (Purple Star Astrology) uses 農曆正月初一 (Chinese New Year, the first day of the first lunar month, typically between 21 January and 20 February) as the boundary.

For example, 1 January 2024 falls before both boundaries, so both systems give 癸卯 for that date. In a year where Chinese New Year falls before 立春, dates between those two events will have different year pillars in each system.

四化 (Four Transformations)

Purple Star Astrology includes 四化 (Four Transformations). This package calculates all four transformations for each Purple Star pillar. Each transformation star is determined by the Heavenly Stem of the pillar, according to the standard table:

Stem 化祿 (Hua Lu) 化權 (Hua Quan) 化科 (Hua Ke) 化忌 (Hua Ji)
廉貞 破軍 武曲 太陽
天機 天梁 紫微 太陰
天同 天機 文昌 廉貞
太陰 天同 天機 巨門
貪狼 太陰 右弼 天機
武曲 貪狼 天梁 文曲
太陽 武曲 太陰 天同
巨門 太陽 文曲 文昌
天梁 紫微 左輔 武曲
破軍 巨門 太陰 貪狼

BaZi pillars do not include 四化.

流曜 (Flowing Stars)

For each Purple Star pillar this package also calculates six 流曜 (Flowing Stars), expressed as the 地支 (Earthly Branch) of their palace position. Three are derived from the pillar's 天干 (Heavenly Stem) and three from its 地支 (Earthly Branch):

Star Chinese Derived from Description
lusun 祿存 Stem Prosperity-preservation star
qingyang 擎羊 Stem One branch after 祿存
tuoluo 陀羅 Stem One branch before 祿存
tianma 天馬 Branch Travelling horse star
wenchang 文昌 Branch Literary star (counts down from 戌)
wenqu 文曲 Branch Literary star (counts up from 辰)

BaZi pillars do not include 流曜.

Precision Levels

flowpillars supports four levels of input precision:

Level Input Example Pillars Generated
YEAR "2023" Annual (流年) only
MONTH "2023-06" Annual + monthly (流年 + 流月)
DAY "2023-06-15" Annual + monthly + daily (流年 + 流月 + 流日)
HOUR "2023-06-15T12" Annual + monthly + daily + hourly (流年 + 流月 + 流日 + 流時)

Pillars that cannot be calculated at the given precision are null in JSON output and empty strings in CSV output.


Installation

# Create and activate a virtual environment
python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

# Install the package and its dependencies
pip install -e ".[dev]"

Dependencies

Package Version Purpose
lunar-python ≥1.3.12 Chinese lunar calendar and 八字 (EightChar) calculations. Complete offline support for solar/lunar conversion, solar terms (節氣), and sexagenary day cycles.
click ≥8.1 CLI framework providing argument parsing, help text generation, and a clean interface for the flowpillars command.
pytz ≥2023.3 Timezone handling and UTC offset parsing for date inputs.
python-dateutil ≥2.8 Date arithmetic — specifically relativedelta for correct month and year increments (e.g. adding 1 month to 31 January).

CLI Usage

The package installs a flowpillars command.

General Options

Options:
  --system [bazi|purple-star|both]          Pillar system(s) to calculate. (default: both)
  --date TEXT                               Single date/datetime string.
  --dates TEXT                              Comma-separated list of date strings.
  --date-file PATH                          File with one date per line.
  --start TEXT                              Start date for range generation.
  --end TEXT                                End date for range generation (inclusive).
  --plus TEXT                               Duration to add to start (e.g. 5y, 3m, 10d, 24h).
  --minus TEXT                              Duration to subtract from start.
  --calendar [solar|lunar]                  Default calendar for input. (default: solar)
  --timezone TEXT                           Timezone name, e.g. Asia/Hong_Kong.
  --format [csv|json|plaintext]             Output format. If omitted, plaintext is printed to stdout.
  --output-dir TEXT                         Directory for output files. (default: .)
  --output-prefix TEXT                      Filename prefix for output files. (default: pillars)
  --help                                    Show this message and exit.

Single Date

# Day precision — prints plaintext to stdout
flowpillars --date "2023-06-15"

# Hour precision
flowpillars --date "2023-06-15T12"

# With timezone
flowpillars --date "2023-06-15T12" --timezone "Asia/Hong_Kong"

# With UTC offset inline
flowpillars --date "2023-06-15T12+08:00"

# Lunar calendar input (prefix with L:)
flowpillars --date "L:2023-04-12"

# BaZi system only, JSON output
flowpillars --date "2023-06-15" --system bazi --format json

# Purple Star Astrology only (includes 化忌)
flowpillars --date "2023-06-15" --system purple-star --format json

Range Modes

# Yearly range 2020–2024 (inclusive)
flowpillars --start "2020" --end "2024"

# Monthly range: January through June 2023
flowpillars --start "2023-01" --plus 5m

# Daily range: 6 days back from 2023-06-15
flowpillars --start "2023-06-15" --minus 6d

# Hourly range: 8 hours starting at 08:00
flowpillars --start "2023-06-15T08" --plus 8h

Duration strings: Ny (years), Nm (months), Nd (days), Nh (hours).

List Mode

# Comma-separated dates (mixed precisions supported)
flowpillars --dates "2020,2021,2023-06,2024-03-15"

# From a file (one date per line; lines starting with # are ignored)
flowpillars --date-file my_dates.txt

Output

# Write plaintext to a file (instead of stdout)
flowpillars --date "2023-06-15T12" --format plaintext --output-dir ./results --output-prefix june_2023
# Produces: ./results/june_2023.txt

# Write CSV or JSON to a file
flowpillars --date "2023-06-15" --format csv --output-dir ./results --output-prefix june_2023
# Produces: ./results/june_2023.csv

Python API

from flowpillars import PillarGenerator, PillarSystem, InputDateTime, Precision, CalendarInput

# Single date — HOUR precision, both systems
gen = PillarGenerator(system=PillarSystem.BOTH)
idt = InputDateTime(year=2023, month=6, day=15, hour=12)
result = gen.generate(idt)

print(result.bazi.year.pillar)              # e.g. "癸卯"
print(result.purple_star.month.pillar)      # e.g. "丁巳"
print(result.purple_star.year.hualu)        # e.g. "破軍" (化祿 star)
print(result.purple_star.year.huaquan)      # e.g. "巨門" (化權 star)
print(result.purple_star.year.huake)        # e.g. "太陰" (化科 star)
print(result.purple_star.year.huaji)        # e.g. "貪狼" (化忌 star)
print(result.purple_star.year.liuyao.lusun) # e.g. "子"   (祿存 palace)
print(result.lunar.month)                   # e.g. 4

# Parse from a string
from flowpillars.input_handler import parse_datetime
idt = parse_datetime("2023-06-15T12+08:00")

# Generate a range
from flowpillars.input_handler import parse_datetime, generate_range
start = parse_datetime("2023-01")
inputs = generate_range(start, plus="11m")   # 12 monthly entries
results = gen.generate_many(inputs)

# Write output
from flowpillars.output import write_csv, write_json, write_plaintext
write_csv(results, "output.csv")
write_json(results, "output.json", system="both")
write_plaintext(results, "output.txt")

PillarResult fields

result.solar_year      # int
result.solar_month     # int or None
result.solar_day       # int or None
result.solar_hour      # int or None
result.timezone        # str or None
result.lunar.year      # int
result.lunar.month     # int (negative = leap month)
result.lunar.day       # int
result.lunar.is_leap_month  # bool

result.bazi.year         # GanZhiPillar or None
result.bazi.year.pillar  # "癸卯"
result.bazi.year.stem    # "癸"
result.bazi.year.branch  # "卯"
result.bazi.year.hualu   # None  (BaZi does not use 四化)
result.bazi.year.huaquan # None
result.bazi.year.huake   # None
result.bazi.year.huaji   # None
result.bazi.year.liuyao  # None  (BaZi does not use 流曜)
# .month, .day, .hour follow the same pattern

result.purple_star.year            # GanZhiPillar or None
result.purple_star.year.pillar     # "癸卯"
result.purple_star.year.stem       # "癸"
result.purple_star.year.branch     # "卯"
result.purple_star.year.hualu      # "破軍"  (化祿 star for 癸 stem)
result.purple_star.year.huaquan    # "巨門"  (化權 star for 癸 stem)
result.purple_star.year.huake      # "太陰"  (化科 star for 癸 stem)
result.purple_star.year.huaji      # "貪狼"  (化忌 star for 癸 stem)
result.purple_star.year.liuyao.lusun    # "子"   (祿存 palace — 癸 stem → 子)
result.purple_star.year.liuyao.qingyang # "丑"   (擎羊 palace)
result.purple_star.year.liuyao.tuoluo   # "亥"   (陀羅 palace)
result.purple_star.year.liuyao.tianma   # "巳"   (天馬 palace — 卯 branch → 巳)
result.purple_star.year.liuyao.wenchang # "未"   (文昌 palace)
result.purple_star.year.liuyao.wenqu    # "未"   (文曲 palace)

CSV Output Format

The CSV file uses UTF-8-BOM encoding (Excel-compatible) with the following columns:

Column Description
input_date Original input string (e.g. 2023-06-15T12 or L:2023-04-12)
precision year, month, day, or hour
calendar solar or lunar
solar_year Solar year number
solar_month Solar month (empty if YEAR precision)
solar_day Solar day (empty if precision < DAY)
solar_hour Solar hour 0–23 (empty if precision < HOUR)
timezone Timezone string, e.g. Asia/Hong_Kong (empty if none)
lunar_year Lunar year number
lunar_month Lunar month (negative = leap month)
lunar_day Lunar day
bazi_year_pillar BaZi year 干支, e.g. 癸卯
bazi_year_stem BaZi year 天干, e.g.
bazi_year_branch BaZi year 地支, e.g.
bazi_month_pillar BaZi month 干支 (empty if precision < MONTH)
bazi_month_stem BaZi month 天干
bazi_month_branch BaZi month 地支
bazi_day_pillar BaZi day 干支 (empty if precision < DAY)
bazi_day_stem BaZi day 天干
bazi_day_branch BaZi day 地支
bazi_hour_pillar BaZi hour 干支 (empty if precision < HOUR)
bazi_hour_stem BaZi hour 天干
bazi_hour_branch BaZi hour 地支
purple_star_year_pillar Purple Star year 干支
purple_star_year_stem Purple Star year 天干
purple_star_year_branch Purple Star year 地支
purple_star_year_hualu Purple Star year 化祿 star
purple_star_year_huaquan Purple Star year 化權 star
purple_star_year_huake Purple Star year 化科 star
purple_star_year_huaji Purple Star year 化忌 star
purple_star_year_liuyao_lusun Purple Star year 祿存 palace (地支)
purple_star_year_liuyao_qingyang Purple Star year 擎羊 palace
purple_star_year_liuyao_tuoluo Purple Star year 陀羅 palace
purple_star_year_liuyao_tianma Purple Star year 天馬 palace
purple_star_year_liuyao_wenchang Purple Star year 文昌 palace
purple_star_year_liuyao_wenqu Purple Star year 文曲 palace
purple_star_month_pillar Purple Star month 干支 (empty if precision < MONTH)
purple_star_month_stem Purple Star month 天干
purple_star_month_branch Purple Star month 地支
purple_star_month_hualu Purple Star month 化祿 star
purple_star_month_huaquan Purple Star month 化權 star
purple_star_month_huake Purple Star month 化科 star
purple_star_month_huaji Purple Star month 化忌 star
purple_star_month_liuyao_lusun Purple Star month 祿存 palace
purple_star_month_liuyao_qingyang Purple Star month 擎羊 palace
purple_star_month_liuyao_tuoluo Purple Star month 陀羅 palace
purple_star_month_liuyao_tianma Purple Star month 天馬 palace
purple_star_month_liuyao_wenchang Purple Star month 文昌 palace
purple_star_month_liuyao_wenqu Purple Star month 文曲 palace
purple_star_day_pillar Purple Star day 干支 (empty if precision < DAY)
purple_star_day_stem Purple Star day 天干
purple_star_day_branch Purple Star day 地支
purple_star_day_hualu Purple Star day 化祿 star
purple_star_day_huaquan Purple Star day 化權 star
purple_star_day_huake Purple Star day 化科 star
purple_star_day_huaji Purple Star day 化忌 star
purple_star_day_liuyao_lusun Purple Star day 祿存 palace
purple_star_day_liuyao_qingyang Purple Star day 擎羊 palace
purple_star_day_liuyao_tuoluo Purple Star day 陀羅 palace
purple_star_day_liuyao_tianma Purple Star day 天馬 palace
purple_star_day_liuyao_wenchang Purple Star day 文昌 palace
purple_star_day_liuyao_wenqu Purple Star day 文曲 palace
purple_star_hour_pillar Purple Star hour 干支 (empty if precision < HOUR)
purple_star_hour_stem Purple Star hour 天干
purple_star_hour_branch Purple Star hour 地支
purple_star_hour_hualu Purple Star hour 化祿 star
purple_star_hour_huaquan Purple Star hour 化權 star
purple_star_hour_huake Purple Star hour 化科 star
purple_star_hour_huaji Purple Star hour 化忌 star
purple_star_hour_liuyao_lusun Purple Star hour 祿存 palace
purple_star_hour_liuyao_qingyang Purple Star hour 擎羊 palace
purple_star_hour_liuyao_tuoluo Purple Star hour 陀羅 palace
purple_star_hour_liuyao_tianma Purple Star hour 天馬 palace
purple_star_hour_liuyao_wenchang Purple Star hour 文昌 palace
purple_star_hour_liuyao_wenqu Purple Star hour 文曲 palace

When --system bazi is used, all purple_star_* columns are present but empty, and vice versa.


JSON Output Schema

{
  "generated_at": "2023-10-01T08:00:00+00:00",
  "system": "both",
  "entries": [
    {
      "input_date": "2023-06-15T12",
      "precision": "hour",
      "calendar_input": "solar",
      "solar": {
        "year": 2023,
        "month": 6,
        "day": 15,
        "hour": 12,
        "timezone": null
      },
      "lunar": {
        "year": 2023,
        "month": 4,
        "day": 28,
        "is_leap_month": false
      },
      "bazi": {
        "year":  { "pillar": "癸卯", "stem": "癸", "branch": "卯" },
        "month": { "pillar": "戊午", "stem": "戊", "branch": "午" },
        "day":   { "pillar": "甲辰", "stem": "甲", "branch": "辰" },
        "hour":  { "pillar": "庚午", "stem": "庚", "branch": "午" }
      },
      "purple_star": {
        "year": {
          "pillar": "癸卯", "stem": "癸", "branch": "卯",
          "hualu": "破軍", "huaquan": "巨門", "huake": "太陰", "huaji": "貪狼",
          "liuyao": { "lusun": "子", "qingyang": "丑", "tuoluo": "亥", "tianma": "巳", "wenchang": "未", "wenqu": "未" }
        },
        "month": {
          "pillar": "丁巳", "stem": "丁", "branch": "巳",
          "hualu": "太陰", "huaquan": "天同", "huake": "天機", "huaji": "巨門",
          "liuyao": { "lusun": "午", "qingyang": "未", "tuoluo": "巳", "tianma": "亥", "wenchang": "巳", "wenqu": "酉" }
        },
        "day": {
          "pillar": "甲辰", "stem": "甲", "branch": "辰",
          "hualu": "廉貞", "huaquan": "破軍", "huake": "武曲", "huaji": "太陽",
          "liuyao": { "lusun": "寅", "qingyang": "卯", "tuoluo": "丑", "tianma": "寅", "wenchang": "午", "wenqu": "申" }
        },
        "hour": {
          "pillar": "庚午", "stem": "庚", "branch": "午",
          "hualu": "太陽", "huaquan": "武曲", "huake": "太陰", "huaji": "天同",
          "liuyao": { "lusun": "申", "qingyang": "酉", "tuoluo": "未", "tianma": "申", "wenchang": "辰", "wenqu": "戌" }
        }
      }
    }
  ]
}

Notes:

  • BaZi pillar objects contain only pillar, stem, and branch — no 四化 or liuyao.
  • Purple Star pillar objects always contain hualu, huaquan, huake, huaji, and liuyao when the pillar is non-null.
  • Null values appear when a pillar is not available at the given precision.

Plaintext Output Format

The plaintext format (.txt) is written entirely in Traditional Chinese, making it immediately readable by practitioners. The === header line is kept in mixed notation so that automated tools can reliably locate entry boundaries. Multiple entries are separated by ---, which renders as a horizontal rule in Markdown-aware chat apps.

Delimiter conventions

Symbol Meaning
(full-width colon) Separates every label from its value
Maps a transformation / star name to its target
(ideographic comma) Separates items within the same group
(two spaces) Separates independent key-value pairs on one line

Example output

=== 2023-06-15T12 | 陽曆 | 流時 ===
陽曆:2023-06-15  時:12
農曆:2023-04-28

八字:
  年柱:癸卯  天干:癸  地支:卯
  月柱:戊午  天干:戊  地支:午
  日柱:甲辰  天干:甲  地支:辰
  時柱:庚午  天干:庚  地支:午

紫微斗數:
  年柱:癸卯  天干:癸  地支:卯
    四化:化祿→破軍、化權→巨門、化科→太陰、化忌→貪狼
    流曜:祿存→子、擎羊→丑、陀羅→亥、天馬→巳、文昌→未、文曲→未
  月柱:丁巳  天干:丁  地支:巳
    四化:化祿→太陰、化權→天同、化科→天機、化忌→巨門
    流曜:祿存→午、擎羊→未、陀羅→巳、天馬→亥、文昌→巳、文曲→酉
  日柱:甲辰  天干:甲  地支:辰
    四化:化祿→廉貞、化權→破軍、化科→武曲、化忌→太陽
    流曜:祿存→寅、擎羊→卯、陀羅→丑、天馬→寅、文昌→午、文曲→申
  時柱:庚午  天干:庚  地支:午
    四化:化祿→太陽、化權→武曲、化科→太陰、化忌→天同
    流曜:祿存→申、擎羊→酉、陀羅→未、天馬→申、文昌→辰、文曲→戌

Notes:

  • Header precision uses the standard flowing-pillar terms: annual (流年), monthly (流月), daily (流日), hourly (流時).
  • Solar calendar is labelled 陽曆; lunar calendar input is labelled 農曆 in the header.
  • Only pillars available at the given precision are printed; lower-precision pillars are omitted.
  • 八字 pillars show 天干 and 地支 only — no 四化 or 流曜.
  • 紫微斗數 pillars include 四化 and 流曜 on separate indented lines.
  • When only one system is active (--system bazi or --system purple-star), the other section is omitted.
  • Lunar input dates are prefixed with L: in the header.
  • Leap months are marked (閏月) in the 農曆: line.
  • The file is UTF-8 encoded (no BOM).

To write from Python:

from flowpillars.output import write_plaintext
write_plaintext(results, "output.txt")

Notes on BaZi vs Purple Star Pillar Differences

Year Pillar

System Changes at Example: 1 Jan 2024 Example: 10 Feb 2024
八字 立春 (~4 Feb) 癸卯 甲辰
紫微斗數 農曆正月初一 癸卯 甲辰

In 2024 these happen to agree because 立春 (4 Feb) preceded Chinese New Year (10 Feb). In years where they differ, dates between the two events will show different year pillars.

Month Pillar

八字 derives the month pillar from solar terms (節氣). The boundary between months falls precisely at the moment the Sun enters a new 15° arc of the ecliptic, calculated by lunar-python.

紫微斗數 (Purple Star) uses the 五虎遁年起月法 formula:

  1. Look up the year stem index, compute year_stem_index % 5
  2. Look up the base stem for 寅月 (lunar month 1): MONTH_STEM_BASES = [丙, 戊, 庚, 壬, 甲]
  3. Month N stem: (base_stem_index + N - 1) % 10
  4. Month N branch: (N + 1) % 12 (寅月→寅, 卯月→卯, …, 子月→子, 丑月→丑)

This means the Purple Star month pillar is derived from the lunar month number, not solar terms, and will generally differ from the BaZi month pillar.

Day and Hour Pillars

Day and hour pillars are identical in both systems. The day pillar follows the unbroken sexagenary day cycle tracked since ancient times. The hour pillar is determined by the 五鼠遁日起時法 formula, which lunar-python handles internally.


Running Tests

pytest tests/ -v

Reference output files are generated in tests/test_outputs/ during the test run. These files are gitignored (only the .gitkeep marker is committed).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flowpillars-0.1.1.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flowpillars-0.1.1-py3-none-any.whl (49.2 kB view details)

Uploaded Python 3

File details

Details for the file flowpillars-0.1.1.tar.gz.

File metadata

  • Download URL: flowpillars-0.1.1.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for flowpillars-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9b15d89107f216990163a2fbdf85de451ea35b4f4697a7b3c7777cf18a40ba30
MD5 f821f08fcef9b42da85fc750e8f9bf34
BLAKE2b-256 1587296c555e4e17064a5b8fba4b0bd02416fde13d0dd4ddc9b5e629c3ec037e

See more details on using hashes here.

File details

Details for the file flowpillars-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: flowpillars-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 49.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for flowpillars-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 52efac1aceef24fc7b174329146b479acfe8390d091b9f28ff9b9b29ccef1acf
MD5 d94263b03c16f98ead3da37e28b96839
BLAKE2b-256 a424d64b6c6805e1d05e860f049381ec1af688357737b17aa22773cd87b052c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page