npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verifyhash/solar-calc

v0.1.1

Published

Zero-dependency solar position & daylight math (NOAA algorithm): sunrise, sunset, solar noon, day length, elevation, azimuth, civil twilight, and golden hour for any latitude/longitude/date.

Readme

solar-calc

A tiny, zero-dependency Node library for solar geometry: sunrise, sunset, solar noon, day length, the Sun's elevation and azimuth at any instant, plus civil-twilight and golden-hour boundaries — for any latitude, longitude, and date.

Every export is a pure function: no I/O, no network, no daemon, no dependencies, and no mutation of its arguments. The math follows the algorithm published by NOAA's Solar Calculator, a simplified form of Jean Meeus, Astronomical Algorithms (2nd ed.). Against published almanac times it lands within about a minute at mid latitudes (see the test fixtures).

Who it's for

Developers building weather, photography, astronomy, or agriculture tools who need reliable solar numbers without pulling in a big ephemeris package or hitting an API:

  • a weather dashboard showing today's sunrise/sunset and length of day;
  • a photography planner that wants the golden-hour window for a shoot;
  • an astronomy or stargazing app that needs when civil twilight ends;
  • a solar/agriculture model that needs the Sun's elevation through the day.

Honest limits

  • Accuracy is ~1 minute at mid latitudes and degrades toward the poles, where a tiny error in the Sun's altitude maps to a large time swing. Near the Arctic/Antarctic circles, treat sunrise/sunset times as approximate; the polar-night / midnight-sun detection (returning null) is reliable.
  • Elevation and azimuth are geometric — the Sun's true position. They do not add atmospheric refraction, so within ~0.5° of the horizon the apparent (refracted) Sun sits a little higher than the returned value.
  • Sunrise/sunset use the standard −0.833° horizon (refraction + solar radius). One visible consequence: at the equator on an equinox the day comes out as ~12 h 07 m, not exactly 12 h — that offset is real, not a bug.
  • This is a position library, not an irradiance/energy model: it tells you where the Sun is, not how much power hits a panel.

Install / use

No install step, no dependencies. Copy the folder in and require it:

const solar = require('./solar-calc');

Conventions

| input | meaning | |-------------|---------------------------------------------------------------------| | lat | latitude in degrees, + North (New York ≈ 40.71) | | lon | longitude in degrees, + East (New York ≈ -74.01) | | dateInput | a JS Date (only its Y/M/D matter, read in UTC) or a plain { year, month, day } object with a 1-based month | | tz | offset of the local clock from UTC in hours (US Eastern Standard Time = -5); defaults to 0 (UTC) |

Daily event functions (sunrise, sunset, solarNoon, twilight, golden hour) return a JS Date for the true UTC instant of the event, or null when it doesn't happen that day (polar night / midnight sun). To print one in local time, use localClockMinutes(instant, tz) or format the Date with your own tz.

The instantaneous functions solarElevation(lat, lon, date) and solarAzimuth(lat, lon, date) take a full Date instant (date and time, UTC) and need no tz — the instant already fixes the moment.

Worked example

const solar = require('./solar-calc');

// New York City, June solstice 2021. EDT is UTC−4.
const lat = 40.7128;
const lon = -74.006;
const tz = -4;
const date = { year: 2021, month: 6, day: 21 };

// Pretty-print a UTC event instant as local "HH:MM".
function localHM(instant) {
  if (!instant) return 'none';
  const m = solar.localClockMinutes(instant, tz);
  const h = Math.floor(m / 60);
  const min = Math.round(m % 60);
  return `${String(h).padStart(2, '0')}:${String(min).padStart(2, '0')}`;
}

console.log('sunrise    ', localHM(solar.sunrise(lat, lon, date, tz)));     // 05:25
console.log('solar noon ', localHM(solar.solarNoon(lat, lon, date, tz)));   // 12:58
console.log('sunset     ', localHM(solar.sunset(lat, lon, date, tz)));      // 20:31

