Files
2026-06-07 20:16:47 +08:00

219 lines
8.2 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
yogini_dasha.py — Yogini Dasha System (36-Year Cycle)
The Yogini Dasha is a Nakshatra-based dasha system with a total cycle of 36 years.
It consists of 8 Yoginis (female planetary deities), each ruling a specific number
of years. The sequence is fixed and cycles continuously.
The 8 Yoginis, their year allotments, and associated planets:
Order | Yogini | Years | Planet
------|-----------|-------|--------
1 | Maya | 1 | Sun
2 | Bhadrika | 2 | Moon
3 | Jaya | 3 | Mars
4 | Rati | 4 | Mercury
5 | Mangala | 5 | Jupiter
6 | Pingala | 6 | Saturn
7 | Dhanya | 7 | Venus
8 | Bhramari | 8 | Rahu
Total: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36 years.
Starting Yogini:
The starting Yogini is determined by the Ascendant (Lagna) Rashi.
Per standard texts:
- Aries / Leo / Sagittarius / Gemini / Libra / Aquarius
→ start from Maya (1)
- Taurus / Virgo / Capricorn / Cancer / Scorpio / Pisces
→ start from Bhadrika (2)
Alternative (more common) rule based on Lagna degree modulo 8:
Lagna Nakshatra Pada determines the starting Yogini.
For simplicity, this implementation uses the Ascendant Rashi rule above.
Reference: Various classical and modern Jyotish texts on Yogini Dasha.
"""
from datetime import datetime, timedelta
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
# 12 Rashis (Zodiac signs)
RASHIS = [
"Aries", "Taurus", "Gemini", "Cancer",
"Leo", "Virgo", "Libra", "Scorpio",
"Sagittarius", "Capricorn", "Aquarius", "Pisces"
]
# Yogini sequence: fixed order, never changes
YOGINI_SEQUENCE = [
{"name": "Maya", "years": 1, "planet": "Sun"},
{"name": "Bhadrika", "years": 2, "planet": "Moon"},
{"name": "Jaya", "years": 3, "planet": "Mars"},
{"name": "Rati", "years": 4, "planet": "Mercury"},
{"name": "Mangala", "years": 5, "planet": "Jupiter"},
{"name": "Pingala", "years": 6, "planet": "Saturn"},
{"name": "Dhanya", "years": 7, "planet": "Venus"},
{"name": "Bhramari", "years": 8, "planet": "Rahu"},
]
TOTAL_CYCLE = 36 # years
# Lagna Rashi to starting Yogini index mapping
# Odd Rashis (0,4,8,2,6,10) → start from Maya (0)
# Even Rashis (1,5,9,3,7,11) → start from Bhadrika (1)
def _get_start_yogini_index(lagna_rashi_index: int) -> int:
"""Return starting Yogini index based on Ascendant Rashi."""
if lagna_rashi_index % 2 == 0:
return 0 # Maya
else:
return 1 # Bhadrika
# ---------------------------------------------------------------------------
# Core Functions
# ---------------------------------------------------------------------------
def _build_major_periods(start_idx: int, birth_date: datetime) -> list:
"""
Build the full list of major Yogini periods.
Returns a list of dicts with keys: yogini, planet, years, start_date, end_date.
"""
periods = []
seq_len = len(YOGINI_SEQUENCE)
current_date = birth_date
for i in range(seq_len):
idx = (start_idx + i) % seq_len
yogini = YOGINI_SEQUENCE[idx]
end_date = current_date + timedelta(days=yogini["years"] * 365.25)
periods.append({
"yogini": yogini["name"],
"planet": yogini["planet"],
"years": yogini["years"],
"start_date": current_date.isoformat(),
"end_date": end_date.isoformat(),
})
current_date = end_date
return periods
def calculate_yogini_dasha(birth_info: dict) -> dict:
"""
Calculate Yogini Dasha for a native.
Args:
birth_info: dict containing:
- "lagna_rashi_index": int (0 = Aries, ..., 11 = Pisces)
- "birth_datetime": datetime object or ISO-format string (optional)
Returns:
dict with keys:
- "major": list of major period dicts
- "current": dict — the currently running major period
- "total_cycle": int — 36
- "starting_yogini": str — first Yogini name
- "starting_planet": str — planet of first Yogini
"""
lagna_idx = birth_info.get("lagna_rashi_index", 0)
birth_dt = birth_info.get("birth_datetime")
if isinstance(birth_dt, str):
birth_dt = datetime.fromisoformat(birth_dt)
if birth_dt is None:
birth_dt = datetime.now()
start_idx = _get_start_yogini_index(lagna_idx)
starting_yogini = YOGINI_SEQUENCE[start_idx]["name"]
starting_planet = YOGINI_SEQUENCE[start_idx]["planet"]
major_periods = _build_major_periods(start_idx, birth_dt)
# Determine current period. Yogini repeats every 36 years; older natives
# should still return a current period instead of None after the first cycle.
now = datetime.now()
current_period = None
age_years = max((now - birth_dt).days / 365.25, 0)
current_in_cycle = age_years % TOTAL_CYCLE
cumulative = 0.0
for p in major_periods:
years = p["years"]
if cumulative <= current_in_cycle < cumulative + years:
cycle_start = birth_dt + timedelta(days=(age_years - current_in_cycle + cumulative) * 365.25)
cycle_end = cycle_start + timedelta(days=years * 365.25)
current_period = p.copy()
current_period["start_date"] = cycle_start.isoformat()
current_period["end_date"] = cycle_end.isoformat()
current_period["elapsed_years"] = (now - cycle_start).days / 365.25
current_period["remaining_years"] = (cycle_end - now).days / 365.25
current_period["cycle_number"] = int(age_years // TOTAL_CYCLE) + 1
break
cumulative += years
return {
"major": major_periods,
"current": current_period,
"total_cycle": TOTAL_CYCLE,
"starting_yogini": starting_yogini,
"starting_planet": starting_planet,
}
# ---------------------------------------------------------------------------
# Self-test
# ---------------------------------------------------------------------------
if __name__ == "__main__":
# Example 1: Ascendant in Aries (odd sign) → starts from Maya
test_birth_1 = {
"lagna_rashi_index": 0, # Aries
"birth_datetime": datetime(1990, 6, 15, 10, 30),
}
result_1 = calculate_yogini_dasha(test_birth_1)
print("=" * 60)
print("Test 1: Ascendant in Aries (odd sign)")
print("=" * 60)
print(f"Starting Yogini : {result_1['starting_yogini']} ({result_1['starting_planet']})")
print(f"Total Cycle : {result_1['total_cycle']} years")
print("Major Periods:")
for p in result_1["major"]:
print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]}{p['end_date'][:10]}")
print(f"Current Period : {result_1['current']}")
print()
# Example 2: Ascendant in Taurus (even sign) → starts from Bhadrika
test_birth_2 = {
"lagna_rashi_index": 1, # Taurus
"birth_datetime": datetime(1985, 3, 20, 8, 0),
}
result_2 = calculate_yogini_dasha(test_birth_2)
print("=" * 60)
print("Test 2: Ascendant in Taurus (even sign)")
print("=" * 60)
print(f"Starting Yogini : {result_2['starting_yogini']} ({result_2['starting_planet']})")
print(f"Total Cycle : {result_2['total_cycle']} years")
for p in result_2["major"]:
print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]}{p['end_date'][:10]}")
print(f"Current Period : {result_2['current']}")
print()
# Example 3: Ascendant in Scorpio (even sign)
test_birth_3 = {
"lagna_rashi_index": 7, # Scorpio
"birth_datetime": datetime(2000, 1, 1, 0, 0),
}
result_3 = calculate_yogini_dasha(test_birth_3)
print("=" * 60)
print("Test 3: Ascendant in Scorpio (even sign)")
print("=" * 60)
print(f"Starting Yogini : {result_3['starting_yogini']} ({result_3['starting_planet']})")
for p in result_3["major"]:
print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]}{p['end_date'][:10]}")