No description
Find a file
Aric Camarata c02f197ece v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm
Complete rewrite from plain JavaScript to TypeScript with dual CJS/ESM output
via tsup. Removes all legacy .js source files and the old CommonJS-only index.

Key changes:
- Full TypeScript source in src/ with strict mode and declaration maps
- tsup build: dist/index.cjs + dist/index.mjs + dual .d.ts / .d.mts types
- 14 traditional fixed-angle methods (UOIF through MUIS) + MSC seasonal method
- PCD dynamic algorithm: MSC seasonal base + Earth-Sun distance correction +
  ecliptic geometry + atmospheric refraction + observer elevation
- getTimesAll() batches all 14x2 zenith angles into a single SPA call
- getMscFajr() / getMscIsha() expose MSC seasonal reference directly
- getAngles() returns the PCD-computed fajrAngle and ishaAngle
- High-latitude bounds: angles clipped to [10, 20] above 55N
- 106 tests across ESM and CJS (test.mjs + test-cjs.cjs)
- CI matrix: Node 20/22/24, typecheck, pack-check
- Wiki: 12 reference pages + 6-page research section with global accuracy study,
  home-territory comparison, observational evidence, and field observation matrix
- Moon functions removed (migrated to moon-sighting package)
- pnpm-only, Node >=20, sideEffects: false
2026-02-25 18:11:20 -05:00
.github/workflows v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
.wiki v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
src v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
.editorconfig v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
.gitignore v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
.npmignore feat: improve Asr solver, dynamic twilight angles, and MSC algorithm 2025-05-04 08:59:31 -04:00
.npmrc v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
.nvmrc v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
CHANGELOG.md v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
LICENSE Fixes for 1.6.1 2025-05-04 09:34:00 -04:00
package.json v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
pnpm-lock.yaml v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
pnpm-workspace.yaml v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
README.md v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
test-cjs.cjs v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
test.mjs v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
tsconfig.json v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00
tsup.config.ts v2.0.0 — TypeScript rewrite, dual ESM/CJS, 14 methods + PCD dynamic algorithm 2026-02-25 18:11:20 -05:00

pray-calc

npm version CI License: MIT

Islamic prayer times for any location and date. The primary method uses a physics-grounded dynamic twilight angle algorithm that adjusts Fajr and Isha angles for latitude, season, Earth-Sun distance, and atmospheric conditions. Fourteen traditional fixed-angle methods are included for direct comparison.

Installation

pnpm add pray-calc   # or npm install pray-calc

Quick Start

import { calcTimes } from 'pray-calc';

const times = calcTimes(
  new Date('2024-06-21'),
  40.7128,   // New York latitude
  -74.0060,  // longitude
  -4,        // UTC offset (hours)
);

console.log(times.Fajr);    // "03:51:24"
console.log(times.Sunrise); // "05:25:08"
console.log(times.Dhuhr);   // "13:01:17"
console.log(times.Asr);     // "17:02:43"
console.log(times.Maghrib); // "20:31:17"
console.log(times.Isha);    // "22:07:43"
console.log(times.angles);  // { fajrAngle: 14.8, ishaAngle: 14.6 }

CJS

const { calcTimes } = require('pray-calc');

Compare all methods

import { calcTimesAll } from 'pray-calc';

const all = calcTimesAll(new Date('2024-06-21'), 40.7128, -74.0060, -4);

// Dynamic primary times
console.log(all.Fajr);   // "03:51:24"

// Traditional method comparison
console.log(all.Methods.ISNA);    // ["03:57:12", "22:22:18"]  [fajr, isha]
console.log(all.Methods.MWL);     // ["03:25:08", "22:40:31"]
console.log(all.Methods.MSC);     // ["03:53:41", "22:09:12"]

API

getTimes(date, lat, lng, tz?, elevation?, temperature?, pressure?, hanafi?)

Returns raw fractional-hour prayer times using the dynamic method.

Parameter Type Default Description
date Date required Observer's local date
lat number required Latitude, decimal degrees
lng number required Longitude, decimal degrees
tz number system offset UTC offset in hours
elevation number 0 Meters above sea level
temperature number 15 Ambient temperature, °C
pressure number 1013.25 Atmospheric pressure, mbar
hanafi boolean false Asr convention: false = Shafi'i, true = Hanafi

Returns PrayerTimes: { Qiyam, Fajr, Sunrise, Noon, Dhuhr, Asr, Maghrib, Isha, angles }. All times are fractional hours in local time (e.g., 5.5 = 05:30:00). NaN when an event cannot be computed (polar night, etc.).