const mins = solar.dayLengthMinutes(lat, lon, date, tz);
console.log('day length ', (mins / 60).toFixed(2), 'hours');               // 15.09 hours

// Golden hour for an evening shoot:
console.log('golden hour', localHM(solar.goldenHourEveningStart(lat, lon, date, tz)),
            '→', localHM(solar.sunset(lat, lon, date, tz)));

// Where is the Sun at 15:00 local (= 19:00 UTC)?
const t = new Date(Date.UTC(2021, 5, 21, 19, 0, 0));
console.log('elevation  ', solar.solarElevation(lat, lon, t).toFixed(1), '°');
console.log('azimuth    ', solar.solarAzimuth(lat, lon, t).toFixed(1), '° from north');

API

All angles are in degrees; all times/durations in minutes unless noted.

Daily events → Date (UTC instant) or null

| function | returns | |----------|---------| | solarNoon(lat, lon, dateInput, tz?) | instant the Sun is highest (always defined) | | sunrise(lat, lon, dateInput, tz?) | upper limb clearing the horizon, or null | | sunset(lat, lon, dateInput, tz?) | upper limb touching the horizon, or null | | civilDawn(lat, lon, dateInput, tz?) | Sun 6° below horizon, rising, or null | | civilDusk(lat, lon, dateInput, tz?) | Sun 6° below horizon, setting, or null | | goldenHourMorningEnd(lat, lon, dateInput, tz?) | Sun reaches 6° elevation after sunrise, or null | | goldenHourEveningStart(lat, lon, dateInput, tz?) | Sun descends to 6° elevation before sunset, or null |

The morning golden hour runs sunrise → goldenHourMorningEnd; the evening golden hour runs goldenHourEveningStart → sunset.

Durations & raw quantities → number

| function | returns | |----------|---------| | dayLengthMinutes(lat, lon, dateInput, tz?) | minutes between sunrise and sunset; 0 on polar night, 1440 under the midnight sun | | declination(dateInput, tz?) | Sun's declination in degrees (+ North) for that day | | equationOfTime(dateInput, tz?) | apparent − mean solar time, in minutes (≈ −14 … +16 over a year) |

Instantaneous position → number (degrees)

| function | returns | |----------|---------| | solarElevation(lat, lon, date) | geometric elevation above the horizon; negative below. date is a full UTC Date instant | | solarAzimuth(lat, lon, date) | compass bearing clockwise from true North (0 = N, 90 = E, 180 = S, 270 = W) |

Helper

| function | returns | |----------|---------| | localClockMinutes(instant, tz?) | minutes-after-midnight [0, 1440) on the local clock for a returned event instant, or null if passed null |

Internals exposed for testing

  • _sunPosition(t) — the raw NOAA kernel behind everything above. Takes the Julian century t (centuries since J2000.0) and returns { declination, eqTime }: the Sun's declination in degrees and the equation of time in minutes (how far a sundial runs ahead of a mean clock). Exported with the underscore prefix so the test suite can pin it against NOAA reference values — treat it as internal; its shape is not covered by semver.

Running the tests

One command, no dependencies:

node test/solar.test.js

The suite checks four known-city fixtures (New York, London, Sydney, Tokyo) against published sunrise/sunset times within ±2 minutes, verifies the equator-at-equinox ≈12-hour day, the Tromsø midnight-sun (no sunset) and polar-night (no sunrise) cases, cross-checks the daily and instantaneous code paths against each other, and asserts the twilight/golden-hour ordering. It exits non-zero if anything fails.

License

MIT.

Install

npm install @verifyhash/solar-calc
const solar = require('@verifyhash/solar-calc');

const lat = 40.7128, lon = -74.006, tz = -4; // New York City, EDT
const date = { year: 2021, month: 6, day: 21 };
solar.sunrise(lat, lon, date, tz);          // Date — UTC instant of sunrise
solar.dayLengthMinutes(lat, lon, date, tz); // minutes of daylight