Prayer times

Compute Fajr, sunrise, Dhuhr, Asr, Maghrib and Isha for any place with six calculation methods, high-latitude rules and manual tuning. The page lists what each method was checked against.

On this page
  1. Overview
  2. Quick start
  3. Creating a calculator
  4. Methods and angles
  5. Asr schools
  6. Makkah: Isha in Ramadan
  7. Elevation
  8. Time zones and daylight saving
  9. The next prayer
  10. High latitudes
  11. Manual tuning
  12. Exceptions
  13. What we checked

Overview#

RtlyKit\Prayer\PrayerTimes calculates the six daily times from astronomical equations. It needs no network and no data file. The solar declination and the equation of time come from the low-precision series in Jean Meeus, Astronomical Algorithms (the same equations as the NOAA Solar Calculator). The hour angle comes from spherical trigonometry. Asr uses the shadow-length factor. Sunrise and sunset use an altitude of -0.833 degrees (refraction plus the semi-diameter of the sun). The Fajr, Maghrib and Isha angles are the published conventions of each method.

Results are strings in HH:MM format, in the local time of the calculator's time zone, rounded to the nearest minute.

Quick start#

use RtlyKit\Prayer\PrayerTimes;

$tz  = new DateTimeZone('Asia/Tehran');
$day = new DateTimeImmutable('2026-03-21 12:00', $tz);

$times = PrayerTimes::forCity('tehran')->getTimes($day);
echo json_encode($times);
// {"fajr":"04:43","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:35","isha":"19:22"}

If you leave out the date, getTimes() uses today in the time zone of the calculator. The date you pass selects the local calendar day of that zone. A namespaced shortcut returns the same array for today: \RtlyKit\prayer_times('tehran', 'Tehran') (see Helpers and globals).

Creating a calculator#

There are two ways.

// 1) A built-in city: coordinates and time zone are filled in.
$p = PrayerTimes::forCity('mecca', PrayerTimes::METHOD_MAKKAH);

// 2) Your own coordinates.
$p = new PrayerTimes(
    latitude:  35.6892,
    longitude: 51.3890,
    method:    PrayerTimes::METHOD_TEHRAN,   // default
    asrFactor: PrayerTimes::ASR_STANDARD,    // default
    timezone:  new DateTimeZone('Asia/Tehran'), // default: date_default_timezone_get()
    elevation: 0.0,                          // metres, default 0
);

forCity() knows these names (any letter case): tehran, mashhad, isfahan, shiraz, tabriz, qom, mecca, medina, riyadh, istanbul, cairo, dubai, baghdad, jakarta. Any other name throws. For other cities, use the constructor with your own coordinates.

Methods and angles#

The method sets the depression angle of the sun for Fajr and Isha, and for Tehran also for Maghrib. Use the class constants, not strings.

ConstantFajrMaghribIsha
METHOD_TEHRAN (Tehran, default)17.7 degreessun at 4.5 degrees below the horizon14 degrees
METHOD_MWL (MWL)18 degreessunset17 degrees
METHOD_ISNA (ISNA)15 degreessunset15 degrees
METHOD_EGYPT (Egypt)19.5 degreessunset17.5 degrees
METHOD_MAKKAH (Makkah)18.5 degreessunsetMaghrib + 90 min (+120 min in Ramadan)
METHOD_KARACHI (Karachi)18 degreessunset18 degrees

The same Tehran day with each method. Only Fajr, Maghrib and Isha change. Sunrise, Dhuhr and Asr do not depend on the method:

foreach (['Tehran', 'MWL', 'ISNA', 'Egypt', 'Makkah', 'Karachi'] as $m) {
    echo str_pad($m, 8), json_encode(PrayerTimes::forCity('tehran', $m)->getTimes($day)), "\n";
}
// Tehran  {"fajr":"04:43","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:35","isha":"19:22"}
// MWL     {"fajr":"04:42","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:17","isha":"19:37"}
// ISNA    {"fajr":"04:57","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:17","isha":"19:27"}
// Egypt   {"fajr":"04:34","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:17","isha":"19:40"}
// Makkah  {"fajr":"04:39","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:17","isha":"19:47"}
// Karachi {"fajr":"04:42","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:17","isha":"19:42"}

Dhuhr is solar noon, when the sun crosses the meridian. No safety margin is added to any time. If your authority adds one, use withTune() below.

Asr schools#

Asr begins when an object's shadow equals its noon shadow plus a multiple of its height. ASR_STANDARD (1, one times the height) is used by the Shafi'i, Maliki, Hanbali and Ja'fari schools. ASR_HANAFI (2, twice the height) gives a later time.

$hanafi = PrayerTimes::forCity('tehran', 'Tehran', PrayerTimes::ASR_HANAFI);
echo $hanafi->getTimes($day)['asr'];   // 16:32  (standard: 15:39)

Makkah: Isha in Ramadan#