calcTimes(date, lat, lng, tz?, elevation?, temperature?, pressure?, hanafi?)

Same as getTimes, formatted as HH:MM:SS strings. Returns "N/A" for unavailable times.

getTimesAll(...)

Same signature. Returns PrayerTimesAll: extends PrayerTimes with Methods, a record mapping each of the 14 method IDs to [fajrTime, ishaTime] as fractional hours.

calcTimesAll(...)

Same as getTimesAll, fully formatted. Methods values are [fajrString, ishaString].

getAngles(date, lat, lng, elevation?, temperature?, pressure?)

Returns { fajrAngle, ishaAngle } in degrees (positive = below horizon).

getAsr(solarNoon, latitude, declination, hanafi?)

Computes Asr from solar noon time, latitude, and solar declination. Returns fractional hours.

getQiyam(fajrTime, ishaTime)

Returns the start of the last third of the night as fractional hours.

getMscFajr(date, latitude) / getMscIsha(date, latitude, shafaq?)

Moonsighting Committee Worldwide minute offsets: minutes before sunrise (Fajr) and minutes after sunset (Isha). shafaq controls which twilight phase is used for Isha: 'general' (default), 'ahmer' (red glow), or 'abyad' (white glow).

METHODS

Exported array of all 14 MethodDefinition objects.

Supported Methods

ID Name Fajr Isha Region
UOIF Union des Organisations Islamiques de France 12° 12° France
ISNACA IQNA / Islamic Council of North America 13° 13° Canada
ISNA FCNA / Islamic Society of North America 15° 15° US, UK, AU, NZ
SAMR Spiritual Administration of Muslims of Russia 16° 15° Russia
IGUT Institute of Geophysics, Univ. of Tehran 17.7° 14° Iran
MWL Muslim World League 18° 17° Global
DIBT Diyanet, Turkey 18° 17° Turkey
Karachi Univ. of Islamic Sciences, Karachi 18° 18° PK, BD, IN, AF
Kuwait Kuwait Ministry of Islamic Affairs 18° 17.5° Kuwait
UAQ Umm Al-Qura Univ., Makkah 18.5° +90 min Saudi Arabia
Qatar Qatar / Gulf Standard 18° +90 min Qatar, Gulf
Egypt Egyptian General Authority of Survey 19.5° 17.5° EG, SY, IQ, LB
MUIS Majlis Ugama Islam Singapura 20° 18° Singapore
MSC Moonsighting Committee Worldwide seasonal seasonal Global

Dynamic Method

Standard prayer time libraries use a fixed angle (e.g., MWL: 18°) applied globally. This works near the equator but fails at higher latitudes: above 48.5°N in summer, the Sun never reaches 18° depression, so a 18°-everywhere library produces missing Isha times. Observational campaigns also show that at mid-latitudes, true dawn appears when the Sun is around 1416° below the horizon, not 18°.

The dynamic method computes the angle in three layers:

  1. MSC seasonal base — Khalid Shaukat's piecewise model, calibrated against field observations across latitudes 0°55°N/S. Returns minutes before/after sunrise/sunset, converted to depression degrees via spherical trigonometry.

  2. Physics corrections — Earth-Sun distance (r via Jean Meeus elliptical orbit), Fourier harmonic smoothing, atmospheric refraction at the computed altitude, and elevation horizon dip.

  3. Physical bounds — clipped to [10°, 22°].

At the equator the result converges to approximately 18°, consistent with historical usage. At 5055°N in summer it falls to 1214°, matching empirical UK observations.

Full detail: Dynamic Algorithm wiki page

Architecture

  • Only runtime dependency: nrel-spa (NREL Solar Position Algorithm)
  • getSolarEphemeris — Jean Meeus Ch. 25: declination, Earth-Sun distance, ecliptic lon
  • getTimesAll — single batch SPA call for all 14×2 + 2 dynamic zenith angles

Full detail: Architecture wiki page

Compatibility

  • Node.js >= 20
  • ESM and CJS builds included
  • TypeScript types bundled
  • No browser-incompatible APIs

TypeScript

import type {
  PrayerTimes,
  FormattedPrayerTimes,
  PrayerTimesAll,
  FormattedPrayerTimesAll,
  TwilightAngles,
  MethodDefinition,
} from 'pray-calc';

Documentation

Full documentation: GitHub Wiki

License

MIT. Copyright (c) 2023-2026 Aric Camarata.

See LICENSE for full terms.