4.6 KiB
Architecture
Module Structure
src/
├── index.ts Main exports
├── types.ts All interfaces and type aliases
├── getSolarEphemeris.ts Jean Meeus solar declination, r, ecliptic lon
├── getMSC.ts MSC piecewise seasonal model
├── getAngles.ts Dynamic Fajr/Isha angle computation
├── getAsr.ts Asr from noon + declination
├── getQiyam.ts Last-third-of-night calculation
├── getTimes.ts Raw fractional-hour prayer times
├── calcTimes.ts Formatted HH:MM:SS prayer times
├── getTimesAll.ts All-methods comparison (raw)
└── calcTimesAll.ts All-methods comparison (formatted)
Data Flow
Date + lat/lng/tz/elev/temp/pressure
│
├─► getSolarEphemeris() ──► decl, r, eclLon
│
├─► getMscFajr/Isha() ──► minutes offset
│
└─► getAngles() ──► fajrAngle, ishaAngle
│
▼
nrel-spa getSpa()
(batch zenith angles)
│
├─► spaData.angles[0].sunrise = Fajr
├─► spaData.sunrise = Sunrise
├─► spaData.solarNoon = Noon
├─► spaData.sunset = Maghrib
└─► spaData.angles[1].sunset = Isha
│
├─► getAsr(noon, lat, decl)
└─► getQiyam(fajr, isha)
For getTimesAll, the SPA call is extended to include all method zenith angles:
allZeniths = [
fajrZenith, // dynamic Fajr
ishaZenith, // dynamic Isha
...methodZeniths // 14 × 2 = 28 more
]
One SPA call, 30 zenith angles. Methods with fixed-minute Isha (UAQ, Qatar) use a
placeholder zenith for the SPA call but override the result with sunset + minutes.
The MSC entry uses MSC minutes offsets relative to the SPA-computed sunrise/sunset.
External Dependency
The only runtime dependency is nrel-spa. It provides:
getSpa(date, lat, lng, tz, opts, zeniths): batch NREL SPA computationformatTime(fractionalHours): converts5.5to"05:30:00", returns"N/A"for NaN
The SPA (Solar Position Algorithm) from the National Renewable Energy Laboratory is the reference algorithm for solar position. It is accurate to within ±0.0003° and covers dates from -2000 to +6000.
Solar Ephemeris
getSolarEphemeris implements Jean Meeus, Astronomical Algorithms (2nd ed.),
Chapter 25. This provides:
- Solar declination (δ): accurate to ~0.01°
- Earth-Sun distance (r) in AU: accurate to ~0.0001 AU
- Apparent ecliptic longitude (λ) in degrees
These values are used by getAngles to apply physics corrections to the MSC base
angle. They are computed independently of the full SPA call using a faster,
lower-precision path that is sufficient for the correction magnitudes involved.
Why not use the SPA declination? The nrel-spa public API does not expose solar declination. The SPA computes it internally, but the value is not part of the public interface. Rather than monkey-patching nrel-spa or making a second SPA call with a known reference point, the Meeus equations provide a clean solution at adequate accuracy.
Asr Computation
getAsr does not call the SPA. Instead, it solves the shadow-ratio equation
analytically:
// Shadow ratio: s = 1 (Shafi'i) or s = 2 (Hanafi)
altitude = arccot(s + tan(|latitude - declination|))
cos(hourAngle) = (sin(altitude) - sin(lat)sin(decl)) / (cos(lat)cos(decl))
asrTime = solarNoon + hourAngle (converted to hours)
This is faster than an SPA call and avoids a dependency on internal ephemeris state.
Type System
All public types are in src/types.ts and re-exported from src/index.ts. The key
distinction is:
FractionalHours(number): raw output fromgetTimes/getTimesAllTimeString(string): formatted output fromcalcTimes/calcTimesAll
PrayerTimesAll extends PrayerTimes rather than duplicating fields. The Methods
map uses string keys (method IDs) rather than a union type, to allow forward
compatibility without breaking changes when methods are added.
Build
tsup builds dual CJS/ESM output:
dist/
├── index.cjs CJS bundle
├── index.mjs ESM bundle
├── index.d.ts CJS type declarations
└── index.d.mts ESM type declarations
Source maps are included. sideEffects: false allows tree-shaking.