The Umm al-Qura convention sets Isha 90 minutes after Maghrib, and 120 minutes after Maghrib in Ramadan. The calculator uses 120 when the local calendar date falls in Ramadan of the package's own Umm al-Qura table (month 9), and 90 otherwise. It follows the civil date, not an Islamic day that starts at sunset.

$mk = PrayerTimes::forCity('mecca', 'Makkah');
foreach (['2026-02-20', '2026-03-01', '2026-03-21'] as $s) {
    $t = $mk->getTimes(new DateTimeImmutable($s, new DateTimeZone('Asia/Riyadh')));
    echo $s, ' maghrib ', $t['maghrib'], ' isha ', $t['isha'], "\n";
}
// 2026-02-20 maghrib 18:21 isha 20:21   (+120, Ramadan)
// 2026-03-01 maghrib 18:25 isha 20:25   (+120, Ramadan)
// 2026-03-21 maghrib 18:32 isha 20:02   (+90, after Ramadan)

Elevation#

The elevation argument is the height of the observer in metres. It lowers the sunrise and sunset altitude by the geometric dip 0.0347 * sqrt(h) degrees. On a mountain, sunrise is earlier and sunset later. Negative values count as 0. It affects sunrise, sunset and the times derived from sunset.

$a = new PrayerTimes(35.6892, 51.3890, 'Tehran', 1, $tz);
$b = new PrayerTimes(35.6892, 51.3890, 'Tehran', 1, $tz, 1500.0);
$d = new DateTimeImmutable('2026-06-21', $tz);
echo $a->getTimes($d)['sunrise'], "\n"; // 04:49
echo $b->getTimes($d)['sunrise'], "\n"; // 04:41

With the Tehran method, Maghrib is set by the 4.5-degree angle and not by sunset. So it does not change with elevation (19:45 in both cases above).

Time zones and daylight saving#

Each time is converted with the real UTC offset of the zone at that moment. A daylight-saving change in the middle of the year needs no extra work. Berlin changes on 2026-03-29:

$tzb = new DateTimeZone('Europe/Berlin');
$ber = new PrayerTimes(52.52, 13.405, 'MWL', 1, $tzb);
foreach (['2026-03-28', '2026-03-29'] as $s) {
    echo $s, ' ', json_encode($ber->getTimes(new DateTimeImmutable($s, $tzb))), "\n";
}
// 2026-03-28 {"fajr":"03:51","sunrise":"05:50","dhuhr":"12:11","asr":"15:39","maghrib":"18:34","isha":"20:26"}
// 2026-03-29 {"fajr":"04:48","sunrise":"06:48","dhuhr":"13:11","asr":"16:40","maghrib":"19:35","isha":"21:28"}

Pass the zone in which your users read the clock. If you leave it out, date_default_timezone_get() is used.

The next prayer#

nextPrayer() returns the first prayer after a moment (default: now). After Isha it moves on to tomorrow's Fajr. The date field tells you on which day the prayer falls. A prayer that falls after midnight is found too, with its own date.

$p = PrayerTimes::forCity('tehran');
echo json_encode($p->nextPrayer(new DateTimeImmutable('2026-03-21 10:00', $tz)));
// {"name":"dhuhr","time":"12:12","date":"2026-03-21"}
echo json_encode($p->nextPrayer(new DateTimeImmutable('2026-03-21 20:00', $tz)));
// {"name":"fajr","time":"04:42","date":"2026-03-22"}

Sunrise is part of the sequence: fajr, sunrise, dhuhr, asr, maghrib, isha.

High latitudes#

Far from the equator, in summer the sun may not sink deep enough for Fajr or Isha to exist, or the twilight may last most of the night. HighLatitudeRule tells the calculator what to do. The night is the time from sunset to the next sunrise.

RuleFajr and Isha are limited to this part of the night
HighLatitudeRule::NoneNo limit. A time whose angle is never reached is null
HighLatitudeRule::NightMiddleHalf of the night
HighLatitudeRule::OneSeventhOne seventh of the night
HighLatitudeRule::AngleBased (default)The twilight angle divided by 60, for example 18 degrees gives 0.3 of the night

The rule acts only where a time would otherwise be missing, or farther from sunrise or sunset than the rule allows. All other times keep their exact astronomical value. A fixed rule such as the Makkah Isha (Maghrib + 90 or 120 minutes) is never changed. Choose a rule with withHighLatitudeRule(). It returns a new calculator. Stockholm, 21 June, with the MWL method:

use RtlyKit\Prayer\HighLatitudeRule;

$tzs = new DateTimeZone('Europe/Stockholm');
$st  = new PrayerTimes(59.33, 18.07, 'MWL', 1, $tzs);
$june = new DateTimeImmutable('2026-06-21', $tzs);

