- Dharmagya
- panchang docs
- Dashas & Transits
Dashas & Transits
panchang · v5.4.0 · MIT
Vimshottari (3-level), Ashtottari, Yogini, Chara, and Narayan dashas, plus the daily transits — Chandra Balam, Tarabala, and Sade Sati.
Five classical dasha systems
All five run off a birth moment. computeVimshottariDashaFromBirth works out the Moon's longitude for you. computeVimshottariDasha, computeAshtottariDasha and computeYoginiDasha take a sidereal Moon longitude you already have, from getSiderealMoonLongitude or from a chart. The two Jaimini systems take a location instead, because they start from the lagna.
A Moon longitude outside [0, 360) is wrapped into it first, so 360 reads as 0 and -0.5 as 359.5. NaN or an infinity throws a PanchangError with code INVALID_INPUT. Both are new in 5.4. For such values TypeScript used to return periods with no lord or the wrong one, or fail with a raw TypeError. In Go, ComputeAshtottariDasha and ComputeYoginiDasha returned ErrInvalidInput for anything outside the range, and ComputeVimshottariDasha panicked, except that on some processors a NaN got a wrong dasha back with no error.
import {
getSiderealMoonLongitude, computeVimshottariDashaFromBirth, computeVimshottariPratyantarIn,
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha,
} from 'panchang-ts';
const birth = new Date('1995-08-15T05:30:00Z');
const loc = { latitude: 28.6139, longitude: 77.2090 };
const asOf = new Date('2011-05-03T00:00:00Z');
const moonLon = getSiderealMoonLongitude(birth, 'lahiri');
// 1. Vimshottari — 120-year, 9-lord, Maha -> Antar -> Pratyantar.
const vim = computeVimshottariDashaFromBirth(birth, 'lahiri', asOf);
console.log(vim.currentMahaDashaLord, vim.currentIndex); // Venus 2
const maha = vim.mahaDashas[0]!;
const antar = maha.antarDashas[0]!; // running at birth
const pra = computeVimshottariPratyantarIn(maha, antar);
console.log(antar.lord, pra.length, pra[0]!.lord); // Rahu 6 Mercury
// 2. Ashtottari — 108-year, 8-lord cycle (no Ketu).
const ash = computeAshtottariDasha(birth, moonLon, asOf);
console.log(ash.mahaDashas[0]!.lord, ash.mahaDashas.length); // Rahu 8
// 3. Yogini — 36-year, 8 yoginis.
const yog = computeYoginiDasha(birth, moonLon, asOf);
console.log(yog.mahaDashas[0]!.yogini, yog.mahaDashas[0]!.lord); // Ulka Saturn
// 4. Chara (Jaimini) — sign-based, 9-8-7 years, forward only.
const cha = computeCharaDasha(birth, loc, 'lahiri', asOf);
console.log(cha.mahaDashas[0]!.rashi, cha.mahaDashas[0]!.years); // 6 9
// 5. Narayan (Jaimini) — direction from lagna parity.
const nar = computeNarayanDasha(birth, loc, 'lahiri', { asOfDate: asOf });
console.log(nar.direction, nar.startingRashi); // forward 6
// Narayan variable-duration variant (Sanjay Rath).
const narV = computeNarayanDasha(birth, loc, 'lahiri', { duration: 'variable', asOfDate: asOf });
console.log(narV.mahaDashas[0]!.years, narV.mahaDashas[1]!.years); // 9 5package main
import (
"fmt"
"time"
"github.com/ishankgupta95/panchang/source/go/v5/panchang"
"github.com/ishankgupta95/panchang/source/go/v5/types"
)
// must unwraps (value, error) and panics on error. Reused by the examples below.
func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
func main() {
s := panchang.New()
birth := time.Date(1995, 8, 15, 5, 30, 0, 0, time.UTC)
loc := types.GeoLocation{Latitude: 28.6139, Longitude: 77.2090}
asOf := time.Date(2011, 5, 3, 0, 0, 0, 0, time.UTC)
moonLon := must(s.GetSiderealMoonLongitude(birth, panchang.Lahiri))
// 1. Vimshottari — 120-year, 9-lord, Maha -> Antar -> Pratyantar.
vim := must(s.ComputeVimshottariDashaFromBirth(birth, panchang.Lahiri, asOf))
fmt.Println(vim.CurrentMahaDashaLord, vim.CurrentIndex) // Venus 2
maha := vim.MahaDashas[0]
antar := maha.AntarDashas[0] // running at birth
pra := must(panchang.ComputeVimshottariPratyantarIn(maha, antar))
fmt.Println(antar.Lord, len(pra), pra[0].Lord) // Rahu 6 Mercury
// 2. Ashtottari — 108-year, 8-lord cycle (no Ketu).
ash := must(panchang.ComputeAshtottariDasha(birth, moonLon, asOf))
fmt.Println(ash.MahaDashas[0].Lord, len(ash.MahaDashas)) // Rahu 8
// 3. Yogini — 36-year, 8 yoginis.
yog := must(panchang.ComputeYoginiDasha(birth, moonLon, asOf))
fmt.Println(yog.MahaDashas[0].Yogini, yog.MahaDashas[0].Lord) // Ulka Saturn
// 4. Chara (Jaimini) — sign-based, 9-8-7 years, forward only.
cha := must(s.ComputeCharaDasha(birth, loc, panchang.Lahiri, asOf))
fmt.Println(cha.MahaDashas[0].Rashi, cha.MahaDashas[0].Years) // 6 9
// 5. Narayan (Jaimini) — direction from lagna parity.
nar := must(s.ComputeNarayanDasha(birth, loc, panchang.Lahiri, asOf))
fmt.Println(nar.Direction, nar.StartingRashi) // forward 6
// Narayan variable-duration variant (Sanjay Rath) is its own method in Go.
narV := must(s.ComputeNarayanDashaVariable(birth, loc, panchang.Lahiri, asOf))
fmt.Println(narV.MahaDashas[0].Years, narV.MahaDashas[1].Years) // 9 5
}| System | Cycle | Result type |
|---|---|---|
| Vimshottari | 120 years, 9 lords, Maha → Antar → Pratyantar | VimshottariDashaResult |
| Ashtottari | 108 years, 8 lords (no Ketu) | VimshottariDashaResult |
| Yogini | 36 years, 8 yoginis with planet lords | YoginiDashaResult |
| Chara (Jaimini) | sign-based, 9-8-7 years per modality | CharaDashaResult |
| Narayan (Jaimini) | sign-based, direction by lagna parity; fixed or variable years | NarayanDashaResult |
Narayan runs forward for a vishama-pada lagna (Aries, Taurus, Gemini, Libra, Scorpio, Sagittarius) and backward for a sama-pada one (Cancer, Leo, Virgo, Capricorn, Aquarius, Pisces). Read the direction off nar.direction. In the variable-duration variant each maha dasha runs 0 to 12 years, counted from the sign to its lord, plus one year if the lord is exalted and minus one if it is debilitated. From 5.2 a sign no longer aspects its immediate neighbours under rasi drishti, which moved some of those durations.
A sign whose lord sits in it gets the full 12 years, counting round the whole zodiac. Before 5.4 it got 0, a period of no length (1 for Mercury in Kanya, where it is also exalted), and every later period started that many years early. For a birth at Pune on 15 May 1990 at 06:30 UTC, Makara holds Saturn and now runs 12 years, from 2037 to 2049.
Chara, by contrast, is a fixed scheme: movable signs run 9 years, fixed 8 and dual 7, always forward, and the chart only picks the starting sign. It is not the Chara dasha of K. N. Rao or P. V. R. Narasimha Rao, whose years come from each sign's lord and whose direction depends on the chart. The variable Narayan dasha is the one that counts to the lord.
The order and year constants (ASHTOTTARI_ORDER, ASHTOTTARI_YEARS, YOGINI_ORDER, CHARA_RASHI_YEARS, …) are exported so you can build your own UI from them. See the export list.
Antardashas and pratyantars
Every mahadasha carries its antardashas in antarDashas. The first mahadasha starts at birth with only the balance of its years left, and its antardashas are those of the full mahadasha: the ones already over at birth are dropped, and the one running at birth is clipped to start there. Vimshottari already worked this way, and since 5.4 Ashtottari and Yogini do too. Before, they squeezed all eight antardashas into the balance, starting with the mahadasha's own lord.
Every list of sub-periods ends exactly at its parent's end, to the millisecond. Before 5.4 the last one could end up to 8 ms before or after it.
import {
getSiderealMoonLongitude, computeVimshottariDashaFromBirth, computeYoginiDasha,
computeVimshottariPratyantar, computeVimshottariPratyantarIn,
} from 'panchang-ts';
const birth = new Date('1995-08-15T05:30:00Z');
const asOf = new Date('2011-05-03T00:00:00Z');
const moonLon = getSiderealMoonLongitude(birth, 'lahiri');
// Yogini: four of Ulka's eight antardashas are left at birth.
const yog = computeYoginiDasha(birth, moonLon, asOf);
console.log(yog.mahaDashas[0]!.antarDashas.map(a => a.yogini).join(' '));
// Pingala Dhanya Bhramari Bhadrika
// Vimshottari: the birth antardasha is Rahu, clipped to start at birth.
const vim = computeVimshottariDashaFromBirth(birth, 'lahiri', asOf);
const maha = vim.mahaDashas[0]!;
const antar = maha.antarDashas[0]!;
// The pratyantars of the whole Rahu antardasha, from the one running at birth.
const pra = computeVimshottariPratyantarIn(maha, antar);
console.log(pra.map(p => p.lord).join(' ')); // Mercury Ketu Venus Sun Moon Mars
console.log(pra[0]!.endDate.toISOString()); // 1995-09-25T00:15:07.079Z
// The plain call spreads all nine over the clipped span, from Rahu.
const squeezed = computeVimshottariPratyantar(antar);
console.log(squeezed.length, squeezed[0]!.lord); // 9 Rahu
// Any later antardasha is whole, and the two calls agree.
const next = maha.antarDashas[1]!;
console.log(JSON.stringify(computeVimshottariPratyantar(next)) ===
JSON.stringify(computeVimshottariPratyantarIn(maha, next))); // true// Inside func main(); must() is the helper from the first example.
// This one also imports "slices".
s := panchang.New()
birth := time.Date(1995, 8, 15, 5, 30, 0, 0, time.UTC)
asOf := time.Date(2011, 5, 3, 0, 0, 0, 0, time.UTC)
moonLon := must(s.GetSiderealMoonLongitude(birth, panchang.Lahiri))
// Yogini: four of Ulka's eight antardashas are left at birth.
yog := must(panchang.ComputeYoginiDasha(birth, moonLon, asOf))
var yoginis []types.YoginiName
for _, a := range yog.MahaDashas[0].AntarDashas {
yoginis = append(yoginis, a.Yogini)
}
fmt.Println(yoginis) // [Pingala Dhanya Bhramari Bhadrika]
// Vimshottari: the birth antardasha is Rahu, clipped to start at birth.
vim := must(s.ComputeVimshottariDashaFromBirth(birth, panchang.Lahiri, asOf))
maha := vim.MahaDashas[0]
antar := maha.AntarDashas[0]
// The pratyantars of the whole Rahu antardasha, from the one running at birth.
pra := must(panchang.ComputeVimshottariPratyantarIn(maha, antar))
var lords []types.DashaLord
for _, p := range pra {
lords = append(lords, p.Lord)
}
fmt.Println(lords) // [Mercury Ketu Venus Sun Moon Mars]
fmt.Println(pra[0].EndDate.ISOString()) // 1995-09-25T00:15:07.079Z
// The plain call spreads all nine over the clipped span, from Rahu.
squeezed := must(panchang.ComputeVimshottariPratyantar(antar))
fmt.Println(len(squeezed), squeezed[0].Lord) // 9 Rahu
// Any later antardasha is whole, and the two calls agree.
next := maha.AntarDashas[1]
a := must(panchang.ComputeVimshottariPratyantar(next))
b := must(panchang.ComputeVimshottariPratyantarIn(maha, next))
fmt.Println(slices.Equal(a, b)) // truecomputeVimshottariPratyantar splits whatever span it is given as if it were a whole antardasha, starting with the antardasha's own lord. That is right for every antardasha except the one running at birth, which is clipped. computeVimshottariPratyantarIn, new in 5.4, takes the mahadasha as well and reads only its lord. It rebuilds the antardasha's full length from the two lords, ending where the antardasha ends, and drops the pratyantars over before it starts. So the birth antardasha's list begins with the pratyantar running at birth and can hold fewer than nine. For an antardasha that was not clipped it returns the same list as the plain call, so when you have the mahadasha to hand it is the one to use.
Both throw a PanchangError with code INVALID_INPUT for an unknown lord (the plain call threw a bare Error before 5.4) and INVALID_DATE for an Invalid Date. In Go both are package functions, panchang.ComputeVimshottariPratyantar and panchang.ComputeVimshottariPratyantarIn, and an unknown lord is ErrInvalidInput.
Pinning the evaluation date
Which dasha is running depends on when you ask. Six entry points take an asOfDate, added in 5.2, so you can ask what was running on 3 May 2011 and get the same answer every time. Five take it as a trailing argument; Narayan takes it in the options bag.
import {
getSiderealMoonLongitude, computeVimshottariDasha, computeVimshottariDashaFromBirth,
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha,
computeSadeSati,
} from 'panchang-ts';
const birth = new Date('1995-08-15T05:30:00Z');
const loc = { latitude: 28.6139, longitude: 77.2090 };
const asOf = new Date('2011-05-03T00:00:00Z');
const moonLon = getSiderealMoonLongitude(birth, 'lahiri');
// Five take it as a trailing positional argument.
console.log(computeVimshottariDasha(birth, moonLon, asOf).currentMahaDashaLord); // Venus
console.log(computeVimshottariDashaFromBirth(birth, 'lahiri', asOf).currentMahaDashaLord); // Venus
console.log(computeAshtottariDasha(birth, moonLon, asOf).currentMahaDashaLord); // Venus
console.log(computeYoginiDasha(birth, moonLon, asOf).currentYogini); // Sankata
console.log(computeCharaDasha(birth, loc, 'lahiri', asOf).currentRashi); // 7
// Narayan takes it in the options bag — both overloads.
console.log(computeNarayanDasha(birth, loc, 'lahiri', { asOfDate: asOf }).currentRashi); // 7
console.log(computeNarayanDasha(birth, loc, 'lahiri',
{ duration: 'variable', asOfDate: asOf }).currentRashi); // 8
// Sade Sati already had it in 5.1.1.
console.log(computeSadeSati(0, asOf).active); // false
// A different pin gives a different answer.
console.log(computeVimshottariDashaFromBirth(birth, 'lahiri',
new Date('2000-01-01T00:00:00Z')).currentMahaDashaLord); // Mercury
// Omit it and the call resolves to new Date(). For this birth that
// prints Venus until 1 October 2028, then Sun.
console.log(computeVimshottariDashaFromBirth(birth, 'lahiri').currentMahaDashaLord);// Inside func main(); must() is the helper from the first example.
s := panchang.New()
birth := time.Date(1995, 8, 15, 5, 30, 0, 0, time.UTC)
loc := types.GeoLocation{Latitude: 28.6139, Longitude: 77.2090}
asOf := time.Date(2011, 5, 3, 0, 0, 0, 0, time.UTC)
moonLon := must(s.GetSiderealMoonLongitude(birth, panchang.Lahiri))
// All seven take asOf as a trailing time.Time argument.
fmt.Println(must(panchang.ComputeVimshottariDasha(birth, moonLon, asOf)).CurrentMahaDashaLord)
fmt.Println(must(s.ComputeVimshottariDashaFromBirth(birth, panchang.Lahiri, asOf)).CurrentMahaDashaLord)
fmt.Println(must(panchang.ComputeAshtottariDasha(birth, moonLon, asOf)).CurrentMahaDashaLord)
fmt.Println(must(panchang.ComputeYoginiDasha(birth, moonLon, asOf)).CurrentYogini)
fmt.Println(must(s.ComputeCharaDasha(birth, loc, panchang.Lahiri, asOf)).CurrentRashi)
fmt.Println(must(s.ComputeNarayanDasha(birth, loc, panchang.Lahiri, asOf)).CurrentRashi)
fmt.Println(must(s.ComputeNarayanDashaVariable(birth, loc, panchang.Lahiri, asOf)).CurrentRashi)
fmt.Println(must(s.ComputeSadeSati(0, asOf, panchang.Lahiri)).Active)
// Prints, one per line: Venus Venus Venus Sankata 7 7 8 false
// A different pin gives a different answer.
y2000 := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(must(s.ComputeVimshottariDashaFromBirth(birth, panchang.Lahiri, y2000)).CurrentMahaDashaLord)
// Mercury
// The zero time.Time means "now" — the Go stand-in for omitting asOfDate.
fmt.Println(must(s.ComputeVimshottariDashaFromBirth(birth, panchang.Lahiri, time.Time{})).CurrentMahaDashaLord)In TypeScript, omit it and the call uses new Date(), so code written before 5.2 behaves as it did. Pass a non-Date or an invalid Date and you get a PanchangError with code INVALID_DATE and the message Invalid asOfDate: …. See Errors & Compatibility.
Go has no optional arguments, so you always pass the date and the zero time.Time is what means now. A time.Time cannot be invalid, so the Invalid asOfDate error has no Go counterpart. A date outside 1900 to 2100 still reports INVALID_DATE, as in TypeScript: a birth, or the asOf of ComputeSadeSati, which is the date it computes Saturn for. The variable-duration Narayan dasha is a separate method, ComputeNarayanDashaVariable, and ComputeSadeSati takes the ayanamsa as a required third argument.
computeVimshottariPratyantar and computeVimshottariPratyantarIn are the exception in both languages. They subdivide an AntarDasha you already have, so they take no date. See Antardashas and pratyantars.
Daily transits — Chandra Balam and Tarabala
Pass janmaRashi and janmaNakshatra to a daily call and the result carries Chandra Balam and Tarabala for that day (see Daily Panchang). Leave them out and both fields are empty. computeChandraBalam and computeTarabala do the same job from a natal index and a transit index.
import { getDailyPanchang, computeChandraBalam, computeTarabala } from 'panchang-ts';
const date = new Date('2026-09-02T00:00:00Z');
const loc = { latitude: 18.5204, longitude: 73.8567 };
const r = getDailyPanchang(date, loc, {
timezone: 330,
janmaRashi: 3, // 0 = Mesha … 11 = Meena
janmaNakshatra: 0, // 0 = Ashwini … 26 = Revati
})!;
// Both fields are null unless the janma indices were supplied.
console.log(r.chandraBalam!.house, r.chandraBalam!.quality, r.chandraBalam!.name); // 10 strong Shubha
console.log(r.tarabala!.taraIndex, r.tarabala!.name, r.tarabala!.quality); // 1 Sampat auspicious
// Or compute them standalone from transit indices.
const cb = computeChandraBalam(3, r.moon.rashi.index);
const tb = computeTarabala(0, r.angas.nakshatras[0].index);
console.log(cb.house, cb.quality); // 10 strong
console.log(tb.taraIndex, tb.name); // 1 Sampat// Inside func main(); this one also imports "log".
s := panchang.New()
date := time.Date(2026, 9, 2, 0, 0, 0, 0, time.UTC)
loc := types.GeoLocation{Latitude: 18.5204, Longitude: 73.8567}
janmaRashi, janmaNakshatra := 3, 0 // 0 = Mesha .. 11 = Meena / 0 = Ashwini .. 26 = Revati
r, ok, err := s.GetDailyPanchang(date, loc, types.PanchangOptions{
Timezone: panchang.OffsetMinutes(330),
InstantPanchangOptions: types.InstantPanchangOptions{
JanmaRashi: &janmaRashi,
JanmaNakshatra: &janmaNakshatra,
},
})
if err != nil {
log.Fatal(err)
}
if !ok {
log.Fatal("no panchang for that day")
}
// Both fields are nil pointers unless the janma indices were supplied.
fmt.Println(r.ChandraBalam.House, r.ChandraBalam.Quality, r.ChandraBalam.Name) // 10 strong Shubha
fmt.Println(r.Tarabala.TaraIndex, r.Tarabala.Name, r.Tarabala.Quality) // 1 Sampat auspicious
// Or compute them standalone from transit indices.
cb, _ := panchang.ComputeChandraBalam(3, r.Moon.Rashi.Index, panchang.English)
tb, _ := panchang.ComputeTarabala(0, r.Angas.Nakshatras[0].Index, panchang.English)
fmt.Println(cb.House, cb.Quality) // 10 strong
fmt.Println(tb.TaraIndex, tb.Name) // 1 SampatChandra Balam counts the transit Moon's house from the janma rashi, where 1 is the janma rashi itself. Houses 1, 3, 6, 7, 10 and 11 are Shubha. Tarabala is the nine-tara cycle from the janma nakshatra: Janma, Sampat, Vipat, Kshema, Pratyari, Sadhaka, Vadha, Mitra, Ati-Mitra.
Sade Sati
computeSadeSati tells you where Saturn is in its seven-and-a-half-year arc over the natal Moon sign. Phase 1 is Saturn in the 12th from the Moon, phase 2 on the Moon, phase 3 in the 2nd.
import { computeSadeSati } from 'panchang-ts';
const asOf = new Date('2026-09-02T00:00:00Z');
// natalMoonRashi 0 = Mesha. Phase and the arc dates are null when inactive.
const ss = computeSadeSati(0, asOf);
console.log(ss.active, ss.phase); // true 1
console.log(ss.currentArcStart!.toISOString()); // 2025-03-30T00:00:00.000Z
console.log(ss.currentArcEnd!.toISOString()); // 2032-05-31T00:00:00.000Z
console.log(ss.nextArcStart); // null
const off = computeSadeSati(3, asOf);
console.log(off.active, off.phase === null, off.nextArcStart!.toISOString());
// false true 2032-05-31T06:00:00.000Z
// A retrograde return into the arc counts as an entry.
const kumbha = computeSadeSati(10, new Date('2027-08-01T00:00:00Z'));
console.log(kumbha.active, kumbha.nextArcStart!.toISOString());
// false 2027-10-20T12:00:00.000Z// Same preamble as the first example, plus a nil-safe date printer:
func iso(d *types.JSDate) string {
if d == nil {
return "null"
}
return d.ISOString()
}
// ... then, inside func main():
s := panchang.New()
asOf := time.Date(2026, 9, 2, 0, 0, 0, 0, time.UTC)
// natalMoonRashi 0 = Mesha. Phase and the arc dates are nil when inactive.
ss := must(s.ComputeSadeSati(0, asOf, panchang.Lahiri))
fmt.Println(ss.Active, *ss.Phase) // true 1
fmt.Println(iso(ss.CurrentArcStart)) // 2025-03-30T00:00:00.000Z
fmt.Println(iso(ss.CurrentArcEnd)) // 2032-05-31T00:00:00.000Z
fmt.Println(iso(ss.NextArcStart)) // null
off := must(s.ComputeSadeSati(3, asOf, panchang.Lahiri))
fmt.Println(off.Active, off.Phase == nil, iso(off.NextArcStart)) // false true 2032-05-31T06:00:00.000Z
// A retrograde return into the arc counts as an entry.
kumbha := must(s.ComputeSadeSati(10, time.Date(2027, 8, 1, 0, 0, 0, 0, time.UTC), panchang.Lahiri))
fmt.Println(kumbha.Active, iso(kumbha.NextArcStart)) // false 2027-10-20T12:00:00.000ZWhen no arc is running, phase, currentArcStart and currentArcEnd are empty and nextArcStart gives you the next start: Saturn's next entry into any of the three signs, found to within a day, or empty when there is none within 30 years. A retrograde return counts as an entry. On 1 August 2027 Saturn has moved on into Mesha, out of a Kumbha Moon's arc, and it retrogrades back into Meena on 20 October. Before 5.4 the search skipped a return like that and could land about 21 years late: here it gave 7 March 2049.
nextArcStart comes from a search of its own, so it can differ by hours from the currentArcStart a query made inside the arc reports. Asked on 22 October 2027, the same Kumbha arc starts at 2027-10-21T00:00:00.000Z. Arc boundaries land within one or two days of authoritative ephemerides; see Accuracy.
