better-suncalc
Version:
A tiny TypeScript library for calculating sun/moon positions and phases.
89 lines • 3.77 kB
JavaScript
/* equinox.ts */
import { J2000 } from "./constants";
import { sunCoords } from "./suncalc";
import { fromJulian, toDays } from "./utils";
/**
* For a given day (expressed in days since J2000), returns the sun's declination (in radians).
* At the equinox, we want to find when this value is zero.
*
* @param d - Days since the J2000 epoch.
* @returns Sun's declination in radians.
*/
function equinoxFunction(d) {
return sunCoords(d).dec;
}
/**
* Refines an approximate equinox time (given in days since J2000) using Newton–Raphson iteration.
* It seeks the solution to f(d)=0, where f(d) is the sun’s declination.
*
* @param guess - The initial guess (in days since J2000).
* @returns A refined value of d (days since J2000) when the declination is near zero.
*/
function refineEquinox(guess) {
var d = guess;
var delta = 0.001; // step in days (~86.4 sec)
var eps = 1e-8; // convergence tolerance in days
for (var i = 0; i < 20; i++) {
var f0 = equinoxFunction(d);
// Finite-difference estimate of the derivative: f'(d)
var fPrime = (equinoxFunction(d + delta) - equinoxFunction(d - delta)) / (2 * delta);
if (Math.abs(fPrime) < 1e-14)
break; // safeguard against division by near-zero
var dNew = d - f0 / fPrime;
if (Math.abs(dNew - d) < eps) {
return dNew;
}
d = dNew;
}
return d;
}
/**
* Provides approximate guesses for the vernal (spring) and autumnal (fall) equinoxes
* of a given calendar year. Note: In JavaScript Dates, months are 0-indexed:
* - March (month 2) for the vernal equinox (around March 20)
* - September (month 8) for the autumnal equinox (around September 22)
*
* @param year - The calendar year.
* @returns An object with 'vernal' and 'autumnal' as approximate days since J2000.
*/
function approximateEquinoxForYear(year) {
var vernalDate = new Date(Date.UTC(year, 2, 20, 0, 0, 0)); // March 20 (approx.)
var autumnalDate = new Date(Date.UTC(year, 8, 22, 0, 0, 0)); // September 22 (approx.)
return {
vernal: toDays(vernalDate),
autumnal: toDays(autumnalDate),
};
}
/**
* Finds all equinox events (vernal and autumnal) occurring within the given date range.
* The algorithm uses an approximate guess per year then refines the guess via Newton’s method.
*
* The returned object contains arrays of Date objects for the vernal and autumnal equinoxes.
*
* @param rangeStart - Starting Date of the search range.
* @param rangeEnd - Ending Date of the search range.
* @returns Object with keys 'vernal' and 'autumnal', each an array of Date instances.
*/
export function getEquinoxes(rangeStart, rangeEnd) {
var equinoxes = { vernal: [], autumnal: [] };
// Extend the search range by one year on each side in case events occur near the boundaries.
var startYear = rangeStart.getUTCFullYear();
var endYear = rangeEnd.getUTCFullYear();
for (var year = startYear - 1; year <= endYear + 1; year++) {
var approx = approximateEquinoxForYear(year);
var refinedVernal = refineEquinox(approx.vernal);
var refinedAutumnal = refineEquinox(approx.autumnal);
// Convert days since J2000 back to a full Julian date then to a Date.
// J2000 corresponds to Julian Date 2451545.
var vernalDate = fromJulian(refinedVernal + J2000);
var autumnalDate = fromJulian(refinedAutumnal + J2000);
if (vernalDate >= rangeStart && vernalDate <= rangeEnd) {
equinoxes.vernal.push(vernalDate);
}
if (autumnalDate >= rangeStart && autumnalDate <= rangeEnd) {
equinoxes.autumnal.push(autumnalDate);
}
}
return equinoxes;
}
//# sourceMappingURL=equinox.js.map