UNPKG

better-suncalc

Version:

A tiny TypeScript library for calculating sun/moon positions and phases.

99 lines 4.17 kB
/* solstice.ts */ import { J2000 } from "./constants"; import { sunCoords } from "./suncalc"; import { fromJulian, toDays } from "./utils"; /** * Returns the sun’s declination (in radians) at a given time expressed as days since J2000. * This is the function whose extrema correspond to the solstices. * @param d - Days since J2000. * @returns Declination in radians. */ function solDec(d) { return sunCoords(d).dec; } /** * Refines an approximate solstice time (given in days since J2000) by Newton–Raphson iteration. * The method uses finite-difference estimates of the first and second derivatives of the sun's declination. * * @param guess - The initial guess (in days since J2000). * @returns A refined value of d (days since J2000) where the derivative is nearly zero. */ function refineSolstice(guess) { var d = guess; var delta = 0.001; // small time step in days (~86.4 seconds) var eps = 1e-8; // convergence tolerance in days for (var i = 0; i < 20; i++) { // Finite-difference estimates: var fPlus = solDec(d + delta); var fMinus = solDec(d - delta); var f0 = solDec(d); // First derivative estimate: var fPrime = (fPlus - fMinus) / (2 * delta); // Second derivative estimate: var fDouble = (fPlus - 2 * f0 + fMinus) / (delta * delta); // Prevent division by near-zero: if (Math.abs(fDouble) < 1e-14) break; var dNew = d - fPrime / fDouble; if (Math.abs(dNew - d) < eps) { d = dNew; break; } d = dNew; } return d; } /** * For a given year, returns an approximate starting guess for the summer and winter solstices. * (Note: In JavaScript Date, month is 0-indexed: 5 for June and 11 for December.) * * @param year - The calendar year. * @returns Object with properties 'summer' and 'winter', each expressed in days since J2000. */ function approximateSolsticeForYear(year) { // Use June 21 and December 21 UT as rough initial guesses. var summerDate = new Date(Date.UTC(year, 5, 21, 0, 0, 0)); // June 21 var winterDate = new Date(Date.UTC(year, 11, 21, 0, 0, 0)); // December 21 return { summer: toDays(summerDate), winter: toDays(winterDate), }; } /** * Finds all solstice events (summer and winter) that occur within the given datetime range. * The algorithm uses a per-year approximate guess, then refines via Newton’s method applied * to the derivative of the sun's declination. * * The function returns an object containing arrays of Date objects for summer and winter solstices. * * @param rangeStart - Starting Date of the range. * @param rangeEnd - Ending Date of the range. * @returns Object with keys 'summer' and 'winter', each an array of Date instances. */ export function getSolstices(rangeStart, rangeEnd) { var solstices = { summer: [], winter: [] }; // Determine the range in calendar years. Include one extra year on each end to catch edge events. var startYear = rangeStart.getUTCFullYear(); var endYear = rangeEnd.getUTCFullYear(); for (var year = startYear - 1; year <= endYear + 1; year++) { var approx = approximateSolsticeForYear(year); // Refine each approximate event: obtain refined times in days since J2000. var refinedSummer = refineSolstice(approx.summer); var refinedWinter = refineSolstice(approx.winter); // Convert days since J2000 back to full Julian date and then to Date. // Note: J2000 corresponds to JD 2451545. var jdSummer = refinedSummer + J2000; var jdWinter = refinedWinter + J2000; var summerDate = fromJulian(jdSummer); var winterDate = fromJulian(jdWinter); // If the calculated event falls within the requested range, add it to the results. if (summerDate >= rangeStart && summerDate <= rangeEnd) { solstices.summer.push(summerDate); } if (winterDate >= rangeStart && winterDate <= rangeEnd) { solstices.winter.push(winterDate); } } return solstices; } //# sourceMappingURL=solstice.js.map