A Python toolkit for generating Zi Wei Dou Shu astrolabes.
Project description
izthon
izthon is a pure-Python re-implementation of the TypeScript library iztro (Zi Wei Dou Shu astrolabe generator).
It is implemented as a normal Python package (no runtime dependency on the TypeScript code).
Upstream Reference
izthonfollows the behavior of the original TypeScript projectiztroas a specification reference.- The bundled
lunar_litemodule follows the upstreamlunar-litebehavior used byiztro. - These upstream projects are references only;
izthondoes not depend on them at runtime.
Design Goals
- Pure Python implementation; TS sources are only used as a spec (no runtime dependency on TS).
- Public APIs are Pythonic
snake_case(no TS-style alias names). - Built-in i18n follows stdlib
gettextsemantics, plus an iztro-compatible reverse lookup helperkot(). - Behavior is regression-tested against upstream Jest test cases via pytest.
Requirements
- Python >= 3.12
uv(recommended; this repo uses uv to manage environments and dependencies)
Install / Run
Run From Source (this repo)
uv run python -c "from izthon.astro import by_solar; print(by_solar('2000-8-16', 2, '女').five_elements_class)"
Use As a Dependency (uv)
In another project (example):
# Local editable (recommended for development)
uv add --editable /path/to/izthon
Quick Start
Generate An Astrolabe (Solar Date)
from izthon.astro import by_solar
astrolabe = by_solar("2000-8-16", time_index=2, gender="女")
print(astrolabe.lunar_date) # 二〇〇〇年七月十七
print(astrolabe.chinese_date) # 庚辰 甲申 丙午 庚寅
print(astrolabe.five_elements_class) # 木三局
Generate An Astrolabe (Lunar Date)
from izthon.astro import by_lunar
# lunar_date: "YYYY-M-D", use is_leap_month=True for leap-month dates
astrolabe = by_lunar("2023-2-20", time_index=4, gender="女", is_leap_month=True)
High-level API (izthon.astro)
Entry points:
by_solar(solar_date, time_index, gender, *, fix_leap=True, language="zh-CN", config=None, plate="sky") -> FunctionalAstrolabeby_lunar(lunar_date, time_index, gender, *, is_leap_month=False, fix_leap=True, language="zh-CN", config=None, plate="sky") -> FunctionalAstrolabewith_options(*, date_value, time_index, gender, date_type="solar", is_leap_month=False, fix_leap=True, language="zh-CN", config=None, plate="sky") -> FunctionalAstrolabeplate:"sky"|"earth"|"human"config: per-call config patch (no cross-call leakage)
- Plugins:
load_plugin(plugin)/load_plugins([...])get_plugins()/reset_plugins()astrolabe.use(plugin)
- Low-level astro helpers:
get_soul_and_body(...)get_five_elements_class(...)get_palace_names(...)get_horoscope(...)rearrange_astrolabe(...)
- Helpers:
get_zodiac_by_solar_date(..., language=..., config=...)get_sign_by_solar_date(..., language=..., config=...)get_sign_by_lunar_date(..., is_leap_month=..., language=..., config=...)get_major_star_by_solar_date(..., fix_leap=..., language=..., config=...)get_major_star_by_lunar_date(..., is_leap_month=..., fix_leap=..., language=..., config=...)
Plate Selection (Sky / Earth / Human)
from izthon.astro import by_solar
astrolabe = by_solar(
"1979-08-21",
7,
"male",
plate="earth", # "sky" | "earth" | "human"
config={"algorithm": "zhongzhou"},
)
Unified Entry (with_options)
from izthon.astro import with_options
astrolabe = with_options(
date_value="2023-10-18",
date_type="lunar",
time_index=4,
gender="female",
)
Plugins
from izthon.astro import by_solar, get_plugins, load_plugin, reset_plugins
def add_five_elements(self):
self.five_elements = lambda: self.five_elements_class
load_plugin(add_five_elements)
print(len(get_plugins()))
global_astrolabe = by_solar("2023-10-18", 4, "female")
print(global_astrolabe.five_elements())
reset_plugins()
instance_astrolabe = by_solar("2023-10-18", 4, "female")
instance_astrolabe.use(add_five_elements)
print(instance_astrolabe.five_elements())
Helper APIs (sign/zodiac/major stars)
from izthon.astro import (
get_major_star_by_lunar_date,
get_major_star_by_solar_date,
get_sign_by_lunar_date,
get_sign_by_solar_date,
get_zodiac_by_solar_date,
)
print(get_zodiac_by_solar_date("2023-2-20")) # 兔
print(get_zodiac_by_solar_date("2023-2-20", language="en-US")) # rabbit
print(get_sign_by_solar_date("2023-9-5")) # 处女座
print(get_sign_by_lunar_date("2023-2-3", is_leap_month=True)) # 白羊座 (leap month)
print(get_major_star_by_solar_date("1987-05-16", 7)) # 天机,天梁
print(get_major_star_by_lunar_date("2023-2-17", 0, is_leap_month=True)) # 贪狼
Core Concepts & Objects
All main APIs live in izthon.astro.
FunctionalAstrolabe: the astrolabe object returned byby_solar()/by_lunar().FunctionalPalace: 12 palaces, accessed viaastrolabe.palace(...).FunctionalStar: stars in each palace; accessed viaastrolabe.star(...)orpalace.major_starsetc.FunctionalHoroscope: luck-cycle view for a given target date (astrolabe.horoscope(...)).FunctionalSurroundingPalaces: surrounded palaces (三方四正), accessed viaastrolabe.surrounding_palaces(...).
Query Palaces / Stars
astrolabe = by_solar("2023-8-15", 0, "女")
ming = astrolabe.palace("命宫")
print(ming.name, ming.heavenly_stem, ming.earthly_branch)
print([s.name for s in ming.major_stars])
ziwei = astrolabe.star("紫微")
print(ziwei.palace().name)
surroundings = astrolabe.surrounding_palaces("命宫")
print(surroundings.opposite.name, surroundings.wealth.name, surroundings.career.name)
print(surroundings.has_stars(["武曲", "贪狼"])) # all included?
print(surroundings.lacks_stars(["地空", "地劫"]))
print(surroundings.has_any_star(["太阳", "文曲"])) # any included?
print(surroundings.has_mutagen("禄"))
print(surroundings.lacks_mutagen("忌"))
Palace Helper Methods
palace = astrolabe.palace("命宫")
print(palace.has_stars(["武曲"]))
print(palace.lacks_stars(["火星"]))
print(palace.has_any_star(["武曲", "贪狼"]))
print(palace.has_mutagen("禄"))
print(palace.lacks_mutagen("忌"))
print(palace.is_empty())
print(palace.flies_to("兄弟", "忌"))
print(palace.flies_one_of_to("夫妻", ["权", "科"]))
print(palace.does_not_fly_to("兄弟", "科"))
print(palace.has_self_mutagen("禄"))
print(palace.has_any_self_mutagen())
print(palace.lacks_self_mutagen("忌"))
print([p.name if p else None for p in palace.mutagen_palaces()])
Star Helper Methods
star = astrolabe.star("紫微")
print(star.palace().name)
print(star.opposite_palace().name)
print(star.surrounding_palaces().target.name)
print(star.with_mutagen(["禄", "权"]))
print(star.with_brightness(["庙", "旺"]))
Horoscope (Decadal/Yearly/Monthly/Daily/Hourly)
astrolabe = by_solar("2000-8-16", 2, "女")
horoscope = astrolabe.horoscope("2023-8-19 3:12")
print(horoscope.decadal.index, horoscope.decadal.heavenly_stem, horoscope.decadal.earthly_branch)
print(horoscope.yearly.index, horoscope.yearly.heavenly_stem, horoscope.yearly.earthly_branch)
# Query horoscope stars (decadal + yearly stars on a given palace in a given scope)
print(horoscope.has_horoscope_stars("疾厄", "decadal", ["流陀", "流曲", "运昌"]))
print(horoscope.has_horoscope_mutagen("兄弟", "decadal", "禄"))
Horoscope Helper Methods
print(horoscope.age_palace().name)
print(horoscope.palace("命宫", "yearly").name)
print(horoscope.surrounding_palaces("命宫", "decadal").opposite.name)
print(horoscope.lacks_horoscope_stars("疾厄", "decadal", ["流喜", "流鸾"]))
print(horoscope.has_any_horoscope_star("疾厄", "decadal", ["流陀", "流曲"]))
Low-level Astro Helpers
from izthon.astro import (
get_five_elements_class,
get_horoscope,
get_palace_names,
get_soul_and_body,
)
print(get_palace_names(0))
print(get_five_elements_class("丙", "寅"))
print(get_soul_and_body(solar_date="2023-8-15", time_index=0, fix_leap=True))
decadals, ages = get_horoscope(solar_date="2023-8-15", time_index=0, gender="女", fix_leap=True)
print(decadals[0], ages[0])
# `rearrange_astrolabe(...)` is the low-level primitive behind `plate="earth"` and `plate="human"`.
Parameters & Conventions
time_index (0..12)
time_index is the traditional 12-branch hour index used by iztro.
| time_index | time (zh-CN) | range |
|---|---|---|
| 0 | 早子时 | 00:00~01:00 |
| 1 | 丑时 | 01:00~03:00 |
| 2 | 寅时 | 03:00~05:00 |
| 3 | 卯时 | 05:00~07:00 |
| 4 | 辰时 | 07:00~09:00 |
| 5 | 巳时 | 09:00~11:00 |
| 6 | 午时 | 11:00~13:00 |
| 7 | 未时 | 13:00~15:00 |
| 8 | 申时 | 15:00~17:00 |
| 9 | 酉时 | 17:00~19:00 |
| 10 | 戌时 | 19:00~21:00 |
| 11 | 亥时 | 21:00~23:00 |
| 12 | 晚子时 | 23:00~00:00 |
Helper:
from izthon.util import time_to_index
print(time_to_index(23)) # 12
gender
gender accepts values in any supported language (e.g. "male", "female", "男", "女"), thanks to kot().
Note: astrolabe.gender is localized to the current language (default: zh-CN), so you may see "男"/"女" even if you pass "male"/"female".
from izthon.astro import by_solar
print(by_solar("2000-8-16", 2, "female").gender) # 女 (default zh-CN)
print(by_solar("2000-8-16", 2, "female", language="en-US").gender) # female
Leap Month Fix (fix_leap)
fix_leap=True follows iztro behavior for leap months:
for a leap month, days after the 15th may be treated as the next month for some placements (unless time_index==12).
Global Config
Use set_config() to set defaults for the current context, or pass config=... per call to avoid cross-call state:
from izthon.astro import get_config, reset_config, set_config
set_config(
{
"year_divide": "exact", # "normal" | "exact"
"horoscope_divide": "exact", # "normal" | "exact"
"age_divide": "birthday", # "normal" | "birthday"
"day_divide": "forward", # "current" | "forward"
"algorithm": "default", # "default" | "zhongzhou"
}
)
print(get_config())
reset_config()
You can also customize:
mutagens: map a heavenly stem to 4 stars (禄/权/科/忌)brightness: map a star to 12 brightness values (one per palace index)
Both keys/values can be provided in any supported language.
Multi-language (i18n)
Supported languages:
en-US,ja-JP,ko-KR,zh-CN,zh-TW,vi-VN
Two ways to set language:
- Pass
languagetoby_solar/by_lunar/ helper APIs:
from izthon.astro import by_solar
print(by_solar("2000-8-16", 2, "女", language="vi-VN").chinese_date)
- Set global language:
from izthon.i18n import set_language
set_language("ko-KR")
Language is context-local and can be safely overridden per API call.
Direct i18n Helpers
from izthon.i18n import get_language, kot, set_language, t, translate
set_language("en-US")
print(get_language())
print(t("soulPalace"))
print(translate("bodyPalace"))
print(kot("female"))
Lunar Conversion (lunar_lite)
izthon.lunar_lite is a Python port of lunar-lite (range: 1900..2100).
from izthon.lunar_lite import (
Options,
get_heavenly_stem_and_earthly_branch_by_lunar_date,
get_heavenly_stem_and_earthly_branch_by_solar_date,
get_sign,
get_total_days_of_lunar_month,
get_zodiac,
lunar_to_solar,
solar_to_lunar,
)
print(solar_to_lunar("2023-4-1").to_chinese()) # 二〇二三年闰二月十一
print(lunar_to_solar("2023-2-11", is_leap_month=True).isoformat()) # 2023-4-1
print(get_sign("2023-9-5"))
print(get_zodiac("卯"))
print(get_total_days_of_lunar_month("2023-4-1"))
options = Options(year="exact", month="exact")
print(get_heavenly_stem_and_earthly_branch_by_solar_date("2025-2-3", 12, options).yearly)
print(get_heavenly_stem_and_earthly_branch_by_lunar_date("2023-2-11", 2, True, options).daily)
Data classes exported by izthon.lunar_lite:
LunarDateSolarDateHeavenlyStemAndEarthlyBranchDateOptions
Low-level Modules
izthon.star
- Container:
init_stars() - Location helpers:
get_start_index(),get_lu_yang_tuo_ma_index(),get_kui_yue_index(),get_zuo_you_index(),get_chang_qu_index(),get_daily_star_index(),get_timely_star_index(),get_kong_jie_index(),get_huo_ling_index(),get_luan_xi_index(),get_huagai_xianchi_index(),get_gu_gua_index(),get_yearly_star_index(),get_tianshi_tianshang_index(),get_nianjie_index(),get_monthly_star_index(),get_chang_qu_index_by_heavenly_stem(),get_jiesha_adj_index(),get_dahao_index() - Star builders:
get_major_star(),get_minor_star(),get_adjective_star() - Decorative / cycle helpers:
get_changsheng_12_start_index(),get_jiangqian_12_start_index(),get_changsheng_12(),get_boshi_12(),get_yearly_12(),get_horoscope_star()
izthon.util
- Index helpers:
fix_index(),earthly_branch_index_to_palace_index(),fix_earthly_branch_index(),fix_lunar_month_index(),fix_lunar_day_index(),get_age_index() - Brightness / mutagen helpers:
get_brightness(),get_mutagen(),get_mutagens_by_heavenly_stem() - Misc helpers:
merge_stars(),time_to_index(),translate_chinese_date()
Development
Run tests:
uv run --extra dev pytest
Generate i18n catalogs (maintainer only; requires the upstream iztro locale sources):
uv run python tools/generate_iztro_i18n_catalogs.py --src-locales /path/to/iztro/src/i18n/locales
Attribution
This project re-implements (as a spec) the behavior of:
iztro@2.5.7lunar-lite@0.2.8
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file izthon-0.2.0.tar.gz.
File metadata
- Download URL: izthon-0.2.0.tar.gz
- Upload date:
- Size: 46.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55f32d812ac68d28decbd2718337f890739a4bc2351f4506ea5a3be89cedd819
|
|
| MD5 |
b8096745dda978338ca95929f157340e
|
|
| BLAKE2b-256 |
198b51cc50ac5a0d5f2d086f48b7a9b3ee51493f535008af29c22685c7be00a5
|
File details
Details for the file izthon-0.2.0-py3-none-any.whl.
File metadata
- Download URL: izthon-0.2.0-py3-none-any.whl
- Upload date:
- Size: 65.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
890407173d2e75151055254d493750cc0cd387eed0e4d87ed881d89b042f47a2
|
|
| MD5 |
0bdf66c21bd946bb105df6c9ea845b8c
|
|
| BLAKE2b-256 |
4311d9141d6bd8ee98bcd1d7a0cb2e80ad86abd0fa926fd7b2ae7c86941020d5
|