%unimsg 0 worship/prayer-times/v1 -- The calculation of Islamic prayer times: one astronomical core, and the -- several conventions layered on it. -- -- This document exists because prayer times are the clearest real case of the -- thing this format was made for. There is one piece of astronomy, agreed by -- everyone, and a set of rules about how to apply it, not agreed by everyone. -- Every implementation in the wild hard-codes those rules as a table in its -- source. They are not code. They are data with authorities attached, and they -- belong somewhere a person can read, compare and cite. -- -- READ THIS BEFORE USING ANY NUMBER HERE. The parameters below carry religious -- weight: an angle that is wrong by a degree moves the start of a fast. They -- are recorded to the best of the author's knowledge, each with the authority -- it is attributed to, and they must be checked against that authority before -- anyone prays by them. The document's structure is offered with confidence; -- its parameter values are offered for verification. -- -- The format takes no position on which convention is correct. It could not: -- this is ikhtilāf — a difference of opinion among scholars, each reasoned and -- each held by a recognised authority — and it is not a question an encoding -- gets a vote on. What the format can do is carry all of them, side by side, -- each saying whose it is. prayer-times @worship/prayer-times/v1 { title "Prayer time calculation, with its conventions carried as data" status :provisional scope { is "the astronomical core, the convention parameters, the schools for Asr, the strategies for latitudes where the core has no solution, and vectors that pin computed times" is-not [ "a ruling on which convention applies to anyone", "a timetable — a timetable is this, evaluated, for one place and year", "an ephemeris: the solar position here is the standard low-precision series, good to well under a minute of time, which is finer than the rounding every convention applies afterwards" ] on-precision "Worth stating plainly, because it is the question that prompted this document. Nothing here needs exact decimals. The arithmetic is trigonometric and the target is about a second; a double carries fifteen significant digits and is comfortable. Exact decimals earn their place where values differ beyond that — money, and amounts denominated in very small units. What this calculation stresses instead is transcendental functions, which is a different axis, and one the store's formula grammar deliberately does not reach." } -- =================================================================== -- THE CORE -- =================================================================== -- Inert data. These steps describe a computation and do not perform one; a -- reader that has sine, cosine and an arc-cosine can carry them out. solar !formula/procedure { yields "the sun's declination and the equation of time, for an instant" given [ "a calendar date", "a longitude, to place the instant" ] needs [ :sin, :cos, :arcsin, :arctan2 ] steps [ | n name rule | 1 "julian day" "the standard civil-to-Julian conversion, less longitude / (15 x 24)" | 2 "days from epoch" "d = jd - 2451545.0" | 3 "mean anomaly" "g = 357.529 + 0.98560028 x d" | 4 "mean longitude" "q = 280.459 + 0.98564736 x d" | 5 "true longitude" "L = q + 1.915 x sin(g) + 0.020 x sin(2g)" | 6 "obliquity" "e = 23.439 - 0.00000036 x d" | 7 "right ascension" "RA = arctan2(cos(e) x sin(L), cos(L)) / 15" | 8 "declination" "dec = arcsin(sin(e) x sin(L))" | 9 "equation of time" "eqt = q / 15 - RA, brought into [-12, 12] hours" ] accuracy "Under a minute of time for any date within a century or so of the epoch. Every convention rounds to the minute afterwards, so the series is finer than the answer." } hour-angle !formula/procedure { yields "the hours between solar noon and the sun standing at a given altitude" rule "T(a) = arccos( ( -sin(a) - sin(dec) x sin(lat) ) / ( cos(dec) x cos(lat) ) ) / 15" no-solution "When the quotient falls outside [-1, 1] the sun never reaches that altitude on that day at that latitude, and T has no value. This is not an error and not a missing datum: it is a correct answer of the form `there is none`. See undefined-is-not-missing below." } noon { rule "dhuhr = 12 + timezone - longitude / 15 - equation-of-time" note "The only prayer time that is not an hour angle. Every other time is noon plus or minus one." } from-noon [ | prayer at altitude-or-rule | :fajr "noon - T(fajr-angle)" "the convention's Fajr angle below the horizon" | :sunrise "noon - T(0.833)" "the sun's upper limb at the horizon, with refraction" | :dhuhr "noon" "the sun on the meridian" | :asr "noon + A(shadow-factor)" "the shadow rule below" | :maghrib "noon + T(0.833)" "sunset, the mirror of sunrise" | :isha "noon + T(isha-angle)" "or a fixed interval after Maghrib, per convention" ] asr !formula/procedure { rule "A(f) = arccos( ( sin(arccot(f + tan(|lat - dec|))) - sin(dec) x sin(lat) ) / ( cos(dec) x cos(lat) ) ) / 15" meaning "The moment an object's shadow exceeds its noon shadow by f times the object's own height." schools [ | school shadow-factor followed-by | :standard 1 "Shafi'i, Maliki and Hanbali" | :hanafi 2 "Hanafi" ] note "One number, and it moves Asr by well over an hour in summer. See vectors p-001 and p-006, which differ in nothing else." } -- =================================================================== -- THE CONVENTIONS -- =================================================================== -- The rules-as-data section, and the reason the document exists. Each row is -- a position held by a named body. The format holds them all; it adjudicates -- none of them. -- -- `isha-minutes` where present replaces the angle: Isha falls a fixed -- interval after Maghrib rather than at a solar altitude. conventions [ | id attributed-to fajr-angle isha-angle isha-minutes | :mwl "Muslim World League" 18.0 17.0 0 | :isna "Islamic Society of North America" 15.0 15.0 0 | :egyptian "Egyptian General Authority of Survey" 19.5 17.5 0 | :umm-al-qura "Umm al-Qura University, Makkah" 18.5 0.0 90 | :karachi "University of Islamic Sciences, Karachi" 18.0 18.0 0 | :tehran "Institute of Geophysics, University of Tehran" 17.7 14.0 0 | :jafari "Shia Ithna Ashari, Leva Research Institute, Qum" 16.0 14.0 0 ] convention-notes { verify-these "Every number in that table is a parameter with religious consequence and is recorded here from secondary knowledge. An implementer must confirm each against the body it is attributed to. The attribution column exists so that this is possible rather than merely advisable." umm-al-qura-ramadan "Umm al-Qura is commonly cited as using 120 minutes rather than 90 during Ramadan. A convention whose parameter depends on the season is exactly the kind of rule that ends up buried in code; if it is adopted it belongs here as a second row, not as a branch." maghrib-is-not-always-sunset "Some conventions, Tehran and Jafari among them, place Maghrib at a small angle below the horizon rather than at sunset. Not modelled in the table above, and that is a gap rather than a decision." } -- =================================================================== -- WHERE THE SUN DOES NOT COOPERATE -- =================================================================== high-latitude { problem "Above roughly 48 degrees the sun does not descend far enough below the horizon in summer for Fajr or Isha to exist. Further north it does not set at all, and Maghrib and sunrise go with them." undefined-is-not-missing "Four states that all look like `no value` if they are not distinguished, and an implementation that conflates them will be wrong in a way nobody notices:" states [ | state means reader-should | :computed "the equation had a solution" "show the time" | :undefined "the sun never reaches that altitude here today" "say so, and name the strategy if one is applied" | :approximated "no solution, and a named strategy supplied a time" "show the time and the strategy together" | :failed "the calculation could not be carried out at all" "report the fault, never a time" ] strategies [ | id rule | :none "leave Fajr and Isha undefined and say so" | :middle-of-night "the night, sunset to sunrise, halved" | :one-seventh "a seventh of the night from sunset, and from sunrise" | :angle-based "a portion of the night set by the convention's own angle, night / (60 / angle)" | :nearest-latitude "compute as though at 48.5 degrees" ] which-strategy "A choice, and not one this document makes. The strategies are recorded because they exist and are used; selecting one is a matter for whoever prays by the result." } -- =================================================================== -- VECTORS -- =================================================================== -- Computed from the rules above, with no strategy applied for high latitudes, -- so the undefined cases show as undefined. Times are local, rounded to the -- nearest minute, and Asr uses the standard factor except where stated. -- -- These make the document falsifiable: an implementation derived from it -- either reproduces them or does not. vectors { rounding "to the nearest minute; a convention that rounds differently will differ from these by up to a minute, which is a real difference and should be declared rather than absorbed" asr-factor "1 everywhere except p-006, which is the same instant computed with 2 and differs in nothing else" times [ | id place date convention fajr sunrise dhuhr asr maghrib isha | :p-001 "Makkah" "2026-08-04" :mwl "04:35" "05:55" "12:27" "15:46" "18:59" "20:14" | :p-002 "Makkah" "2026-08-04" :isna "04:49" "05:55" "12:27" "15:46" "18:59" "20:05" | :p-003 "Makkah" "2026-08-04" :egyptian "04:27" "05:55" "12:27" "15:46" "18:59" "20:17" | :p-004 "Makkah" "2026-08-04" :umm-al-qura "04:32" "05:55" "12:27" "15:46" "18:59" "20:29" | :p-005 "Makkah" "2026-08-04" :karachi "04:35" "05:55" "12:27" "15:46" "18:59" "20:19" | :p-006 "Makkah" "2026-08-04" :mwl "04:35" "05:55" "12:27" "17:01" "18:59" "20:14" | :p-010 "London" "2026-08-04" :mwl "02:44" "05:28" "13:07" "17:15" "20:45" "23:15" | :p-011 "London" "2026-06-21" :mwl "-" "04:43" "13:02" "17:25" "21:21" "-" | :p-012 "Tromso" "2026-06-21" :mwl "-" "-" "12:46" "17:58" "-" "-" ] undefined-vectors { p-011 "London on the summer solstice: the sun sets and rises, so Maghrib and sunrise exist, and it never falls 18 degrees below the horizon, so Fajr and Isha do not. A dash here is the answer, not a gap." p-012 "Tromso on the same day: the sun does not set. Sunrise, Maghrib, Fajr and Isha are all undefined together, and only Dhuhr and Asr survive — the two that do not depend on the horizon." p-010 "London five weeks later, everything defined, and Fajr at 02:44 with Isha at 23:15. Nothing is wrong with it, and it is the case that makes people reach for a strategy." } what-would-falsify-this "Any of these times differing by more than a minute from an implementation of the steps above, or a dash where a time was computed, or a time where a dash was expected." } -- =================================================================== -- PROVENANCE -- =================================================================== provenance { astronomy "The low-precision solar series is standard and long published; it is not anyone's proprietary work and is reproduced here as steps rather than as code." parameters "Recorded from secondary knowledge and attributed per row. Not verified against the issuing bodies by the author of this file. This is stated in the header, in convention-notes, and here, because it is the one thing about this document that could cause harm if skimmed." vectors-from "Evaluating the steps in this document, not from any published timetable. They test that an implementation matches this document. They do not test that this document matches the authorities — only checking the parameters can do that, and that check has not been done." method-note "Written after a session in which prose specifications were repeatedly found to be satisfied by whatever the reader understood them to mean. The conventions are a table, the states are a table, the strategies are a table, and the vectors carry numbers, for that reason." } }