261 lines
9.6 KiB
Python
261 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
ashtottari_dasha.py — Ashtottari Dasha System (108-Year Cycle)
|
|
|
|
Reference: Brihat Parashara Hora Shastra (BPHS), Chapter 19
|
|
|
|
Ashtottari Dasha is a conditional dasha system with a total cycle of 108 years.
|
|
It is applicable only when the Moon is in specific Nakshatras:
|
|
|
|
- Krishna Paksha (waning Moon): Rohini, Ardra, Pushya, Ashlesha, Magha, Revati
|
|
- Shukla Paksha (waxing Moon): Ashwini, Mrigashira, Punarvasu, Chitra, Shravana, Dhanishta
|
|
|
|
The 8 planetary lords and their year allotments:
|
|
1. Sun — 6 years
|
|
2. Moon — 15 years
|
|
3. Mars — 8 years
|
|
4. Mercury — 17 years
|
|
5. Saturn — 10 years
|
|
6. Jupiter — 19 years
|
|
7. Rahu — 12 years
|
|
8. Venus — 21 years
|
|
|
|
Total: 6 + 15 + 8 + 17 + 10 + 19 + 12 + 21 = 108 years.
|
|
|
|
The dasha sequence always runs: Sun → Moon → Mars → Mercury → Saturn → Jupiter → Rahu → Venus.
|
|
The starting lord is determined by the Moon's Nakshatra at birth.
|
|
"""
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Constants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Nakshatra indices (0-based): Ashwini=0, Bharani=1, ..., Revati=26
|
|
NAKSHATRAS = [
|
|
"Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra",
|
|
"Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni",
|
|
"Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha",
|
|
"Anuradha", "Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha",
|
|
"Shravana", "Dhanishta", "Shatabhisha", "Purva Bhadrapada",
|
|
"Uttara Bhadrapada", "Revati"
|
|
]
|
|
|
|
# Applicable Nakshatras for Ashtottari Dasha
|
|
SHUKLA_PAKSHA_NAKSHATRAS = {0, 4, 6, 13, 20, 22} # Ashwini, Mrigashira, Punarvasu, Chitra, Shravana, Dhanishta
|
|
KRISHNA_PAKSHA_NAKSHATRAS = {3, 5, 7, 8, 9, 26} # Rohini, Ardra, Pushya, Ashlesha, Magha, Revati
|
|
|
|
# Ashtottari planetary sequence and year allotments (BPHS Ch.19)
|
|
DASHA_SEQUENCE = [
|
|
{"planet": "Sun", "years": 6},
|
|
{"planet": "Moon", "years": 15},
|
|
{"planet": "Mars", "years": 8},
|
|
{"planet": "Mercury", "years": 17},
|
|
{"planet": "Saturn", "years": 10},
|
|
{"planet": "Jupiter", "years": 19},
|
|
{"planet": "Rahu", "years": 12},
|
|
{"planet": "Venus", "years": 21},
|
|
]
|
|
|
|
TOTAL_CYCLE = 108 # years
|
|
|
|
# Mapping from starting Nakshatra index to first dasha lord index in DASHA_SEQUENCE
|
|
# Per BPHS Ch.19:
|
|
# - Rohini (3), Ardra (5), Pushya (7), Ashlesha (8), Magha (9), Revati (26)
|
|
# → start from Sun (0)
|
|
# - Ashwini (0), Mrigashira (4), Punarvasu (6)
|
|
# → start from Mars (2)
|
|
# - Chitra (13), Shravana (20), Dhanishta (22)
|
|
# → start from Jupiter (5)
|
|
STARTING_LORD_MAP = {
|
|
3: 0, # Rohini -> Sun
|
|
5: 0, # Ardra -> Sun
|
|
7: 0, # Pushya -> Sun
|
|
8: 0, # Ashlesha -> Sun
|
|
9: 0, # Magha -> Sun
|
|
26: 0, # Revati -> Sun
|
|
0: 2, # Ashwini -> Mars
|
|
4: 2, # Mrigashira -> Mars
|
|
6: 2, # Punarvasu -> Mars
|
|
13: 5, # Chitra -> Jupiter
|
|
20: 5, # Shravana -> Jupiter
|
|
22: 5, # Dhanishta -> Jupiter
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Core Functions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def is_ashtottari_applicable(moon_nakshatra_index: int, is_shukla_paksha: bool) -> bool:
|
|
"""
|
|
Check whether Ashtottari Dasha is applicable for the given birth conditions.
|
|
|
|
Args:
|
|
moon_nakshatra_index: 0-based index of Moon's Nakshatra (0 = Ashwini).
|
|
is_shukla_paksha: True if birth is in Shukla Paksha (waxing Moon), else False.
|
|
|
|
Returns:
|
|
bool: True if Ashtottari Dasha applies.
|
|
"""
|
|
if is_shukla_paksha:
|
|
return moon_nakshatra_index in SHUKLA_PAKSHA_NAKSHATRAS
|
|
else:
|
|
return moon_nakshatra_index in KRISHNA_PAKSHA_NAKSHATRAS
|
|
|
|
|
|
def _build_major_periods(start_lord_idx: int, birth_date: datetime) -> list:
|
|
"""
|
|
Build the full list of major periods (Mahadashas) given the starting lord.
|
|
|
|
Returns a list of dicts with keys: planet, years, start_date, end_date.
|
|
"""
|
|
periods = []
|
|
seq_len = len(DASHA_SEQUENCE)
|
|
current_date = birth_date
|
|
|
|
for i in range(seq_len):
|
|
idx = (start_lord_idx + i) % seq_len
|
|
lord = DASHA_SEQUENCE[idx]
|
|
end_date = current_date + timedelta(days=lord["years"] * 365.25)
|
|
periods.append({
|
|
"planet": lord["planet"],
|
|
"years": lord["years"],
|
|
"start_date": current_date.isoformat(),
|
|
"end_date": end_date.isoformat(),
|
|
})
|
|
current_date = end_date
|
|
|
|
return periods
|
|
|
|
|
|
def calculate_ashtottari_dasha(birth_info: dict) -> dict:
|
|
"""
|
|
Calculate Ashtottari Dasha for a native.
|
|
|
|
Args:
|
|
birth_info: dict containing:
|
|
- "moon_nakshatra_index": int (0-26)
|
|
- "is_shukla_paksha": bool
|
|
- "birth_datetime": datetime object or ISO-format string
|
|
|
|
Returns:
|
|
dict with keys:
|
|
- "applicable": bool — whether this dasha system applies
|
|
- "major": list of major period dicts (planet, years, start/end dates)
|
|
- "current": dict — the currently running major period
|
|
- "total_cycle": int — 108
|
|
- "starting_planet": str — first dasha lord
|
|
"""
|
|
moon_idx = birth_info.get("moon_nakshatra_index")
|
|
is_shukla = birth_info.get("is_shukla_paksha", True)
|
|
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()
|
|
|
|
applicable = is_ashtottari_applicable(moon_idx, is_shukla)
|
|
|
|
if not applicable:
|
|
return {
|
|
"applicable": False,
|
|
"major": [],
|
|
"current": None,
|
|
"total_cycle": TOTAL_CYCLE,
|
|
"starting_planet": None,
|
|
"reason": f"Moon Nakshatra '{NAKSHATRAS[moon_idx]}' does not qualify for Ashtottari Dasha under {'Shukla' if is_shukla else 'Krishna'} Paksha.",
|
|
}
|
|
|
|
start_lord_idx = STARTING_LORD_MAP.get(moon_idx, 0)
|
|
starting_planet = DASHA_SEQUENCE[start_lord_idx]["planet"]
|
|
|
|
major_periods = _build_major_periods(start_lord_idx, birth_dt)
|
|
|
|
# Determine current period. Major periods repeat every 108 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 {
|
|
"applicable": True,
|
|
"major": major_periods,
|
|
"current": current_period,
|
|
"total_cycle": TOTAL_CYCLE,
|
|
"starting_planet": starting_planet,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Self-test
|
|
# ---------------------------------------------------------------------------
|
|
|
|
if __name__ == "__main__":
|
|
# Example 1: Applicable case — Moon in Rohini, Krishna Paksha
|
|
test_birth_1 = {
|
|
"moon_nakshatra_index": 3, # Rohini
|
|
"is_shukla_paksha": False, # Krishna Paksha
|
|
"birth_datetime": datetime(1990, 6, 15, 10, 30),
|
|
}
|
|
result_1 = calculate_ashtottari_dasha(test_birth_1)
|
|
print("=" * 60)
|
|
print("Test 1: Moon in Rohini, Krishna Paksha")
|
|
print("=" * 60)
|
|
print(f"Applicable: {result_1['applicable']}")
|
|
print(f"Starting Planet: {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['planet']:10s} | {p['years']:2d} years | {p['start_date'][:10]} → {p['end_date'][:10]}")
|
|
print(f"Current Period: {result_1['current']}")
|
|
print()
|
|
|
|
# Example 2: Applicable case — Moon in Ashwini, Shukla Paksha
|
|
test_birth_2 = {
|
|
"moon_nakshatra_index": 0, # Ashwini
|
|
"is_shukla_paksha": True, # Shukla Paksha
|
|
"birth_datetime": datetime(1985, 3, 20, 8, 0),
|
|
}
|
|
result_2 = calculate_ashtottari_dasha(test_birth_2)
|
|
print("=" * 60)
|
|
print("Test 2: Moon in Ashwini, Shukla Paksha")
|
|
print("=" * 60)
|
|
print(f"Applicable: {result_2['applicable']}")
|
|
print(f"Starting Planet: {result_2['starting_planet']}")
|
|
print(f"Total Cycle: {result_2['total_cycle']} years")
|
|
for p in result_2["major"]:
|
|
print(f" {p['planet']:10s} | {p['years']:2d} years | {p['start_date'][:10]} → {p['end_date'][:10]}")
|
|
print(f"Current Period: {result_2['current']}")
|
|
print()
|
|
|
|
# Example 3: Non-applicable case — Moon in Bharani, Shukla Paksha
|
|
test_birth_3 = {
|
|
"moon_nakshatra_index": 1, # Bharani
|
|
"is_shukla_paksha": True,
|
|
"birth_datetime": datetime(2000, 1, 1, 0, 0),
|
|
}
|
|
result_3 = calculate_ashtottari_dasha(test_birth_3)
|
|
print("=" * 60)
|
|
print("Test 3: Moon in Bharani, Shukla Paksha (Non-applicable)")
|
|
print("=" * 60)
|
|
print(f"Applicable: {result_3['applicable']}")
|
|
print(f"Reason: {result_3['reason']}")
|