foreach (HighLatitudeRule::cases() as $rule) {
    $t = $st->withHighLatitudeRule($rule)->getTimes($june);
    echo str_pad($rule->name, 12), $t['fajr'] ?? 'null', ' ', $t['isha'] ?? 'null', "\n";
}
// None         null null
// NightMiddle  00:49 00:50
// OneSeventh   02:45 22:54
// AngleBased   01:54 23:40

In places with no sunrise or sunset at all (polar day or polar night), sunrise and maghrib stay null. dhuhr is always defined. asr is null only when the sun stays below the horizon all day. Fajr and Isha then come from the rule, using a night of 12 hours around solar noon as the reference. If you need another convention, use HighLatitudeRule::None and decide for yourself.

Good to know. The default rule is AngleBased. Below about 44 degrees north all times are the same as in version 0.1.x. North of about 44 to 46 degrees, around the June solstice, some Fajr and Isha values differ from 0.1.x, and a time that used to be null now has a value. To get the old output, call withHighLatitudeRule(HighLatitudeRule::None).

Manual tuning#

Many authorities add a few minutes to a time for safety. withTune() does this. Pass minutes from -30 to +30 for any of fajr, sunrise, dhuhr, asr, maghrib and isha. Names you leave out get 0, so a new call replaces an earlier tune, and withTune([]) clears it. The minutes are added before the time is rounded, so a tuned time can cross midnight on the clock. nextPrayer() uses the tuned times.

$p = PrayerTimes::forCity('tehran');
$t = $p->withTune(['fajr' => 2, 'maghrib' => 3]);

echo json_encode($p->getTimes($day));
// {"fajr":"04:43","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:35","isha":"19:22"}
echo json_encode($t->getTimes($day));
// {"fajr":"04:45","sunrise":"06:07","dhuhr":"12:12","asr":"15:39","maghrib":"18:38","isha":"19:22"}

An unknown key, a value that is not an int (floats, numeric strings and null too) or a value outside -30 to 30 throws InvalidPrayerConfigException.

Exceptions#

Invalid configuration throws InvalidPrayerConfigException. It extends RtlyKitException and so also \InvalidArgumentException. For example:

new PrayerTimes(35, 51, 'Foo');
// Unknown calculation method: Foo. Available: MWL, ISNA, Egypt, Makkah, Karachi, Tehran
new PrayerTimes(35, 51, 'MWL', 3);
// Invalid Asr factor: 3 (use 1 or 2)
PrayerTimes::forCity('paris');
// Unknown city: paris
$p->withTune(['fajr' => 31]);
// Tune for fajr must be an integer from -30 to 30 minutes

See Error handling to catch all library errors with one catch.

What we checked#

The angles are conventions, so the useful test is to compute a city and a date and compare the result with a timetable published by a body that uses the method. We did this on 2026-10-08. Agreement within about 1 to 2 minutes counts as a match. Aggregator websites are not counted, because they use the same published angles.

MethodCompared withResult
TehranInstitute of Geophysics, University of Tehran, tables for 1405: Tehran, Mashhad, Isfahan and Shiraz on four datesFajr, sunrise, noon and Maghrib agree within 1 minute. The tables have no Asr and no Isha column. A council member of the Institute states Fajr 17.7 and Maghrib 4.5 degrees
MakkahUmm Al-Qura Calendar: five dates outside Ramadan and two dates in Ramadan 1447All six times agree within 1 minute, including Isha = Maghrib + 120 in Ramadan
EgyptDar al-Ifta, Cairo: six dates in October 2026All six times agree within 1 minute
MWL (18 / 17)Turkey, Diyanet: Istanbul, Ankara and Izmir on four dates between 8 October and 7 November 2026The published imsak and yatsi agree with our Fajr and Isha within 1 minute, so they fit the same 18 / 17 angles
Karachi (18 / 18) with Hanafi AsrJamia Uloom-e-Islamia Allama Banuri Town, Karachi: all 31 days of October 2026Fajr, sunrise, Asr, sunset and Isha agree within 1 minute, exactly on most days. The Hanafi rule (shadow of twice the height) is the one stated by Darul Uloom Deoband
Karachi (18 / 18)Malaysia, JAKIM e-solat, zone SGR01: four dates in October 2026Fajr and Isha differ by 1 to 2 minutes, which fits 18 / 18
ISNA (15 / 15)Fiqh Council of North AmericaThe Council names 15 degrees for both Fajr and Isha for the setting that corresponds to ISNA
Sunrise and sunsetNOAA Solar Calculator and timeanddateAgree within 1 minute
Good to know. The Banuri Town, Diyanet and JAKIM pages do not state their angles, so those comparisons show that the tables fit our angles. We did not find timetables issued by the University of Islamic Sciences in Karachi, by ISNA or by the Muslim World League, and no official Tehran table with Isha and Asr. The Tehran Isha of 14 degrees is attributed to the Lavaa Institute in Qom by secondary pages. High latitudes, elevation and dates outside those listed were checked against independent calculations and not against published tables. For religious or legal use, compare with the timetable of your local authority.