mihrab-methods 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +319 -2
- package/certification.json +120 -0
- package/data/corrections/algeria.json +784 -0
- package/data/corrections/diyanet.json +18444 -0
- package/data/corrections/indonesia.json +210 -0
- package/data/corrections/morocco.json +993 -0
- package/data/corrections/sweden.json +516 -0
- package/dist/cjs/algeria.d.ts +4 -0
- package/dist/cjs/algeria.js +22 -0
- package/dist/cjs/authorities/algeria.d.ts +3 -0
- package/dist/cjs/authorities/algeria.js +57 -0
- package/dist/cjs/authorities/diyanet.d.ts +9 -0
- package/dist/cjs/authorities/diyanet.js +90 -0
- package/dist/cjs/authorities/indonesia.d.ts +4 -0
- package/dist/cjs/authorities/indonesia.js +82 -0
- package/dist/cjs/authorities/morocco.d.ts +5 -0
- package/dist/cjs/authorities/morocco.js +65 -0
- package/dist/cjs/authorities/slug.d.ts +2 -0
- package/dist/cjs/authorities/slug.js +17 -0
- package/dist/cjs/authorities/sweden.d.ts +3 -0
- package/dist/cjs/authorities/sweden.js +58 -0
- package/dist/cjs/core/api.d.ts +61 -0
- package/dist/cjs/core/api.js +176 -0
- package/dist/cjs/core/civilDate.d.ts +17 -0
- package/dist/cjs/core/civilDate.js +41 -0
- package/dist/cjs/core/clock.d.ts +15 -0
- package/dist/cjs/core/clock.js +46 -0
- package/dist/cjs/core/compute.d.ts +10 -0
- package/dist/cjs/core/compute.js +59 -0
- package/dist/cjs/core/correctionList.d.ts +25 -0
- package/dist/cjs/core/correctionList.js +62 -0
- package/dist/cjs/core/fetchLatest.d.ts +57 -0
- package/dist/cjs/core/fetchLatest.js +137 -0
- package/dist/cjs/core/index.d.ts +14 -0
- package/dist/cjs/core/index.js +45 -0
- package/dist/cjs/core/registry.d.ts +28 -0
- package/dist/cjs/core/registry.js +29 -0
- package/dist/cjs/core/types.d.ts +193 -0
- package/dist/cjs/core/types.js +22 -0
- package/dist/cjs/core.d.ts +2 -0
- package/dist/cjs/core.js +18 -0
- package/dist/cjs/data/generated/algeriaCertification.d.ts +3 -0
- package/dist/cjs/data/generated/algeriaCertification.js +4 -0
- package/dist/cjs/data/generated/algeriaCities.d.ts +8 -0
- package/dist/cjs/data/generated/algeriaCities.js +5 -0
- package/dist/cjs/data/generated/algeriaCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/algeriaCorrections.js +4 -0
- package/dist/cjs/data/generated/algeriaMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/algeriaMethodPoints.js +5 -0
- package/dist/cjs/data/generated/countryOutlines.d.ts +5 -0
- package/dist/cjs/data/generated/countryOutlines.js +5 -0
- package/dist/cjs/data/generated/diyanetCertification.d.ts +3 -0
- package/dist/cjs/data/generated/diyanetCertification.js +4 -0
- package/dist/cjs/data/generated/diyanetCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/diyanetCorrections.js +4 -0
- package/dist/cjs/data/generated/diyanetCountries.d.ts +5 -0
- package/dist/cjs/data/generated/diyanetCountries.js +5 -0
- package/dist/cjs/data/generated/diyanetTurkeyPoints.d.ts +7 -0
- package/dist/cjs/data/generated/diyanetTurkeyPoints.js +5 -0
- package/dist/cjs/data/generated/diyanetWorldPoints.d.ts +10 -0
- package/dist/cjs/data/generated/diyanetWorldPoints.js +5 -0
- package/dist/cjs/data/generated/indonesiaCertification.d.ts +3 -0
- package/dist/cjs/data/generated/indonesiaCertification.js +4 -0
- package/dist/cjs/data/generated/indonesiaCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/indonesiaCorrections.js +4 -0
- package/dist/cjs/data/generated/kemenagMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/kemenagMethodPoints.js +5 -0
- package/dist/cjs/data/generated/kemenagPlaces.d.ts +8 -0
- package/dist/cjs/data/generated/kemenagPlaces.js +5 -0
- package/dist/cjs/data/generated/moroccoCertification.d.ts +3 -0
- package/dist/cjs/data/generated/moroccoCertification.js +4 -0
- package/dist/cjs/data/generated/moroccoCities.d.ts +8 -0
- package/dist/cjs/data/generated/moroccoCities.js +5 -0
- package/dist/cjs/data/generated/moroccoCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/moroccoCorrections.js +4 -0
- package/dist/cjs/data/generated/moroccoMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/moroccoMethodPoints.js +5 -0
- package/dist/cjs/data/generated/swedenCertification.d.ts +3 -0
- package/dist/cjs/data/generated/swedenCertification.js +4 -0
- package/dist/cjs/data/generated/swedenCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/swedenCorrections.js +4 -0
- package/dist/cjs/data/generated/swedenPlaces.d.ts +5 -0
- package/dist/cjs/data/generated/swedenPlaces.js +5 -0
- package/dist/cjs/diyanet.d.ts +4 -0
- package/dist/cjs/diyanet.js +22 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/indonesia.d.ts +4 -0
- package/dist/cjs/indonesia.js +22 -0
- package/dist/cjs/locate.d.ts +21 -0
- package/dist/cjs/locate.js +204 -0
- package/dist/cjs/methods/algeria.d.ts +56 -0
- package/dist/cjs/methods/algeria.js +119 -0
- package/dist/cjs/methods/indonesia.d.ts +31 -0
- package/dist/cjs/methods/indonesia.js +94 -0
- package/dist/cjs/methods/morocco.d.ts +61 -0
- package/dist/cjs/methods/morocco.js +113 -0
- package/dist/cjs/methods/revisions.d.ts +15 -0
- package/dist/cjs/methods/revisions.js +18 -0
- package/dist/cjs/methods/scratch-congress.d.ts +87 -0
- package/dist/cjs/methods/scratch-congress.js +539 -0
- package/dist/cjs/methods/sweden.d.ts +88 -0
- package/dist/cjs/methods/sweden.js +568 -0
- package/dist/cjs/morocco.d.ts +4 -0
- package/dist/cjs/morocco.js +22 -0
- package/dist/cjs/sun/preciseSun.d.ts +19 -0
- package/dist/cjs/sun/preciseSun.js +185 -0
- package/dist/cjs/sweden.d.ts +4 -0
- package/dist/cjs/sweden.js +22 -0
- package/dist/esm/algeria.d.ts +4 -0
- package/dist/esm/algeria.js +4 -0
- package/dist/esm/authorities/algeria.d.ts +3 -0
- package/dist/esm/authorities/algeria.js +51 -0
- package/dist/esm/authorities/diyanet.d.ts +9 -0
- package/dist/esm/authorities/diyanet.js +83 -0
- package/dist/esm/authorities/indonesia.d.ts +4 -0
- package/dist/esm/authorities/indonesia.js +75 -0
- package/dist/esm/authorities/morocco.d.ts +5 -0
- package/dist/esm/authorities/morocco.js +59 -0
- package/dist/esm/authorities/slug.d.ts +2 -0
- package/dist/esm/authorities/slug.js +14 -0
- package/dist/esm/authorities/sweden.d.ts +3 -0
- package/dist/esm/authorities/sweden.js +52 -0
- package/dist/esm/core/api.d.ts +61 -0
- package/dist/esm/core/api.js +162 -0
- package/dist/esm/core/civilDate.d.ts +17 -0
- package/dist/esm/core/civilDate.js +36 -0
- package/dist/esm/core/clock.d.ts +15 -0
- package/dist/esm/core/clock.js +39 -0
- package/dist/esm/core/compute.d.ts +10 -0
- package/dist/esm/core/compute.js +56 -0
- package/dist/esm/core/correctionList.d.ts +25 -0
- package/dist/esm/core/correctionList.js +56 -0
- package/dist/esm/core/fetchLatest.d.ts +57 -0
- package/dist/esm/core/fetchLatest.js +98 -0
- package/dist/esm/core/index.d.ts +14 -0
- package/dist/esm/core/index.js +14 -0
- package/dist/esm/core/registry.d.ts +28 -0
- package/dist/esm/core/registry.js +23 -0
- package/dist/esm/core/types.d.ts +193 -0
- package/dist/esm/core/types.js +18 -0
- package/dist/esm/core.d.ts +2 -0
- package/dist/esm/core.js +2 -0
- package/dist/esm/data/generated/algeriaCertification.d.ts +3 -0
- package/dist/esm/data/generated/algeriaCertification.js +2 -0
- package/dist/esm/data/generated/algeriaCities.d.ts +8 -0
- package/dist/esm/data/generated/algeriaCities.js +3 -0
- package/dist/esm/data/generated/algeriaCorrections.d.ts +3 -0
- package/dist/esm/data/generated/algeriaCorrections.js +2 -0
- package/dist/esm/data/generated/algeriaMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/algeriaMethodPoints.js +3 -0
- package/dist/esm/data/generated/countryOutlines.d.ts +5 -0
- package/dist/esm/data/generated/countryOutlines.js +3 -0
- package/dist/esm/data/generated/diyanetCertification.d.ts +3 -0
- package/dist/esm/data/generated/diyanetCertification.js +2 -0
- package/dist/esm/data/generated/diyanetCorrections.d.ts +3 -0
- package/dist/esm/data/generated/diyanetCorrections.js +2 -0
- package/dist/esm/data/generated/diyanetCountries.d.ts +5 -0
- package/dist/esm/data/generated/diyanetCountries.js +3 -0
- package/dist/esm/data/generated/diyanetTurkeyPoints.d.ts +7 -0
- package/dist/esm/data/generated/diyanetTurkeyPoints.js +3 -0
- package/dist/esm/data/generated/diyanetWorldPoints.d.ts +10 -0
- package/dist/esm/data/generated/diyanetWorldPoints.js +3 -0
- package/dist/esm/data/generated/indonesiaCertification.d.ts +3 -0
- package/dist/esm/data/generated/indonesiaCertification.js +2 -0
- package/dist/esm/data/generated/indonesiaCorrections.d.ts +3 -0
- package/dist/esm/data/generated/indonesiaCorrections.js +2 -0
- package/dist/esm/data/generated/kemenagMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/kemenagMethodPoints.js +3 -0
- package/dist/esm/data/generated/kemenagPlaces.d.ts +8 -0
- package/dist/esm/data/generated/kemenagPlaces.js +3 -0
- package/dist/esm/data/generated/moroccoCertification.d.ts +3 -0
- package/dist/esm/data/generated/moroccoCertification.js +2 -0
- package/dist/esm/data/generated/moroccoCities.d.ts +8 -0
- package/dist/esm/data/generated/moroccoCities.js +3 -0
- package/dist/esm/data/generated/moroccoCorrections.d.ts +3 -0
- package/dist/esm/data/generated/moroccoCorrections.js +2 -0
- package/dist/esm/data/generated/moroccoMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/moroccoMethodPoints.js +3 -0
- package/dist/esm/data/generated/swedenCertification.d.ts +3 -0
- package/dist/esm/data/generated/swedenCertification.js +2 -0
- package/dist/esm/data/generated/swedenCorrections.d.ts +3 -0
- package/dist/esm/data/generated/swedenCorrections.js +2 -0
- package/dist/esm/data/generated/swedenPlaces.d.ts +5 -0
- package/dist/esm/data/generated/swedenPlaces.js +3 -0
- package/dist/esm/diyanet.d.ts +4 -0
- package/dist/esm/diyanet.js +4 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/indonesia.d.ts +4 -0
- package/dist/esm/indonesia.js +4 -0
- package/dist/esm/locate.d.ts +21 -0
- package/dist/esm/locate.js +196 -0
- package/dist/esm/methods/algeria.d.ts +56 -0
- package/dist/esm/methods/algeria.js +114 -0
- package/dist/esm/methods/indonesia.d.ts +31 -0
- package/dist/esm/methods/indonesia.js +90 -0
- package/dist/esm/methods/morocco.d.ts +61 -0
- package/dist/esm/methods/morocco.js +107 -0
- package/dist/esm/methods/revisions.d.ts +15 -0
- package/dist/esm/methods/revisions.js +15 -0
- package/dist/esm/methods/scratch-congress.d.ts +87 -0
- package/dist/esm/methods/scratch-congress.js +534 -0
- package/dist/esm/methods/sweden.d.ts +88 -0
- package/dist/esm/methods/sweden.js +563 -0
- package/dist/esm/morocco.d.ts +4 -0
- package/dist/esm/morocco.js +4 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/sun/preciseSun.d.ts +19 -0
- package/dist/esm/sun/preciseSun.js +181 -0
- package/dist/esm/sweden.d.ts +4 -0
- package/dist/esm/sweden.js +4 -0
- package/docs/methods.md +190 -0
- package/package.json +178 -4
- package/schemas/certification.schema.json +59 -0
- package/schemas/corrections.schema.json +29 -0
package/README.md
CHANGED
|
@@ -1,3 +1,320 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mihrab-methods
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/mihrab-methods)
|
|
4
|
+
[](https://github.com/MihrabHQ/mihrab-methods/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
An open prayer-time library: the calculation methods of the national
|
|
7
|
+
authorities, implemented to reproduce their official timetables to the
|
|
8
|
+
minute, free for anyone to use. Accurate prayer times shouldn't be something
|
|
9
|
+
only a few apps can offer.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install mihrab-methods
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { nearestPlace, timesFor } from 'mihrab-methods/algeria'; // or 'mihrab-methods' for all five
|
|
17
|
+
|
|
18
|
+
const { place } = nearestPlace('algeria', 36.75, 3.06)!; // Algiers
|
|
19
|
+
const day = timesFor(place, '2026-10-15');
|
|
20
|
+
day.status; // 'certified' — the ministry's own printed minute
|
|
21
|
+
day.clock.label; // 'Africa/Algiers'
|
|
22
|
+
day.times!.fajr; // { instant: 2026-10-15T04:29:00.000Z, printed: '05:29' }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It is a library, in TypeScript, with a small server over it for everything
|
|
26
|
+
else (`npm run serve`); see [Use](#use).
|
|
27
|
+
|
|
28
|
+
Each method is the authority's own formula, worked out again from its
|
|
29
|
+
published times, with a small, fixed correction list for the few times where the formula
|
|
30
|
+
alone lands a fraction of a second on the wrong side of a minute. The aim is that
|
|
31
|
+
any app, site or mosque can give the times its community prays by, without
|
|
32
|
+
asking anyone.
|
|
33
|
+
|
|
34
|
+
Methods today:
|
|
35
|
+
|
|
36
|
+
| Authority | Method | On the printed minute | Within a minute | Checked over |
|
|
37
|
+
|---|---|---|---|---|
|
|
38
|
+
| **Sweden** — Diyanet's method, as used by Islamiska Förbundet | `src/methods/sweden.ts` — the Istanbul congress method as Diyanet computes it, with `data/corrections/sweden.json` | **100 %** of Diyanet's timetables (1.39 M times, 2024–2027) and of Islamiska Förbundet's (107 cities, 59 640 times, Oct 2026 → Nov 2027, with the rows set aside below) — by the method alone, 99.981 % and 99.966 % | 100 % | 2024 → 2027 |
|
|
39
|
+
| **Diyanet** — Türkiye and worldwide | `src/methods/sweden.ts` with Isha's angle by country (16° or 17°), a point per prayer per year in Türkiye, with `data/corrections/diyanet.json` | **100 %** of Diyanet's timetables for all 880 districts of Türkiye and Northern Cyprus and 7 252 places in 197 other countries (70.0 M times) — 99.987 % by the method alone | 100 % | 2024 → 2027 |
|
|
40
|
+
| **Morocco** — Ministry of Habous | `src/methods/morocco.ts` — each city's own sunrise and Maghrib horizon, the sun's semidiameter by its distance, the tables' own clock (GMT+1 until 17 Sep 2026), with `data/corrections/morocco.json` | **100 %** of the ministry's tables for its 164 located cities (75 768 times) — 99.122 % by the method alone | 100 % | 27 Aug → 11 Nov 2026 |
|
|
41
|
+
| **Algeria** — Ministry of Religious Affairs and Wakfs | `src/methods/algeria.ts`, with `data/corrections/algeria.json` | **100 %** of the ministry's 1448 AH table for its 68 cities (101 184 times) — 99.366 % by the method alone | 100 % | 1 Oct 2026 → 5 Jun 2027 |
|
|
42
|
+
| **Indonesia** — Kemenag | `src/methods/indonesia.ts`, with `data/corrections/indonesia.json` | **100 %** of the ministry's tables for all 517 regencies and cities (266 772 times) — 99.969 % by the method alone | 100 % | 7 Oct → 31 Dec 2026 |
|
|
43
|
+
|
|
44
|
+
The goal is 100 % on the printed minute, for every time of a full year, for
|
|
45
|
+
each method.
|
|
46
|
+
|
|
47
|
+
## Certification: 100 % or not at all
|
|
48
|
+
|
|
49
|
+
A method may be used only once it reproduces **every** time of its
|
|
50
|
+
authority's timetable on the printed minute — not one minute off, anywhere.
|
|
51
|
+
`npm run certify` checks each method day by day and writes
|
|
52
|
+
`certification.json`; `timesFor` throws `UncertifiedMethodError` for any
|
|
53
|
+
method not certified there, and says `status: 'method-only'` for a date
|
|
54
|
+
beyond the span it was checked over. `candidateTimes` runs a method
|
|
55
|
+
regardless, for research and for the check itself — not for showing anyone
|
|
56
|
+
their prayer times.
|
|
57
|
+
|
|
58
|
+
Days the timetable itself gets wrong (another town's times under a name, a
|
|
59
|
+
previous year's figures for the date, a figure that differs from the
|
|
60
|
+
authority's own) are set aside and listed in the record, so each can be
|
|
61
|
+
judged.
|
|
62
|
+
|
|
63
|
+
Today (checked 2026-10-16) **every method is certified**: Sweden and Diyanet for 2024–2027, and Morocco, Algeria and Indonesia for the tables published so far:
|
|
64
|
+
|
|
65
|
+
| Authority | Off the printed minute |
|
|
66
|
+
|---|---|
|
|
67
|
+
| Sweden | 0 of 59 640 (Islamiska Förbundet); 0 of 1 393 794 (Diyanet) |
|
|
68
|
+
| Diyanet (Türkiye and worldwide) | 0 of 69 999 603 |
|
|
69
|
+
| Morocco | 0 of 75 768 |
|
|
70
|
+
| Algeria | 0 of 101 184 |
|
|
71
|
+
| Indonesia | 0 of 266 772 |
|
|
72
|
+
|
|
73
|
+
## Status
|
|
74
|
+
|
|
75
|
+
| Method | Where it stands |
|
|
76
|
+
|---|---|
|
|
77
|
+
| Sweden | Certified, 2024–2027, against Diyanet's tables for Sweden (159 places), and against Islamiska Förbundet's published tables (11 Oct 2026 → 19 Nov 2027) for 107 of its 108 cities. Set aside from Islamiska Förbundet's: Kiruna, printed with Umeå's times; 33 395 times that differ from Diyanet's own table for the day (its 2027 rows repeat 2026's figures); and 646 rows copied from another day. On each, the method gives Diyanet's own table. |
|
|
78
|
+
| Diyanet | Certified, 2024–2027, for every district of Türkiye and Northern Cyprus and 7 252 places in 197 other countries: 8 291 of the 8 528 places Diyanet lists, with Sweden's 159. Each place is computed at a point of its own, checked to lie in the place's country. Not yet included: 147 places that could not be placed — seven of them Diyanet serves no table for (Grozny, Gudermes, Shali and Urus-Martan, Rožaje, Monte Carlo, Punta Arenas), the rest names the gazetteer does not match; 75 whose tables the method does not yet follow over all four years — about half of them in Mexico and Australia, whose tables keep a clock of their own, the rest scattered (Kazan, Fez, Ushuaia, Svalbard among them); 12 whose tables Diyanet computes at a point elsewhere — in another country, at sea or in the other hemisphere (Tupiza, Ribeirão das Neves, Montería, Hami, Kungälv and Galena among them); Syktyvkar, whose 2024 and 2027 tables print a Fajr hours from its 2025 and 2026 ones; and Ping Tung City and Rilchingen-Hanweiler, each listed again at the same point (as Pingtung and Kleinblittersdorf) with a table a minute apart on two days. |
|
|
79
|
+
| Morocco | Certified for 27 Aug → 11 Nov 2026 (164 cities): the method gives 99.12 % alone, and a list of 665 corrections the rest. Certification covers only the dates in the table: each newly published month is checked against the method, and its corrections are added in a new list. |
|
|
80
|
+
| Algeria | Certified for 1448 AH (1 Oct 2026 → 5 Jun 2027, 68 cities): the method gives 99.37 % alone, and a list of 642 corrections the rest. The method is at its limit: the angles, horizon, Maghrib margin, rounding, sun series, iterations and city points are each sharp optima, and the 642 are independent differences of about a second or less (no pattern by prayer, city, date or season; refitting the points to them only fits noise, and is worse on days held out). |
|
|
81
|
+
| Indonesia | Certified for 7 Oct → 31 Dec 2026 (517 regencies and cities): the method gives 99.969 % alone, and a list of 84 corrections the rest. Karawang's Asr, Maghrib and Isha are computed at a second point, about 2 km north-east of its first (its afternoon fits it, and it holds on days held out). The 84 are independent differences at a minute's edge (median 0.2 s, nine in ten under 1.3 s, the largest 3.9 s) — eight at Sibolga, whose mornings no point fits — and refitting points to them only fits noise. Certification covers only the dates in the tables: each newly published month is checked against the method, and its corrections are added in a new list. |
|
|
82
|
+
|
|
83
|
+
## Formulas and correction lists: why this can be used freely
|
|
84
|
+
|
|
85
|
+
Under EU and Swedish law, this approach is lawful use and "insubstantial
|
|
86
|
+
extraction": an unprotected mathematical formula, worked out from published
|
|
87
|
+
times, plus a very small, static list of factual data points that fixes edge
|
|
88
|
+
cases.
|
|
89
|
+
|
|
90
|
+
- **The methods are formulas.** A method of calculation is not protected as
|
|
91
|
+
such. What is here is the arithmetic: the sun's position, the angles, the
|
|
92
|
+
margins and the high-latitude rules each authority applies. It is written
|
|
93
|
+
from scratch, not copied.
|
|
94
|
+
- **The correction lists are microscopically small and static.** Each one
|
|
95
|
+
holds only the few times where the authority's own rounding falls the
|
|
96
|
+
other way: a point, a date, a prayer, a minute either way. Sweden's is 270
|
|
97
|
+
entries against 1.39 million times (0.02 %). The aim is the same for every
|
|
98
|
+
method: get the formula as close as possible, so the list that remains is
|
|
99
|
+
insubstantial.
|
|
100
|
+
- **No timetable is part of this repository.** It holds no copy of any
|
|
101
|
+
authority's tables. The times are computed, for any date and any point.
|
|
102
|
+
|
|
103
|
+
This is the project's position, not legal advice. The same rule applies to
|
|
104
|
+
every method added here: the best formula first, and a correction list only
|
|
105
|
+
for what is left.
|
|
106
|
+
|
|
107
|
+
## Sweden's and Diyanet's correction lists
|
|
108
|
+
|
|
109
|
+
The method reproduces Diyanet's computation; what it cannot reproduce is the
|
|
110
|
+
arithmetic of Diyanet's program, which puts about one time in five thousand on
|
|
111
|
+
the other side of a minute's rounding point — a fraction of a second away,
|
|
112
|
+
independently from place to place and day to day. `data/corrections/sweden.json`
|
|
113
|
+
lists those times (point, date, prayer, a minute either way: 270 over
|
|
114
|
+
2024–2027) and `timesFor` applies them. The list covers the years the
|
|
115
|
+
timetables do, and certification holds over those dates only: outside them
|
|
116
|
+
`timesFor` answers with `status: 'method-only'` (or refuses, with
|
|
117
|
+
`strict`). Diyanet's worldwide list
|
|
118
|
+
(`data/corrections/diyanet.json`) is the same kind: 9 118 entries at 4 660 points against
|
|
119
|
+
70.0 million times. Each year, once the next year's timetables are out:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
npm run corrections && for a in diyanet morocco algeria indonesia; do npm run corrections -- $a; done && npm run certify
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
What Diyanet's worldwide tables add to the Swedish method:
|
|
126
|
+
|
|
127
|
+
- **Isha by country:** 16° (the Congress's) in most of Europe, Russia,
|
|
128
|
+
Ukraine, Belarus, Kazakhstan, Mongolia, China, the United States and
|
|
129
|
+
Canada; 17° elsewhere. The high-latitude rules are worked at 16° either way.
|
|
130
|
+
- **The takdir at midsummer:** where the dawn forms all summer but midsummer's
|
|
131
|
+
true Isha falls later than the share carried from that night, midsummer is
|
|
132
|
+
the takdir's day, with the twenty-minute ramps (from about 44.4° north).
|
|
133
|
+
- **Türkiye's districts:** each prayer is computed at a point of its own in the
|
|
134
|
+
district, and the points move by a few thousandths of a degree from one
|
|
135
|
+
year's tables to the next (`data/places/diyanetTurkeyPoints.json`, a point
|
|
136
|
+
per prayer per year; Northern Cyprus the same).
|
|
137
|
+
- **Past local midnight:** where the first true dawn after the summer gap falls
|
|
138
|
+
before local midnight on the table's clock (far east of a zone's meridian,
|
|
139
|
+
as at Aihui in China), there is no transition: the gap's last day is printed
|
|
140
|
+
twenty minutes after the takdir and the true times follow.
|
|
141
|
+
- **Tables on their own clocks:** 192 of Diyanet's tables are printed on a
|
|
142
|
+
clock other than their place's zone — Alberta's keep summer time from 2026
|
|
143
|
+
on; Mexico's had it again in 2025; Queensland's and Western Australia's have
|
|
144
|
+
it in 2024, 2025 and 2027; Paraguay's keep the summer time the country gave
|
|
145
|
+
up in October 2024 until the end of 2025; Xinjiang's are Beijing time;
|
|
146
|
+
western Brazil's, Armenia's and some of Russia's and Mongolia's are a zone
|
|
147
|
+
or two away from their own; Alchevsk's are Moscow time with summer time.
|
|
148
|
+
For those the table's clock is read off the table itself (`clock` in
|
|
149
|
+
`data/places/diyanetWorldPoints.json`); the instants are the method's.
|
|
150
|
+
- **The day's clock:** a table prints the whole day in the clock kept at noon,
|
|
151
|
+
so on the morning the clocks go forward a Fajr before the change is printed
|
|
152
|
+
in the new time.
|
|
153
|
+
|
|
154
|
+
## Keeping an app current
|
|
155
|
+
|
|
156
|
+
`certification.json` is meant to be read by apps. For each authority it
|
|
157
|
+
says whether the method is certified, the method's `revision`
|
|
158
|
+
(`src/methods/revisions.ts`) and the correction list it certifies
|
|
159
|
+
(`corrections`: the dates it covers and how many times it moves):
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
"morocco": { "certified": true, "revision": 1,
|
|
163
|
+
"corrections": { "from": "2026-08-27", "to": "2026-11-11", "entries": 665 }, … }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
An app that computes revision 1 can take any newer list certified for
|
|
167
|
+
revision 1 straight from `data/corrections/<authority>.json` — no release
|
|
168
|
+
needed. A higher revision means the method itself changed and its lists no
|
|
169
|
+
longer fit the older code: the app should ask its user to update.
|
|
170
|
+
`certified: false` means the authority has published tables the method is
|
|
171
|
+
not yet on every minute of. Past a list's `to`, the method alone applies.
|
|
172
|
+
|
|
173
|
+
The check done right is in the library:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { fetchLatestCorrections } from 'mihrab-methods/morocco';
|
|
177
|
+
const r = await fetchLatestCorrections({ authority: 'morocco' });
|
|
178
|
+
// r.status: 'current' | 'updated' | 'method-moved' | 'uncertified' | 'rejected'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
It reads `certification.json`, takes a list only for the revision this
|
|
182
|
+
build computes, only when it is the list the certification names (dates,
|
|
183
|
+
size, and the **SHA-256** of the file as published), and puts it in force
|
|
184
|
+
(`applyCorrections` restores one an app has stored). Both files have a
|
|
185
|
+
published schema: `schemas/certification.schema.json` and
|
|
186
|
+
`schemas/corrections.schema.json`; `npm test` validates them.
|
|
187
|
+
|
|
188
|
+
Addresses: `main` is the live feed (what the Mihrab app reads). Every
|
|
189
|
+
release is also tagged, and the same paths under a tag are fixed for good:
|
|
190
|
+
`https://raw.githubusercontent.com/MihrabHQ/mihrab-methods/v1.0.0/certification.json`.
|
|
191
|
+
Pass a tag's base URL as `baseUrl` to pin.
|
|
192
|
+
|
|
193
|
+
The revision is raised whenever a change to a method or to its places'
|
|
194
|
+
points changes what it prints; `npm test` fails until certification.json
|
|
195
|
+
names the current revision and list. `CHANGELOG.md` says what each
|
|
196
|
+
revision changed in the output.
|
|
197
|
+
|
|
198
|
+
## Use
|
|
199
|
+
|
|
200
|
+
### As a package
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
npm install mihrab-methods
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
One entry point per authority, so an app ships only the data it needs
|
|
207
|
+
(Indonesia's is 100 KB; Diyanet's worldwide points and list are 1.4 MB):
|
|
208
|
+
|
|
209
|
+
| Import | Loads |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `mihrab-methods` | all five |
|
|
212
|
+
| `mihrab-methods/sweden`, `/diyanet`, `/morocco`, `/algeria`, `/indonesia` | one authority: its places, list and certification |
|
|
213
|
+
| `mihrab-methods/locate` | `countryAt`, `defaultAuthorityFor` — the country at a point, and the authority whose table it prays by (400 KB of outlines) |
|
|
214
|
+
| `mihrab-methods/core` | the calls and types with no data (load authorities yourself) |
|
|
215
|
+
|
|
216
|
+
CommonJS and ES modules, with types. Node 18+, or any bundler; nothing is
|
|
217
|
+
read from disk or fetched unless you call `fetchLatestCorrections`.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { placesOf, placeById, nearestPlace, timesFor, timesForRange, candidateTimes, methodInfo } from 'mihrab-methods';
|
|
221
|
+
import { defaultAuthorityFor } from 'mihrab-methods/locate';
|
|
222
|
+
|
|
223
|
+
defaultAuthorityFor(59.33, 18.07); // 'sweden' (null where none of the five is the authority)
|
|
224
|
+
const { place, distanceKm } = nearestPlace('sweden', 59.33, 18.07)!;
|
|
225
|
+
place.id; // 'sweden:stockholm' — stable; place.country 'SE'
|
|
226
|
+
placeById('diyanet:9146'); // Adana
|
|
227
|
+
|
|
228
|
+
timesFor(place, '2026-10-15'); // or { year: 2026, month: 10, day: 15 } — January is 1
|
|
229
|
+
// {
|
|
230
|
+
// status: 'certified', 'method-only' outside the certified dates: the method alone,
|
|
231
|
+
// on the printed minute all but rarely, never a minute further
|
|
232
|
+
// date: '2026-10-15', certifiedFrom: '2024-01-01', certifiedUntil: '2027-12-31', revision: 1,
|
|
233
|
+
// clock: { zone: 'Europe/Stockholm', offsetMinutes: 120, label: 'Europe/Stockholm' },
|
|
234
|
+
// times: { fajr: { instant: Date, printed: '05:13' }, sunrise: …, dhuhr: …, asr: …, maghrib: …, isha: … }
|
|
235
|
+
// }
|
|
236
|
+
timesFor(place, '2031-01-01', { strict: true }); // throws UncertifiedMethodError instead
|
|
237
|
+
timesFor(place, '2026-10-15', { asrShadow: 2 }); // the Hanafi Asr (the tables print the standard one)
|
|
238
|
+
timesForRange(place, '2026-10-01', '2026-10-31'); // a month, one DayTimes per day
|
|
239
|
+
candidateTimes(place, '2026-10-15', { corrections: false }); // the formula alone — research
|
|
240
|
+
methodInfo('morocco'); // angles, margins, rounding, revision, certification, sources, spec
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`printed` is the wall-clock minute on `clock`, which is the clock the
|
|
244
|
+
authority prints the day in: the place's zone as kept at noon, or the
|
|
245
|
+
table's own clock where it keeps one no zone does (Morocco's GMT+1 until
|
|
246
|
+
September 2026; some of Diyanet's). An app shows `printed` and schedules
|
|
247
|
+
on `instant`; nothing needs rounding or converting.
|
|
248
|
+
|
|
249
|
+
### As a server
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
npm run serve # http://localhost:8787
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
GET /v1/times?authority=diyanet&lat=41.01&lng=28.98&date=2026-10-15
|
|
257
|
+
GET /v1/times?place=algeria:27&from=2026-10-01&to=2026-10-31
|
|
258
|
+
GET /v1/nearest?authority=morocco&lat=33.57&lng=-7.59
|
|
259
|
+
GET /v1/authority?lat=59.33&lng=18.07
|
|
260
|
+
GET /v1/places?authority=indonesia
|
|
261
|
+
GET /v1/methods
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Self-hostable, nothing stored (`tools/serve.ts`, eighty lines).
|
|
265
|
+
|
|
266
|
+
### In another language
|
|
267
|
+
|
|
268
|
+
`docs/methods.md` states each method in prose and formulas, and
|
|
269
|
+
`test/vectors.json` is a small sample of places, dates and the times to
|
|
270
|
+
expect — enough to prove a port matches, too small to be a timetable.
|
|
271
|
+
|
|
272
|
+
### This repository
|
|
273
|
+
|
|
274
|
+
```sh
|
|
275
|
+
npm install # NODE_ENV must not be "production", or the dev tools are skipped
|
|
276
|
+
npm test # the library, the schemas, the vectors; the table checks when the tables are present
|
|
277
|
+
npm run build # dist/ (CommonJS and ESM, with types)
|
|
278
|
+
npm run verify # every method against the reference timetables
|
|
279
|
+
npm run verify -- morocco indonesia
|
|
280
|
+
npm run verify -- --alone # each method without its correction list
|
|
281
|
+
npm run corrections # rewrites Sweden's correction list (`-- diyanet`, `-- morocco`, … for the others)
|
|
282
|
+
npm run certify # rewrites certification.json — what may be used
|
|
283
|
+
npm run vectors # rewrites test/vectors.json (after a method revision)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The reference timetables are not part of this repository; point
|
|
287
|
+
`MIHRAB_TABLES` at them (default `.tables`). Without them `candidateTimes`
|
|
288
|
+
still runs, the checks and their tests skip, and `npm run certify` writes
|
|
289
|
+
nothing.
|
|
290
|
+
|
|
291
|
+
## Layout
|
|
292
|
+
|
|
293
|
+
| Path | What |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `src/methods/` | The methods: Sweden and Diyanet (`sweden.ts`), Morocco, Algeria, Indonesia; their revisions |
|
|
296
|
+
| `src/sun/preciseSun.ts` | VSOP87 apparent sun, after Astronomy Engine: within about 0.05 s and 0.5″ of JPL's DE421 |
|
|
297
|
+
| `src/core/` | The calls (`api.ts`), the computation they share (`compute.ts`), dates, clocks, the correction-list index, `fetchLatestCorrections` |
|
|
298
|
+
| `src/authorities/` | Each authority's places, at the point (and Morocco's horizon) its method is computed at, with its description |
|
|
299
|
+
| `src/index.ts`, `src/<authority>.ts`, `src/locate.ts`, `src/core.ts` | The package's entry points |
|
|
300
|
+
| `data/places/`, `data/corrections/`, `data/locate/` | The place lists and fitted points, the correction lists, the country outlines — embedded into the code by `scripts/embed.mjs` at build time |
|
|
301
|
+
| `certification.json`, `schemas/` | The 100 % gate's record, and the schemas of the files apps read |
|
|
302
|
+
| `tools/verify.ts`, `tools/certify.ts`, `tools/corrections.ts` | The day-by-day check against the timetables; the certification; the correction lists |
|
|
303
|
+
| `tools/vectors.ts`, `test/vectors.json`, `docs/methods.md` | For ports: the sample and the written method |
|
|
304
|
+
| `tools/serve.ts` | The HTTP server |
|
|
305
|
+
|
|
306
|
+
## Back into Mihrab
|
|
307
|
+
|
|
308
|
+
A method that improves here goes back as the same file in Mihrab's
|
|
309
|
+
`src/providers/` (and its points file in `src/providers/data/`). The app
|
|
310
|
+
reads `certification.json` and the lists from this repository's `main`;
|
|
311
|
+
that feed does not change shape between versions (`schemas/`).
|
|
312
|
+
|
|
313
|
+
## Licence
|
|
314
|
+
|
|
315
|
+
[MPL-2.0](LICENSE). Use the library in any app, open or closed, on a phone
|
|
316
|
+
or on a server; what the licence asks is that changes to *these files* —
|
|
317
|
+
a method fitted closer to its table, a corrected point — are published
|
|
318
|
+
under the same terms, so an improvement to a method reaches everyone. The
|
|
319
|
+
Mihrab app itself stays AGPL-3.0; it includes this library as the MPL
|
|
320
|
+
allows. Versions before 1.0.0 were AGPL-3.0-or-later.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sweden": {
|
|
3
|
+
"certified": true,
|
|
4
|
+
"checkedAt": "2026-10-16",
|
|
5
|
+
"checks": [
|
|
6
|
+
{
|
|
7
|
+
"tables": "Sweden — Islamiska Förbundet’s tables",
|
|
8
|
+
"places": 107,
|
|
9
|
+
"times": 59640,
|
|
10
|
+
"off": 0,
|
|
11
|
+
"from": "2026-10-11",
|
|
12
|
+
"to": "2027-11-19",
|
|
13
|
+
"setAside": {
|
|
14
|
+
"misplaced": [],
|
|
15
|
+
"sourceFaults": 33395,
|
|
16
|
+
"copiedRows": 646
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"tables": "Sweden — Diyanet’s own tables",
|
|
21
|
+
"places": 159,
|
|
22
|
+
"times": 1393794,
|
|
23
|
+
"off": 0,
|
|
24
|
+
"from": "2024-01-01",
|
|
25
|
+
"to": "2027-12-31"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"corrections": {
|
|
29
|
+
"from": "2024-01-01",
|
|
30
|
+
"to": "2027-12-31",
|
|
31
|
+
"entries": 270,
|
|
32
|
+
"sha256": "173d7145b5ec917882f8ac9890dd1fa5f3143bd714eb540482861bf8a6bac660"
|
|
33
|
+
},
|
|
34
|
+
"revision": 1
|
|
35
|
+
},
|
|
36
|
+
"diyanet": {
|
|
37
|
+
"certified": true,
|
|
38
|
+
"checkedAt": "2026-10-16",
|
|
39
|
+
"checks": [
|
|
40
|
+
{
|
|
41
|
+
"tables": "Diyanet — its tables worldwide",
|
|
42
|
+
"places": 8132,
|
|
43
|
+
"times": 69999603,
|
|
44
|
+
"off": 0,
|
|
45
|
+
"from": "2024-01-01",
|
|
46
|
+
"to": "2027-12-31"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"corrections": {
|
|
50
|
+
"from": "2024-01-01",
|
|
51
|
+
"to": "2027-12-31",
|
|
52
|
+
"entries": 9118,
|
|
53
|
+
"sha256": "47997767e178ce48babd80f0e4a53460c5d379eb098bff1e7ebbba1e6afdf605"
|
|
54
|
+
},
|
|
55
|
+
"revision": 1
|
|
56
|
+
},
|
|
57
|
+
"morocco": {
|
|
58
|
+
"certified": true,
|
|
59
|
+
"checkedAt": "2026-10-16",
|
|
60
|
+
"checks": [
|
|
61
|
+
{
|
|
62
|
+
"tables": "Morocco — Habous",
|
|
63
|
+
"places": 164,
|
|
64
|
+
"times": 75768,
|
|
65
|
+
"off": 0,
|
|
66
|
+
"from": "2026-08-27",
|
|
67
|
+
"to": "2026-11-11"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"corrections": {
|
|
71
|
+
"from": "2026-08-27",
|
|
72
|
+
"to": "2026-11-11",
|
|
73
|
+
"entries": 665,
|
|
74
|
+
"sha256": "26ccb0979ee3e632c5acd736fdba3f8272530b48577cb1c0f2897d061e695bd0"
|
|
75
|
+
},
|
|
76
|
+
"revision": 1
|
|
77
|
+
},
|
|
78
|
+
"algeria": {
|
|
79
|
+
"certified": true,
|
|
80
|
+
"checkedAt": "2026-10-16",
|
|
81
|
+
"checks": [
|
|
82
|
+
{
|
|
83
|
+
"tables": "Algeria — Ministry of Religious Affairs",
|
|
84
|
+
"places": 68,
|
|
85
|
+
"times": 101184,
|
|
86
|
+
"off": 0,
|
|
87
|
+
"from": "2026-10-01",
|
|
88
|
+
"to": "2027-06-05"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"corrections": {
|
|
92
|
+
"from": "2026-10-01",
|
|
93
|
+
"to": "2027-06-05",
|
|
94
|
+
"entries": 642,
|
|
95
|
+
"sha256": "f8d227cdd88aabe5bcd1775ec0324abfdae07a8a3986fdd19816337c2d4ab3ca"
|
|
96
|
+
},
|
|
97
|
+
"revision": 1
|
|
98
|
+
},
|
|
99
|
+
"indonesia": {
|
|
100
|
+
"certified": true,
|
|
101
|
+
"checkedAt": "2026-10-16",
|
|
102
|
+
"checks": [
|
|
103
|
+
{
|
|
104
|
+
"tables": "Indonesia — Kemenag",
|
|
105
|
+
"places": 517,
|
|
106
|
+
"times": 266772,
|
|
107
|
+
"off": 0,
|
|
108
|
+
"from": "2026-10-07",
|
|
109
|
+
"to": "2026-12-31"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"corrections": {
|
|
113
|
+
"from": "2026-10-07",
|
|
114
|
+
"to": "2026-12-31",
|
|
115
|
+
"entries": 84,
|
|
116
|
+
"sha256": "7c7d3b09a5b01a4d77c605ad8a37fbdac4fd3ef65df483332d75c67891b2359e"
|
|
117
|
+
},
|
|
118
|
+
"revision": 1
|
|
119
|
+
}
|
|
120
|
+
}
|