Skip to content
Documentation · all sections

Eclipses & Moon Phases

panchang-ts · v5.0.0 · MIT

Solar and lunar eclipse detection with sutak windows, upcoming-eclipse search, precise lunar phase instants, and their engine-free table readers.

Eclipse on the daily result

const r = getDailyPanchang(date, loc, { timezone: 330 })!;
if (r.eclipse) {
  r.eclipse.kind;                 // 'solar' | 'lunar'
  r.eclipse.subtype;              // 'partial' | 'total' | 'annular' | 'penumbral'
  r.eclipse.obscuration;          // 0..1 fraction of the disc AREA covered
  r.eclipse.magnitude;            // catalogue magnitude — DIAMETER fraction;
                                  // >1 when total, negative when penumbral
  r.eclipse.visibleFromLocation;  // body above horizon at peak?
  r.eclipse.start; r.eclipse.peak; r.eclipse.end;
  r.eclipse.sutakStart; r.eclipse.sutakEnd;
  // Sutak: 12 h (4 prahara) before solar, 9 h (3 prahara) before lunar
}
import {
  getUpcomingSolarEclipse, getUpcomingLunarEclipse,
  getUpcomingEclipses, computeEclipsesInRange, computeEclipsesForYear,
} from 'panchang-ts';

const next = getUpcomingSolarEclipse(new Date(), loc, 365 /* days */);
getUpcomingEclipses(new Date(), loc, 5);                        // next N
computeEclipsesForYear(2027, loc, { timezone: 330 });           // one year
computeEclipsesInRange(start, end, loc, { timezone: 330 });     // arbitrary span

These return EclipseInfo-shaped results (the search functions add optional *Local fields), so a value from them is assignable wherever r.eclipse is.

Eclipses table — build your own and cache it

Same pattern as the festivals table: build with buildEclipsesTable, persist the JSON, read it back through the engine-free panchang-ts/eclipses entry point. No table is bundled — which eclipses are visible, and therefore which carry sutak, is location-dependent.

import { buildEclipsesTable } from 'panchang-ts';            // uses the engine
import {
  readEclipsesForYear,
  readEclipsesForDate,
  readEclipsesYearRange,
} from 'panchang-ts/eclipses';                                // engine-free

const table = buildEclipsesTable({
  location: { latitude: 25.3176, longitude: 82.9739 },   // Varanasi
  timezoneOffsetMinutes: 330,
  startYear: 2024,
  endYear: 2031,
  languages: ['en', 'hi'],
  // visibleOnly: false → also include eclipses below the horizon (no sutak)
});
// …persist `table` as JSON, then:

readEclipsesYearRange(table);            // { start: 2024, end: 2031 }
const e = readEclipsesForYear(table, 2025)![0].eclipses[0];
e.kind;                 // 'lunar'
e.subtype;              // 'total'
e.start; e.peak; e.end; // ISO UTC strings
e.obscuration;          // 0..1 disc area covered at peak
e.magnitude;            // catalogue magnitude (diameter); >1 total, <0 penumbral
e.visibleFromLocation;  // visible during any phase?
e.visibleAtPeak;        // is greatest eclipse itself above the horizon?
e.sutak;                // { start, end } — see note below
readEclipsesForDate(table, '2025-09-07', 'hi');  // [पूर्ण चंद्र ग्रहण]

By default a table lists every eclipse visible from the location during any phase (so one already in progress at moon/sunrise or moon/sunset is included); visibleAtPeak tells you whether greatest eclipse itself is observable. Solar eclipses report the subtype seen locally — a globally-total eclipse may read partial from a given place.

Moon phases

The four principal lunar phases — new (Amavasya), first quarter, full (Purnima), last quarter — as precise instants. These are the astronomical quarter moments, distinct from the same-named tithis, which are ~24 h windows.

import { computeMoonPhasesInRange, computeMoonPhasesForYear } from 'panchang-ts';

const phases = computeMoonPhasesInRange(new Date('2026-01-01'), new Date('2026-12-31'));
phases.forEach(p => console.log(p.phase, p.time.toISOString()));  // ~49 / year

computeMoonPhasesForYear(2027, { timezone: 330 });

Moon-phases table

Same pattern again, at panchang-ts/moon-phases. Phases are global instants, so buildMoonPhasesTable takes only a timezoneOffsetMinutes (no coordinates) — the timezone just decides which calendar date each instant lands on (a new moon at 19:52 UTC on Jan 18 is listed under Jan 19 in IST).

import { buildMoonPhasesTable } from 'panchang-ts';          // uses the engine
import {
  readMoonPhasesForYear,
  readMoonPhasesForDate,
  readMoonPhasesYearRange,
} from 'panchang-ts/moon-phases';                             // engine-free

const table = buildMoonPhasesTable({
  timezoneOffsetMinutes: 330,    // IST; -300 = US Eastern
  startYear: 2024,
  endYear: 2031,
  languages: ['en', 'hi'],
});
// …persist `table` as JSON, then:

readMoonPhasesYearRange(table);                        // { start: 2024, end: 2031 }
readMoonPhasesForYear(table, 2026)!.length;            // ~49 phase days
readMoonPhasesForDate(table, '2026-01-03');            // [{ phase: 'full', name: 'Full Moon', … }]
readMoonPhasesForDate(table, '2026-01-03', 'hi');      // [{ phase: 'full', name: 'पूर्णिमा', … }]

Each entry carries phase, the phase time (ISO UTC), and en + hi text. The library repo's npm run eclipses:gen and npm run moon-phases:gen scripts are worked examples of the whole build-and-persist pattern.