mihrab-methods 1.4.0 → 1.5.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.
Files changed (44) hide show
  1. package/README.md +3 -2
  2. package/certification.json +23 -0
  3. package/data/corrections/kazakhstan.json +5 -0
  4. package/dist/cjs/authorities/kazakhstan.d.ts +3 -0
  5. package/dist/cjs/authorities/kazakhstan.js +49 -0
  6. package/dist/cjs/core/compute.js +8 -4
  7. package/dist/cjs/core/types.d.ts +1 -1
  8. package/dist/cjs/core/types.js +1 -1
  9. package/dist/cjs/data/generated/kazakhstanCertification.d.ts +3 -0
  10. package/dist/cjs/data/generated/kazakhstanCertification.js +4 -0
  11. package/dist/cjs/data/generated/kazakhstanCities.d.ts +7 -0
  12. package/dist/cjs/data/generated/kazakhstanCities.js +5 -0
  13. package/dist/cjs/data/generated/kazakhstanCorrections.d.ts +3 -0
  14. package/dist/cjs/data/generated/kazakhstanCorrections.js +4 -0
  15. package/dist/cjs/index.d.ts +1 -0
  16. package/dist/cjs/index.js +1 -0
  17. package/dist/cjs/kazakhstan.d.ts +4 -0
  18. package/dist/cjs/kazakhstan.js +22 -0
  19. package/dist/cjs/methods/kazakhstan.d.ts +58 -0
  20. package/dist/cjs/methods/kazakhstan.js +129 -0
  21. package/dist/cjs/methods/revisions.d.ts +1 -0
  22. package/dist/cjs/methods/revisions.js +1 -0
  23. package/dist/esm/authorities/kazakhstan.d.ts +3 -0
  24. package/dist/esm/authorities/kazakhstan.js +43 -0
  25. package/dist/esm/core/compute.js +8 -4
  26. package/dist/esm/core/types.d.ts +1 -1
  27. package/dist/esm/core/types.js +1 -1
  28. package/dist/esm/data/generated/kazakhstanCertification.d.ts +3 -0
  29. package/dist/esm/data/generated/kazakhstanCertification.js +2 -0
  30. package/dist/esm/data/generated/kazakhstanCities.d.ts +7 -0
  31. package/dist/esm/data/generated/kazakhstanCities.js +3 -0
  32. package/dist/esm/data/generated/kazakhstanCorrections.d.ts +3 -0
  33. package/dist/esm/data/generated/kazakhstanCorrections.js +2 -0
  34. package/dist/esm/index.d.ts +1 -0
  35. package/dist/esm/index.js +1 -0
  36. package/dist/esm/kazakhstan.d.ts +4 -0
  37. package/dist/esm/kazakhstan.js +4 -0
  38. package/dist/esm/methods/kazakhstan.d.ts +58 -0
  39. package/dist/esm/methods/kazakhstan.js +124 -0
  40. package/dist/esm/methods/revisions.d.ts +1 -0
  41. package/dist/esm/methods/revisions.js +1 -0
  42. package/docs/methods.md +29 -0
  43. package/package.json +17 -2
  44. package/schemas/certification.schema.json +2 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  **Prayer times that match the published timetable to the printed minute.**
10
10
  Each national authority's own calculation method, worked out again from its
11
11
  published times and checked day by day against its timetables: <!--fig:total.off-->0<!--/fig--> of
12
- <!--fig:total.times-->75,458,222<!--/fig--> times off the printed minute. Free for any app, site or
12
+ <!--fig:total.times-->76,010,102<!--/fig--> times off the printed minute. Free for any app, site or
13
13
  mosque to use.
14
14
 
15
15
  [![Usage, a day: npm downloads and API requests](docs/stats.svg)](#usage)
@@ -44,8 +44,9 @@ The full API, the HTTP server and the per-authority imports are in
44
44
  | **Algeria** | Ministry of Religious Affairs | 68 | 101,184 | **0** | 99.365 % | 642 | 1 Oct 2026 → 5 Jun 2027 |
45
45
  | **Indonesia** | Kemenag | 517 | 362,934 | **0** | 99.935 % | 234 | 7 Oct 2026 → 31 Jan 2027 |
46
46
  | **Türkiye Takvimi** | its cities’ years | 821 | 1,812,768 | **0** | 99.952 % | 860 | 31 Dec 2025 → 2 Jan 2027 |
47
+ | **Kazakhstan** | the Spiritual Administration’s times | 84 | 551,880 | **0** | 100.000 % | 0 | 1 Jan 2025 → 31 Dec 2027 |
47
48
 
48
- 6 of 6 methods certified; 0 of 75,458,222 times off the printed minute. Last checked 9 November 2026. Generated from [`certification.json`](certification.json) by `scripts/readme.mjs`.
49
+ 7 of 7 methods certified; 0 of 76,010,102 times off the printed minute. Last checked 9 November 2026. Generated from [`certification.json`](certification.json) by `scripts/readme.mjs`.
49
50
  <!-- certification-table:end -->
50
51
 
51
52
  "By the method alone" is the formula without its correction list. The list
@@ -150,5 +150,28 @@
150
150
  "sha256": "edf5bd37ccd06dbcb87300a6c943353adb624a545ff6ea416a8f89715713d7bd"
151
151
  },
152
152
  "revision": 1
153
+ },
154
+ "kazakhstan": {
155
+ "certified": true,
156
+ "checkedAt": "2026-11-11",
157
+ "places": 84,
158
+ "checks": [
159
+ {
160
+ "tables": "Kazakhstan — the Spiritual Administration’s times",
161
+ "places": 84,
162
+ "times": 551880,
163
+ "off": 0,
164
+ "offWithoutCorrections": 0,
165
+ "from": "2025-01-01",
166
+ "to": "2027-12-31"
167
+ }
168
+ ],
169
+ "corrections": {
170
+ "from": "2025-01-01",
171
+ "to": "2027-12-31",
172
+ "entries": 0,
173
+ "sha256": "e3d60e566ca082f74ce9904886087b29e65d2871c5bcc02f46349d82c8994d88"
174
+ },
175
+ "revision": 1
153
176
  }
154
177
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "from": "2025-01-01",
3
+ "to": "2027-12-31",
4
+ "points": {}
5
+ }
@@ -0,0 +1,3 @@
1
+ import type { Place } from '../core/types.js';
2
+ export declare const kazakhstan: import("../core/registry.js").AuthorityData;
3
+ export declare const places: readonly Place[];
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.places = exports.kazakhstan = void 0;
7
+ /**
8
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan: its
9
+ * cities, each at the coordinates the Administration computes it at.
10
+ */
11
+ const kazakhstanCities_js_1 = __importDefault(require("../data/generated/kazakhstanCities.js"));
12
+ const kazakhstanCorrections_js_1 = __importDefault(require("../data/generated/kazakhstanCorrections.js"));
13
+ const kazakhstanCertification_js_1 = __importDefault(require("../data/generated/kazakhstanCertification.js"));
14
+ const revisions_js_1 = require("../methods/revisions.js");
15
+ const registry_js_1 = require("../core/registry.js");
16
+ const AUTHORITY = 'kazakhstan';
17
+ /** The whole country keeps UTC+5 (since 1 March 2024), and the Administration prints every place on it. */
18
+ const TZ = 'Asia/Almaty';
19
+ function buildPlaces() {
20
+ return kazakhstanCities_js_1.default.map(c => {
21
+ const key = String(c.id);
22
+ return { authority: AUTHORITY, id: `${AUTHORITY}:${key}`, key, name: c.name, country: 'KZ', lat: c.lat, lon: c.lon, tz: TZ };
23
+ });
24
+ }
25
+ exports.kazakhstan = (0, registry_js_1.registerAuthority)({
26
+ authority: AUTHORITY,
27
+ revision: revisions_js_1.METHOD_REVISION.kazakhstan,
28
+ places: buildPlaces(),
29
+ corrections: kazakhstanCorrections_js_1.default,
30
+ certification: kazakhstanCertification_js_1.default,
31
+ description: {
32
+ name: 'Kazakhstan — Spiritual Administration of Muslims of Kazakhstan',
33
+ method: 'A short series for the sun, each time computed once from a fixed first guess of its hour; Fajr and Isha at 15°, capped at a quarter of the night from sunrise and sunset; sunrise and sunset at −0.833°; Asr at two shadow lengths (Hanafi); a margin of 3 minutes (5 from 48° N) before sunrise, after the transit, on Asr and on Maghrib after sunset; every time rounded to the nearest minute.',
34
+ countries: ['KZ'],
35
+ parameters: {
36
+ fajrAngle: 15,
37
+ ishaAngle: 15,
38
+ sunriseAltitude: -0.833,
39
+ asrShadow: 2,
40
+ highLatitude: 'a quarter of the night, where the angle is further or not reached',
41
+ marginMinutes: '3; 5 from 48° N — sunrise earlier, Dhuhr, Asr and Maghrib later',
42
+ rounding: 'to the nearest minute',
43
+ sun: 'low-precision series (mean elements linear in time, two terms of the equation of the centre), not iterated',
44
+ },
45
+ sources: [{ title: 'Spiritual Administration of Muslims of Kazakhstan — намаз уақыттары', url: 'https://namaz.muftyat.kz/' }],
46
+ spec: 'docs/methods.md#kazakhstan',
47
+ },
48
+ });
49
+ exports.places = exports.kazakhstan.places;
@@ -12,6 +12,7 @@ const morocco_js_1 = require("../methods/morocco.js");
12
12
  const algeria_js_1 = require("../methods/algeria.js");
13
13
  const indonesia_js_1 = require("../methods/indonesia.js");
14
14
  const turkiyeTakvimi_js_1 = require("../methods/turkiyeTakvimi.js");
15
+ const kazakhstan_js_1 = require("../methods/kazakhstan.js");
15
16
  const registry_js_1 = require("./registry.js");
16
17
  const clock_js_1 = require("./clock.js");
17
18
  /**
@@ -35,14 +36,17 @@ function candidateInstants(place, year, month, day, asrShadow = 1, opts = {}) {
35
36
  case 'morocco':
36
37
  case 'algeria':
37
38
  case 'indonesia':
38
- case 'turkiyetakvimi': {
39
+ case 'turkiyetakvimi':
40
+ case 'kazakhstan': {
39
41
  const t = place.authority === 'morocco'
40
42
  ? (0, morocco_js_1.moroccoTimes)(place.lat, place.lon, year, month, day, place.dipDeg, asrShadow, place.dipMaghribDeg)
41
43
  : place.authority === 'algeria'
42
44
  ? (0, algeria_js_1.algeriaTimes)(place.lat, place.lon, year, month, day, asrShadow)
43
- : place.authority === 'turkiyetakvimi'
44
- ? (0, turkiyeTakvimi_js_1.takvimTimes)(place.lat, place.lon, year, month, day, place.temkin, asrShadow)
45
- : (0, indonesia_js_1.kemenagTimes)(place.lat, place.lon, year, month, day, { highland: place.highland, twilightDip: place.twilightDip, asrShadow, afternoon: place.afternoon });
45
+ : place.authority === 'kazakhstan'
46
+ ? (0, kazakhstan_js_1.kazakhstanTimes)(place.lat, place.lon, year, month, day)
47
+ : place.authority === 'turkiyetakvimi'
48
+ ? (0, turkiyeTakvimi_js_1.takvimTimes)(place.lat, place.lon, year, month, day, place.temkin, asrShadow)
49
+ : (0, indonesia_js_1.kemenagTimes)(place.lat, place.lon, year, month, day, { highland: place.highland, twilightDip: place.twilightDip, asrShadow, afternoon: place.afternoon });
46
50
  // the correction list: the authority's few times on the other side of a minute (the standard Asr only)
47
51
  const c = !t || !fix ? undefined : fix.correction(place.lat, place.lon, year, month, day);
48
52
  if (!t || !c)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The shapes the library speaks in. Nothing here computes anything.
3
3
  */
4
- export type Authority = 'sweden' | 'diyanet' | 'morocco' | 'algeria' | 'indonesia' | 'turkiyetakvimi';
4
+ export type Authority = 'sweden' | 'diyanet' | 'morocco' | 'algeria' | 'indonesia' | 'turkiyetakvimi' | 'kazakhstan';
5
5
  export declare const AUTHORITIES: readonly Authority[];
6
6
  export type PrayerKey = 'fajr' | 'sunrise' | 'dhuhr' | 'asr' | 'maghrib' | 'isha';
7
7
  export declare const PRAYER_KEYS: readonly PrayerKey[];
@@ -4,7 +4,7 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.UncertifiedMethodError = exports.PRAYER_KEYS = exports.AUTHORITIES = void 0;
7
- exports.AUTHORITIES = ['sweden', 'diyanet', 'morocco', 'algeria', 'indonesia', 'turkiyetakvimi'];
7
+ exports.AUTHORITIES = ['sweden', 'diyanet', 'morocco', 'algeria', 'indonesia', 'turkiyetakvimi', 'kazakhstan'];
8
8
  exports.PRAYER_KEYS = ['fajr', 'sunrise', 'dhuhr', 'asr', 'maghrib', 'isha'];
9
9
  /** The method has not reproduced its authority's tables to the minute (or not for this date). */
10
10
  class UncertifiedMethodError extends Error {
@@ -0,0 +1,3 @@
1
+ import type { Certification } from '../../core/types.js';
2
+ declare const data: Certification | null;
3
+ export default data;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const data = { "certified": true, "checkedAt": "2026-11-11", "places": 84, "checks": [{ "tables": "Kazakhstan — the Spiritual Administration’s times", "places": 84, "times": 551880, "off": 0, "offWithoutCorrections": 0, "from": "2025-01-01", "to": "2027-12-31" }], "corrections": { "from": "2025-01-01", "to": "2027-12-31", "entries": 0, "sha256": "e3d60e566ca082f74ce9904886087b29e65d2871c5bcc02f46349d82c8994d88" }, "revision": 1 };
4
+ exports.default = data;
@@ -0,0 +1,7 @@
1
+ declare const data: {
2
+ id: number;
3
+ name: string;
4
+ lat: number;
5
+ lon: number;
6
+ }[];
7
+ export default data;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Generated by scripts/embed.mjs — do not edit; edit the JSON under data/ instead.
4
+ const data = [{ "id": 2, "name": "Қонаев", "lat": 43.883333, "lon": 77.083333 }, { "id": 3, "name": "Астана", "lat": 51.133333, "lon": 71.433333 }, { "id": 7, "name": "Орал", "lat": 51.204019, "lon": 51.370537 }, { "id": 17, "name": "Көкшетау", "lat": 53.291667, "lon": 69.391667 }, { "id": 18, "name": "Атырау", "lat": 47.116667, "lon": 51.883333 }, { "id": 20, "name": "Ақтөбе", "lat": 50.300377, "lon": 57.154555 }, { "id": 21, "name": "Степногорск", "lat": 52.346944, "lon": 71.881667 }, { "id": 22, "name": "Үшарал", "lat": 46.169722, "lon": 80.939444 }, { "id": 23, "name": "Атбасар", "lat": 51.815761, "lon": 68.358335 }, { "id": 24, "name": "Қандыағаш", "lat": 49.474444, "lon": 57.423333 }, { "id": 25, "name": "Хромтау", "lat": 50.250278, "lon": 58.434722 }, { "id": 26, "name": "Шалқар", "lat": 47.831392, "lon": 59.61929 }, { "id": 27, "name": "Құлсары", "lat": 46.983333, "lon": 54.016667 }, { "id": 29, "name": "Аягөз", "lat": 47.966667, "lon": 80.433333 }, { "id": 30, "name": "Өскемен", "lat": 49.948325, "lon": 82.627848 }, { "id": 31, "name": "Риддер", "lat": 50.33886, "lon": 83.506329 }, { "id": 32, "name": "Семей", "lat": 50.404976, "lon": 80.249235 }, { "id": 33, "name": "Жаңатас", "lat": 43.55465, "lon": 69.722525 }, { "id": 34, "name": "Қаратау", "lat": 43.166667, "lon": 70.466667 }, { "id": 35, "name": "Тараз", "lat": 42.883333, "lon": 71.366667 }, { "id": 36, "name": "Балқаш", "lat": 46.843721, "lon": 74.977301 }, { "id": 38, "name": "Жезқазған", "lat": 47.799711, "lon": 67.71409 }, { "id": 39, "name": "Қарағанды", "lat": 49.806406, "lon": 73.085485 }, { "id": 40, "name": "Сәтпаев", "lat": 47.907455, "lon": 67.528112 }, { "id": 41, "name": "Арқалық", "lat": 50.248611, "lon": 66.911389 }, { "id": 42, "name": "Қостанай", "lat": 53.219333, "lon": 63.634194 }, { "id": 43, "name": "Рудный", "lat": 52.966667, "lon": 63.116667 }, { "id": 46, "name": "Қызылорда", "lat": 44.842544, "lon": 65.502563 }, { "id": 47, "name": "Ақтау", "lat": 43.635379, "lon": 51.169135 }, { "id": 49, "name": "Форт-Шевченко", "lat": 44.507463, "lon": 50.262833 }, { "id": 50, "name": "Жаңаөзен", "lat": 43.343266, "lon": 52.865792 }, { "id": 51, "name": "Петропавл", "lat": 54.862222, "lon": 69.140833 }, { "id": 52, "name": "Екібастұз", "lat": 51.729778, "lon": 75.326583 }, { "id": 53, "name": "Павлодар", "lat": 52.315556, "lon": 76.956389 }, { "id": 54, "name": "Жетісай", "lat": 40.775278, "lon": 68.327222 }, { "id": 55, "name": "Сарыағаш", "lat": 41.466667, "lon": 69.166667 }, { "id": 56, "name": "Шардара", "lat": 41.254722, "lon": 67.969167 }, { "id": 57, "name": "Шымкент", "lat": 42.368009, "lon": 69.612769 }, { "id": 58, "name": "Түркістан", "lat": 43.302025, "lon": 68.268979 }, { "id": 59, "name": "Ақсай", "lat": 51.164945, "lon": 53.020868 }, { "id": 69, "name": "Лисаков", "lat": 52.544079, "lon": 62.492641 }, { "id": 70, "name": "Жаркент", "lat": 44.169365, "lon": 80.003842 }, { "id": 71, "name": "Арал", "lat": 46.797738, "lon": 61.660792 }, { "id": 72, "name": "Алматы", "lat": 43.238293, "lon": 76.945465 }, { "id": 73, "name": "Талғар", "lat": 43.302813, "lon": 77.23969 }, { "id": 75, "name": "Қаскелең", "lat": 43.202449, "lon": 76.622278 }, { "id": 77, "name": "Шу", "lat": 43.611782, "lon": 73.760237 }, { "id": 79, "name": "Қаражал", "lat": 48.001042, "lon": 70.785762 }, { "id": 82, "name": "Ленгер", "lat": 42.182051, "lon": 69.88212 }, { "id": 93, "name": "Ембі", "lat": 48.823344, "lon": 58.148397 }, { "id": 95, "name": "Зайсан", "lat": 47.453008, "lon": 84.969846 }, { "id": 100, "name": "Кентау", "lat": 43.518131, "lon": 68.504652 }, { "id": 103, "name": "Тайынша", "lat": 53.852738, "lon": 69.782631 }, { "id": 109, "name": "Теміртау", "lat": 50.058756, "lon": 72.953424 }, { "id": 112, "name": "Алға", "lat": 49.89673, "lon": 57.328586 }, { "id": 113, "name": "Ақсу", "lat": 52.03789, "lon": 76.920582 }, { "id": 128, "name": "Курчатов", "lat": 50.7549, "lon": 78.544826 }, { "id": 134, "name": "Арыс", "lat": 42.432744, "lon": 68.813798 }, { "id": 137, "name": "Қосшы", "lat": 50.970262, "lon": 71.355366 }, { "id": 143, "name": "Текелі", "lat": 44.863094, "lon": 78.764266 }, { "id": 146, "name": "Қарқаралы", "lat": 49.41224, "lon": 75.470095 }, { "id": 149, "name": "Шар", "lat": 49.584964, "lon": 81.047173 }, { "id": 150, "name": "Ақкөл", "lat": 51.995855, "lon": 70.952948 }, { "id": 151, "name": "Державинск", "lat": 51.098685, "lon": 66.314712 }, { "id": 152, "name": "Ерейментау", "lat": 51.619626, "lon": 73.102598 }, { "id": 153, "name": "Есіл", "lat": 51.958004, "lon": 66.406215 }, { "id": 154, "name": "Макинск", "lat": 52.63281, "lon": 70.417677 }, { "id": 155, "name": "Степняк", "lat": 52.832, "lon": 70.783427 }, { "id": 157, "name": "Жем", "lat": 48.774532, "lon": 58.072436 }, { "id": 158, "name": "Темір", "lat": 49.143142, "lon": 57.12633 }, { "id": 159, "name": "Алатау", "lat": 43.681261, "lon": 77.116046 }, { "id": 161, "name": "Сарқан", "lat": 45.413666, "lon": 79.916894 }, { "id": 162, "name": "Талдықорған", "lat": 45.017837, "lon": 78.382123 }, { "id": 163, "name": "Үштөбе", "lat": 45.251221, "lon": 77.980827 }, { "id": 165, "name": "Приозёрск", "lat": 46.028969, "lon": 73.708027 }, { "id": 166, "name": "Саран", "lat": 49.801993, "lon": 72.828387 }, { "id": 167, "name": "Шахтинск", "lat": 49.705868, "lon": 72.594637 }, { "id": 170, "name": "Қазалы", "lat": 45.760633, "lon": 62.105836 }, { "id": 171, "name": "Булаев", "lat": 54.896056, "lon": 70.448157 }, { "id": 172, "name": "Мамлют", "lat": 54.94112, "lon": 68.546199 }, { "id": 173, "name": "Сергеев", "lat": 53.888542, "lon": 67.416271 }, { "id": 174, "name": "Алтай", "lat": 49.725218, "lon": 84.273562 }, { "id": 175, "name": "Серебрянск", "lat": 49.679277, "lon": 83.300651 }, { "id": 176, "name": "Шемонаиха", "lat": 50.627624, "lon": 81.916697 }];
5
+ exports.default = data;
@@ -0,0 +1,3 @@
1
+ import type { CorrectionsFile } from '../../core/types.js';
2
+ declare const data: CorrectionsFile;
3
+ export default data;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const data = { "from": "2025-01-01", "to": "2027-12-31", "points": {} };
4
+ exports.default = data;
@@ -16,5 +16,6 @@ import './authorities/morocco.js';
16
16
  import './authorities/algeria.js';
17
17
  import './authorities/indonesia.js';
18
18
  import './authorities/turkiyeTakvimi.js';
19
+ import './authorities/kazakhstan.js';
19
20
  export * from './core/index.js';
20
21
  export { diyanetCountry } from './authorities/diyanet.js';
package/dist/cjs/index.js CHANGED
@@ -33,6 +33,7 @@ require("./authorities/morocco.js");
33
33
  require("./authorities/algeria.js");
34
34
  require("./authorities/indonesia.js");
35
35
  require("./authorities/turkiyeTakvimi.js");
36
+ require("./authorities/kazakhstan.js");
36
37
  __exportStar(require("./core/index.js"), exports);
37
38
  var diyanet_js_1 = require("./authorities/diyanet.js");
38
39
  Object.defineProperty(exports, "diyanetCountry", { enumerable: true, get: function () { return diyanet_js_1.diyanetCountry; } });
@@ -0,0 +1,4 @@
1
+ /** mihrab-methods/kazakhstan — the Kazakhstan method with its places and correction list, and nothing else's. */
2
+ import './authorities/kazakhstan.js';
3
+ export * from './core/index.js';
4
+ export { places } from './authorities/kazakhstan.js';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.places = void 0;
18
+ /** mihrab-methods/kazakhstan — the Kazakhstan method with its places and correction list, and nothing else's. */
19
+ require("./authorities/kazakhstan.js");
20
+ __exportStar(require("./core/index.js"), exports);
21
+ var kazakhstan_js_1 = require("./authorities/kazakhstan.js");
22
+ Object.defineProperty(exports, "places", { enumerable: true, get: function () { return kazakhstan_js_1.places; } });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan's
3
+ * method, as the times it publishes for a place are computed.
4
+ *
5
+ * Read off its published year for places from 40.8° to 54.9° N:
6
+ *
7
+ * • a short series for the sun: mean anomaly g and mean longitude q linear
8
+ * in the days D from J2000.0, the ecliptic longitude
9
+ * L = q + 1.915° sin g + 0.020° sin 2g, the obliquity 23.439° − 0.00000036° D,
10
+ * and from them the declination and the equation of time;
11
+ * • the day counted from 0h of the civil date at the place's own meridian
12
+ * (the Julian day of the date less longitude/360);
13
+ * • each time computed once, with the sun taken at a fixed first guess of
14
+ * its hour (Fajr 5h, sunrise 6h, Dhuhr 12h, Asr 13h, sunset and Isha
15
+ * 18h), not iterated;
16
+ * • Fajr and Isha at 15°; sunrise and sunset at −0.833°; Asr at two shadow
17
+ * lengths beyond the noon shadow (Hanafi);
18
+ * • where Fajr would fall more than a quarter of the night before sunrise,
19
+ * or Isha more than a quarter of the night after sunset — or the sun does
20
+ * not reach 15° — the quarter of the night is taken (the night from
21
+ * sunset to sunrise);
22
+ * • a margin of 3 minutes, 5 minutes from 48° N: sunrise that much
23
+ * earlier, Dhuhr and Asr that much later, Maghrib that much after
24
+ * sunset; none on Fajr or Isha;
25
+ * • every time rounded to the nearest minute.
26
+ *
27
+ * With that the method gives every published time of the places read, with
28
+ * no correction list.
29
+ */
30
+ export type KazakhstanInstants = {
31
+ fajr: Date;
32
+ sunrise: Date;
33
+ dhuhr: Date;
34
+ asr: Date;
35
+ maghrib: Date;
36
+ isha: Date;
37
+ };
38
+ /** The margin at a latitude, minutes. */
39
+ export declare function kazakhstanMargin(latitude: number): number;
40
+ /**
41
+ * The times before rounding, minutes past 00:00 UT of the civil date (month
42
+ * 0-based), and the sunset the method prints beside Maghrib.
43
+ */
44
+ export declare function kazakhstanRawMinutes(latitude: number, longitude: number, year: number, month: number, day: number): {
45
+ fajr: number;
46
+ sunrise: number;
47
+ dhuhr: number;
48
+ asr: number;
49
+ sunset: number;
50
+ maghrib: number;
51
+ isha: number;
52
+ };
53
+ /**
54
+ * The Administration's times for the civil date (month 0-based) at the
55
+ * point, each on the minute it prints (rounded to the nearest); Asr is the
56
+ * Hanafi one its table carries. Null where a time does not form.
57
+ */
58
+ export declare function kazakhstanTimes(latitude: number, longitude: number, year: number, month: number, day: number): KazakhstanInstants | null;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ /**
3
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan's
4
+ * method, as the times it publishes for a place are computed.
5
+ *
6
+ * Read off its published year for places from 40.8° to 54.9° N:
7
+ *
8
+ * • a short series for the sun: mean anomaly g and mean longitude q linear
9
+ * in the days D from J2000.0, the ecliptic longitude
10
+ * L = q + 1.915° sin g + 0.020° sin 2g, the obliquity 23.439° − 0.00000036° D,
11
+ * and from them the declination and the equation of time;
12
+ * • the day counted from 0h of the civil date at the place's own meridian
13
+ * (the Julian day of the date less longitude/360);
14
+ * • each time computed once, with the sun taken at a fixed first guess of
15
+ * its hour (Fajr 5h, sunrise 6h, Dhuhr 12h, Asr 13h, sunset and Isha
16
+ * 18h), not iterated;
17
+ * • Fajr and Isha at 15°; sunrise and sunset at −0.833°; Asr at two shadow
18
+ * lengths beyond the noon shadow (Hanafi);
19
+ * • where Fajr would fall more than a quarter of the night before sunrise,
20
+ * or Isha more than a quarter of the night after sunset — or the sun does
21
+ * not reach 15° — the quarter of the night is taken (the night from
22
+ * sunset to sunrise);
23
+ * • a margin of 3 minutes, 5 minutes from 48° N: sunrise that much
24
+ * earlier, Dhuhr and Asr that much later, Maghrib that much after
25
+ * sunset; none on Fajr or Isha;
26
+ * • every time rounded to the nearest minute.
27
+ *
28
+ * With that the method gives every published time of the places read, with
29
+ * no correction list.
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.kazakhstanMargin = kazakhstanMargin;
33
+ exports.kazakhstanRawMinutes = kazakhstanRawMinutes;
34
+ exports.kazakhstanTimes = kazakhstanTimes;
35
+ const RAD = Math.PI / 180;
36
+ const sin = (d) => Math.sin(d * RAD);
37
+ const cos = (d) => Math.cos(d * RAD);
38
+ const tan = (d) => Math.tan(d * RAD);
39
+ /** a − b·⌊a/b⌋: into [0, b). */
40
+ const fix = (a, b) => a - b * Math.floor(a / b);
41
+ /** Fajr's and Isha's depression, degrees. */
42
+ const TWILIGHT = 15;
43
+ const HORIZON = 0.833;
44
+ /** Shadow lengths beyond the noon shadow at Asr. */
45
+ const ASR_SHADOW = 2;
46
+ /** The latitude from which the margin is five minutes, not three. */
47
+ const NORTH = 48;
48
+ /** The first guess of each time's hour, at which its sun is taken. */
49
+ const GUESS = { fajr: 5, sunrise: 6, dhuhr: 12, asr: 13, sunset: 18, isha: 18 };
50
+ /** The Julian day of 0h UT of a civil date (month 1-based). */
51
+ function julian(year, month, day) {
52
+ if (month <= 2) {
53
+ year -= 1;
54
+ month += 12;
55
+ }
56
+ const a = Math.floor(year / 100);
57
+ const b = 2 - a + Math.floor(a / 4);
58
+ return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5;
59
+ }
60
+ /** The method's sun at a Julian day: declination (degrees), equation of time (hours). */
61
+ function sunAt(jd) {
62
+ const D = jd - 2451545.0;
63
+ const g = fix(357.529 + 0.98560028 * D, 360);
64
+ const q = fix(280.459 + 0.98564736 * D, 360);
65
+ const L = fix(q + 1.915 * sin(g) + 0.02 * sin(2 * g), 360);
66
+ const e = 23.439 - 0.00000036 * D;
67
+ const ra = Math.atan2(cos(e) * sin(L), cos(L)) / RAD / 15;
68
+ return { decl: Math.asin(sin(e) * sin(L)) / RAD, eqt: q / 15 - fix(ra, 24) };
69
+ }
70
+ /** The margin at a latitude, minutes. */
71
+ function kazakhstanMargin(latitude) {
72
+ return latitude >= NORTH ? 5 : 3;
73
+ }
74
+ /**
75
+ * The times before rounding, minutes past 00:00 UT of the civil date (month
76
+ * 0-based), and the sunset the method prints beside Maghrib.
77
+ */
78
+ function kazakhstanRawMinutes(latitude, longitude, year, month, day) {
79
+ const jd = julian(year, month + 1, day) - longitude / 360;
80
+ const midday = (t) => fix(12 - sunAt(jd + t).eqt, 24);
81
+ /** Hours (local mean time) at which the sun is `angle` degrees below the horizon; NaN where it is not. */
82
+ const at = (angle, t, before) => {
83
+ const { decl } = sunAt(jd + t);
84
+ const x = (-sin(angle) - sin(decl) * sin(latitude)) / (cos(decl) * cos(latitude));
85
+ if (!(Math.abs(x) <= 1))
86
+ return NaN;
87
+ const h = Math.acos(x) / RAD / 15;
88
+ return midday(t) + (before ? -h : h);
89
+ };
90
+ const asrAngle = (t) => -Math.atan(1 / (ASR_SHADOW + tan(Math.abs(latitude - sunAt(jd + t).decl)))) / RAD;
91
+ let fajr = at(TWILIGHT, GUESS.fajr / 24, true);
92
+ const sunrise = at(HORIZON, GUESS.sunrise / 24, true);
93
+ const dhuhr = midday(GUESS.dhuhr / 24);
94
+ const asr = at(asrAngle(GUESS.asr / 24), GUESS.asr / 24, false);
95
+ const sunset = at(HORIZON, GUESS.sunset / 24, false);
96
+ let isha = at(TWILIGHT, GUESS.isha / 24, false);
97
+ // the quarter of the night, where the angle is further than that or is not reached
98
+ const portion = (TWILIGHT / 60) * fix(sunrise - sunset, 24);
99
+ if (!Number.isFinite(fajr) || fix(sunrise - fajr, 24) > portion)
100
+ fajr = sunrise - portion;
101
+ if (!Number.isFinite(isha) || fix(isha - sunset, 24) > portion)
102
+ isha = sunset + portion;
103
+ const margin = kazakhstanMargin(latitude);
104
+ // local mean time (hours) to minutes past 00:00 UT of the date
105
+ const ut = (h) => (h - longitude / 15) * 60;
106
+ return {
107
+ fajr: ut(fajr),
108
+ sunrise: ut(sunrise) - margin,
109
+ dhuhr: ut(dhuhr) + margin,
110
+ asr: ut(asr) + margin,
111
+ sunset: ut(sunset),
112
+ maghrib: ut(sunset) + margin,
113
+ isha: ut(isha),
114
+ };
115
+ }
116
+ /**
117
+ * The Administration's times for the civil date (month 0-based) at the
118
+ * point, each on the minute it prints (rounded to the nearest); Asr is the
119
+ * Hanafi one its table carries. Null where a time does not form.
120
+ */
121
+ function kazakhstanTimes(latitude, longitude, year, month, day) {
122
+ const r = kazakhstanRawMinutes(latitude, longitude, year, month, day);
123
+ const all = [r.fajr, r.sunrise, r.dhuhr, r.asr, r.maghrib, r.isha];
124
+ if (!all.every(Number.isFinite))
125
+ return null;
126
+ const start = Date.UTC(year, month, day);
127
+ const at = (m) => new Date(start + Math.floor(m + 0.5) * 60_000);
128
+ return { fajr: at(r.fajr), sunrise: at(r.sunrise), dhuhr: at(r.dhuhr), asr: at(r.asr), maghrib: at(r.maghrib), isha: at(r.isha) };
129
+ }
@@ -13,4 +13,5 @@ export declare const METHOD_REVISION: {
13
13
  readonly algeria: 1;
14
14
  readonly indonesia: 2;
15
15
  readonly turkiyetakvimi: 1;
16
+ readonly kazakhstan: 1;
16
17
  };
@@ -16,4 +16,5 @@ exports.METHOD_REVISION = {
16
16
  algeria: 1,
17
17
  indonesia: 2,
18
18
  turkiyetakvimi: 1,
19
+ kazakhstan: 1,
19
20
  };
@@ -0,0 +1,3 @@
1
+ import type { Place } from '../core/types.js';
2
+ export declare const kazakhstan: import("../core/registry.js").AuthorityData;
3
+ export declare const places: readonly Place[];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan: its
3
+ * cities, each at the coordinates the Administration computes it at.
4
+ */
5
+ import cities from '../data/generated/kazakhstanCities.js';
6
+ import corrections from '../data/generated/kazakhstanCorrections.js';
7
+ import certification from '../data/generated/kazakhstanCertification.js';
8
+ import { METHOD_REVISION } from '../methods/revisions.js';
9
+ import { registerAuthority } from '../core/registry.js';
10
+ const AUTHORITY = 'kazakhstan';
11
+ /** The whole country keeps UTC+5 (since 1 March 2024), and the Administration prints every place on it. */
12
+ const TZ = 'Asia/Almaty';
13
+ function buildPlaces() {
14
+ return cities.map(c => {
15
+ const key = String(c.id);
16
+ return { authority: AUTHORITY, id: `${AUTHORITY}:${key}`, key, name: c.name, country: 'KZ', lat: c.lat, lon: c.lon, tz: TZ };
17
+ });
18
+ }
19
+ export const kazakhstan = registerAuthority({
20
+ authority: AUTHORITY,
21
+ revision: METHOD_REVISION.kazakhstan,
22
+ places: buildPlaces(),
23
+ corrections,
24
+ certification,
25
+ description: {
26
+ name: 'Kazakhstan — Spiritual Administration of Muslims of Kazakhstan',
27
+ method: 'A short series for the sun, each time computed once from a fixed first guess of its hour; Fajr and Isha at 15°, capped at a quarter of the night from sunrise and sunset; sunrise and sunset at −0.833°; Asr at two shadow lengths (Hanafi); a margin of 3 minutes (5 from 48° N) before sunrise, after the transit, on Asr and on Maghrib after sunset; every time rounded to the nearest minute.',
28
+ countries: ['KZ'],
29
+ parameters: {
30
+ fajrAngle: 15,
31
+ ishaAngle: 15,
32
+ sunriseAltitude: -0.833,
33
+ asrShadow: 2,
34
+ highLatitude: 'a quarter of the night, where the angle is further or not reached',
35
+ marginMinutes: '3; 5 from 48° N — sunrise earlier, Dhuhr, Asr and Maghrib later',
36
+ rounding: 'to the nearest minute',
37
+ sun: 'low-precision series (mean elements linear in time, two terms of the equation of the centre), not iterated',
38
+ },
39
+ sources: [{ title: 'Spiritual Administration of Muslims of Kazakhstan — намаз уақыттары', url: 'https://namaz.muftyat.kz/' }],
40
+ spec: 'docs/methods.md#kazakhstan',
41
+ },
42
+ });
43
+ export const places = kazakhstan.places;
@@ -9,6 +9,7 @@ import { moroccoTimes } from '../methods/morocco.js';
9
9
  import { algeriaTimes } from '../methods/algeria.js';
10
10
  import { kemenagTimes } from '../methods/indonesia.js';
11
11
  import { takvimTimes } from '../methods/turkiyeTakvimi.js';
12
+ import { kazakhstanTimes } from '../methods/kazakhstan.js';
12
13
  import { authorityData } from './registry.js';
13
14
  import { placeClockAt } from './clock.js';
14
15
  /**
@@ -32,14 +33,17 @@ export function candidateInstants(place, year, month, day, asrShadow = 1, opts =
32
33
  case 'morocco':
33
34
  case 'algeria':
34
35
  case 'indonesia':
35
- case 'turkiyetakvimi': {
36
+ case 'turkiyetakvimi':
37
+ case 'kazakhstan': {
36
38
  const t = place.authority === 'morocco'
37
39
  ? moroccoTimes(place.lat, place.lon, year, month, day, place.dipDeg, asrShadow, place.dipMaghribDeg)
38
40
  : place.authority === 'algeria'
39
41
  ? algeriaTimes(place.lat, place.lon, year, month, day, asrShadow)
40
- : place.authority === 'turkiyetakvimi'
41
- ? takvimTimes(place.lat, place.lon, year, month, day, place.temkin, asrShadow)
42
- : kemenagTimes(place.lat, place.lon, year, month, day, { highland: place.highland, twilightDip: place.twilightDip, asrShadow, afternoon: place.afternoon });
42
+ : place.authority === 'kazakhstan'
43
+ ? kazakhstanTimes(place.lat, place.lon, year, month, day)
44
+ : place.authority === 'turkiyetakvimi'
45
+ ? takvimTimes(place.lat, place.lon, year, month, day, place.temkin, asrShadow)
46
+ : kemenagTimes(place.lat, place.lon, year, month, day, { highland: place.highland, twilightDip: place.twilightDip, asrShadow, afternoon: place.afternoon });
43
47
  // the correction list: the authority's few times on the other side of a minute (the standard Asr only)
44
48
  const c = !t || !fix ? undefined : fix.correction(place.lat, place.lon, year, month, day);
45
49
  if (!t || !c)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The shapes the library speaks in. Nothing here computes anything.
3
3
  */
4
- export type Authority = 'sweden' | 'diyanet' | 'morocco' | 'algeria' | 'indonesia' | 'turkiyetakvimi';
4
+ export type Authority = 'sweden' | 'diyanet' | 'morocco' | 'algeria' | 'indonesia' | 'turkiyetakvimi' | 'kazakhstan';
5
5
  export declare const AUTHORITIES: readonly Authority[];
6
6
  export type PrayerKey = 'fajr' | 'sunrise' | 'dhuhr' | 'asr' | 'maghrib' | 'isha';
7
7
  export declare const PRAYER_KEYS: readonly PrayerKey[];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The shapes the library speaks in. Nothing here computes anything.
3
3
  */
4
- export const AUTHORITIES = ['sweden', 'diyanet', 'morocco', 'algeria', 'indonesia', 'turkiyetakvimi'];
4
+ export const AUTHORITIES = ['sweden', 'diyanet', 'morocco', 'algeria', 'indonesia', 'turkiyetakvimi', 'kazakhstan'];
5
5
  export const PRAYER_KEYS = ['fajr', 'sunrise', 'dhuhr', 'asr', 'maghrib', 'isha'];
6
6
  /** The method has not reproduced its authority's tables to the minute (or not for this date). */
7
7
  export class UncertifiedMethodError extends Error {
@@ -0,0 +1,3 @@
1
+ import type { Certification } from '../../core/types.js';
2
+ declare const data: Certification | null;
3
+ export default data;
@@ -0,0 +1,2 @@
1
+ const data = { "certified": true, "checkedAt": "2026-11-11", "places": 84, "checks": [{ "tables": "Kazakhstan — the Spiritual Administration’s times", "places": 84, "times": 551880, "off": 0, "offWithoutCorrections": 0, "from": "2025-01-01", "to": "2027-12-31" }], "corrections": { "from": "2025-01-01", "to": "2027-12-31", "entries": 0, "sha256": "e3d60e566ca082f74ce9904886087b29e65d2871c5bcc02f46349d82c8994d88" }, "revision": 1 };
2
+ export default data;
@@ -0,0 +1,7 @@
1
+ declare const data: {
2
+ id: number;
3
+ name: string;
4
+ lat: number;
5
+ lon: number;
6
+ }[];
7
+ export default data;
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/embed.mjs — do not edit; edit the JSON under data/ instead.
2
+ const data = [{ "id": 2, "name": "Қонаев", "lat": 43.883333, "lon": 77.083333 }, { "id": 3, "name": "Астана", "lat": 51.133333, "lon": 71.433333 }, { "id": 7, "name": "Орал", "lat": 51.204019, "lon": 51.370537 }, { "id": 17, "name": "Көкшетау", "lat": 53.291667, "lon": 69.391667 }, { "id": 18, "name": "Атырау", "lat": 47.116667, "lon": 51.883333 }, { "id": 20, "name": "Ақтөбе", "lat": 50.300377, "lon": 57.154555 }, { "id": 21, "name": "Степногорск", "lat": 52.346944, "lon": 71.881667 }, { "id": 22, "name": "Үшарал", "lat": 46.169722, "lon": 80.939444 }, { "id": 23, "name": "Атбасар", "lat": 51.815761, "lon": 68.358335 }, { "id": 24, "name": "Қандыағаш", "lat": 49.474444, "lon": 57.423333 }, { "id": 25, "name": "Хромтау", "lat": 50.250278, "lon": 58.434722 }, { "id": 26, "name": "Шалқар", "lat": 47.831392, "lon": 59.61929 }, { "id": 27, "name": "Құлсары", "lat": 46.983333, "lon": 54.016667 }, { "id": 29, "name": "Аягөз", "lat": 47.966667, "lon": 80.433333 }, { "id": 30, "name": "Өскемен", "lat": 49.948325, "lon": 82.627848 }, { "id": 31, "name": "Риддер", "lat": 50.33886, "lon": 83.506329 }, { "id": 32, "name": "Семей", "lat": 50.404976, "lon": 80.249235 }, { "id": 33, "name": "Жаңатас", "lat": 43.55465, "lon": 69.722525 }, { "id": 34, "name": "Қаратау", "lat": 43.166667, "lon": 70.466667 }, { "id": 35, "name": "Тараз", "lat": 42.883333, "lon": 71.366667 }, { "id": 36, "name": "Балқаш", "lat": 46.843721, "lon": 74.977301 }, { "id": 38, "name": "Жезқазған", "lat": 47.799711, "lon": 67.71409 }, { "id": 39, "name": "Қарағанды", "lat": 49.806406, "lon": 73.085485 }, { "id": 40, "name": "Сәтпаев", "lat": 47.907455, "lon": 67.528112 }, { "id": 41, "name": "Арқалық", "lat": 50.248611, "lon": 66.911389 }, { "id": 42, "name": "Қостанай", "lat": 53.219333, "lon": 63.634194 }, { "id": 43, "name": "Рудный", "lat": 52.966667, "lon": 63.116667 }, { "id": 46, "name": "Қызылорда", "lat": 44.842544, "lon": 65.502563 }, { "id": 47, "name": "Ақтау", "lat": 43.635379, "lon": 51.169135 }, { "id": 49, "name": "Форт-Шевченко", "lat": 44.507463, "lon": 50.262833 }, { "id": 50, "name": "Жаңаөзен", "lat": 43.343266, "lon": 52.865792 }, { "id": 51, "name": "Петропавл", "lat": 54.862222, "lon": 69.140833 }, { "id": 52, "name": "Екібастұз", "lat": 51.729778, "lon": 75.326583 }, { "id": 53, "name": "Павлодар", "lat": 52.315556, "lon": 76.956389 }, { "id": 54, "name": "Жетісай", "lat": 40.775278, "lon": 68.327222 }, { "id": 55, "name": "Сарыағаш", "lat": 41.466667, "lon": 69.166667 }, { "id": 56, "name": "Шардара", "lat": 41.254722, "lon": 67.969167 }, { "id": 57, "name": "Шымкент", "lat": 42.368009, "lon": 69.612769 }, { "id": 58, "name": "Түркістан", "lat": 43.302025, "lon": 68.268979 }, { "id": 59, "name": "Ақсай", "lat": 51.164945, "lon": 53.020868 }, { "id": 69, "name": "Лисаков", "lat": 52.544079, "lon": 62.492641 }, { "id": 70, "name": "Жаркент", "lat": 44.169365, "lon": 80.003842 }, { "id": 71, "name": "Арал", "lat": 46.797738, "lon": 61.660792 }, { "id": 72, "name": "Алматы", "lat": 43.238293, "lon": 76.945465 }, { "id": 73, "name": "Талғар", "lat": 43.302813, "lon": 77.23969 }, { "id": 75, "name": "Қаскелең", "lat": 43.202449, "lon": 76.622278 }, { "id": 77, "name": "Шу", "lat": 43.611782, "lon": 73.760237 }, { "id": 79, "name": "Қаражал", "lat": 48.001042, "lon": 70.785762 }, { "id": 82, "name": "Ленгер", "lat": 42.182051, "lon": 69.88212 }, { "id": 93, "name": "Ембі", "lat": 48.823344, "lon": 58.148397 }, { "id": 95, "name": "Зайсан", "lat": 47.453008, "lon": 84.969846 }, { "id": 100, "name": "Кентау", "lat": 43.518131, "lon": 68.504652 }, { "id": 103, "name": "Тайынша", "lat": 53.852738, "lon": 69.782631 }, { "id": 109, "name": "Теміртау", "lat": 50.058756, "lon": 72.953424 }, { "id": 112, "name": "Алға", "lat": 49.89673, "lon": 57.328586 }, { "id": 113, "name": "Ақсу", "lat": 52.03789, "lon": 76.920582 }, { "id": 128, "name": "Курчатов", "lat": 50.7549, "lon": 78.544826 }, { "id": 134, "name": "Арыс", "lat": 42.432744, "lon": 68.813798 }, { "id": 137, "name": "Қосшы", "lat": 50.970262, "lon": 71.355366 }, { "id": 143, "name": "Текелі", "lat": 44.863094, "lon": 78.764266 }, { "id": 146, "name": "Қарқаралы", "lat": 49.41224, "lon": 75.470095 }, { "id": 149, "name": "Шар", "lat": 49.584964, "lon": 81.047173 }, { "id": 150, "name": "Ақкөл", "lat": 51.995855, "lon": 70.952948 }, { "id": 151, "name": "Державинск", "lat": 51.098685, "lon": 66.314712 }, { "id": 152, "name": "Ерейментау", "lat": 51.619626, "lon": 73.102598 }, { "id": 153, "name": "Есіл", "lat": 51.958004, "lon": 66.406215 }, { "id": 154, "name": "Макинск", "lat": 52.63281, "lon": 70.417677 }, { "id": 155, "name": "Степняк", "lat": 52.832, "lon": 70.783427 }, { "id": 157, "name": "Жем", "lat": 48.774532, "lon": 58.072436 }, { "id": 158, "name": "Темір", "lat": 49.143142, "lon": 57.12633 }, { "id": 159, "name": "Алатау", "lat": 43.681261, "lon": 77.116046 }, { "id": 161, "name": "Сарқан", "lat": 45.413666, "lon": 79.916894 }, { "id": 162, "name": "Талдықорған", "lat": 45.017837, "lon": 78.382123 }, { "id": 163, "name": "Үштөбе", "lat": 45.251221, "lon": 77.980827 }, { "id": 165, "name": "Приозёрск", "lat": 46.028969, "lon": 73.708027 }, { "id": 166, "name": "Саран", "lat": 49.801993, "lon": 72.828387 }, { "id": 167, "name": "Шахтинск", "lat": 49.705868, "lon": 72.594637 }, { "id": 170, "name": "Қазалы", "lat": 45.760633, "lon": 62.105836 }, { "id": 171, "name": "Булаев", "lat": 54.896056, "lon": 70.448157 }, { "id": 172, "name": "Мамлют", "lat": 54.94112, "lon": 68.546199 }, { "id": 173, "name": "Сергеев", "lat": 53.888542, "lon": 67.416271 }, { "id": 174, "name": "Алтай", "lat": 49.725218, "lon": 84.273562 }, { "id": 175, "name": "Серебрянск", "lat": 49.679277, "lon": 83.300651 }, { "id": 176, "name": "Шемонаиха", "lat": 50.627624, "lon": 81.916697 }];
3
+ export default data;
@@ -0,0 +1,3 @@
1
+ import type { CorrectionsFile } from '../../core/types.js';
2
+ declare const data: CorrectionsFile;
3
+ export default data;
@@ -0,0 +1,2 @@
1
+ const data = { "from": "2025-01-01", "to": "2027-12-31", "points": {} };
2
+ export default data;
@@ -16,5 +16,6 @@ import './authorities/morocco.js';
16
16
  import './authorities/algeria.js';
17
17
  import './authorities/indonesia.js';
18
18
  import './authorities/turkiyeTakvimi.js';
19
+ import './authorities/kazakhstan.js';
19
20
  export * from './core/index.js';
20
21
  export { diyanetCountry } from './authorities/diyanet.js';
package/dist/esm/index.js CHANGED
@@ -16,5 +16,6 @@ import './authorities/morocco.js';
16
16
  import './authorities/algeria.js';
17
17
  import './authorities/indonesia.js';
18
18
  import './authorities/turkiyeTakvimi.js';
19
+ import './authorities/kazakhstan.js';
19
20
  export * from './core/index.js';
20
21
  export { diyanetCountry } from './authorities/diyanet.js';
@@ -0,0 +1,4 @@
1
+ /** mihrab-methods/kazakhstan — the Kazakhstan method with its places and correction list, and nothing else's. */
2
+ import './authorities/kazakhstan.js';
3
+ export * from './core/index.js';
4
+ export { places } from './authorities/kazakhstan.js';
@@ -0,0 +1,4 @@
1
+ /** mihrab-methods/kazakhstan — the Kazakhstan method with its places and correction list, and nothing else's. */
2
+ import './authorities/kazakhstan.js';
3
+ export * from './core/index.js';
4
+ export { places } from './authorities/kazakhstan.js';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan's
3
+ * method, as the times it publishes for a place are computed.
4
+ *
5
+ * Read off its published year for places from 40.8° to 54.9° N:
6
+ *
7
+ * • a short series for the sun: mean anomaly g and mean longitude q linear
8
+ * in the days D from J2000.0, the ecliptic longitude
9
+ * L = q + 1.915° sin g + 0.020° sin 2g, the obliquity 23.439° − 0.00000036° D,
10
+ * and from them the declination and the equation of time;
11
+ * • the day counted from 0h of the civil date at the place's own meridian
12
+ * (the Julian day of the date less longitude/360);
13
+ * • each time computed once, with the sun taken at a fixed first guess of
14
+ * its hour (Fajr 5h, sunrise 6h, Dhuhr 12h, Asr 13h, sunset and Isha
15
+ * 18h), not iterated;
16
+ * • Fajr and Isha at 15°; sunrise and sunset at −0.833°; Asr at two shadow
17
+ * lengths beyond the noon shadow (Hanafi);
18
+ * • where Fajr would fall more than a quarter of the night before sunrise,
19
+ * or Isha more than a quarter of the night after sunset — or the sun does
20
+ * not reach 15° — the quarter of the night is taken (the night from
21
+ * sunset to sunrise);
22
+ * • a margin of 3 minutes, 5 minutes from 48° N: sunrise that much
23
+ * earlier, Dhuhr and Asr that much later, Maghrib that much after
24
+ * sunset; none on Fajr or Isha;
25
+ * • every time rounded to the nearest minute.
26
+ *
27
+ * With that the method gives every published time of the places read, with
28
+ * no correction list.
29
+ */
30
+ export type KazakhstanInstants = {
31
+ fajr: Date;
32
+ sunrise: Date;
33
+ dhuhr: Date;
34
+ asr: Date;
35
+ maghrib: Date;
36
+ isha: Date;
37
+ };
38
+ /** The margin at a latitude, minutes. */
39
+ export declare function kazakhstanMargin(latitude: number): number;
40
+ /**
41
+ * The times before rounding, minutes past 00:00 UT of the civil date (month
42
+ * 0-based), and the sunset the method prints beside Maghrib.
43
+ */
44
+ export declare function kazakhstanRawMinutes(latitude: number, longitude: number, year: number, month: number, day: number): {
45
+ fajr: number;
46
+ sunrise: number;
47
+ dhuhr: number;
48
+ asr: number;
49
+ sunset: number;
50
+ maghrib: number;
51
+ isha: number;
52
+ };
53
+ /**
54
+ * The Administration's times for the civil date (month 0-based) at the
55
+ * point, each on the minute it prints (rounded to the nearest); Asr is the
56
+ * Hanafi one its table carries. Null where a time does not form.
57
+ */
58
+ export declare function kazakhstanTimes(latitude: number, longitude: number, year: number, month: number, day: number): KazakhstanInstants | null;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Kazakhstan — the Spiritual Administration of Muslims of Kazakhstan's
3
+ * method, as the times it publishes for a place are computed.
4
+ *
5
+ * Read off its published year for places from 40.8° to 54.9° N:
6
+ *
7
+ * • a short series for the sun: mean anomaly g and mean longitude q linear
8
+ * in the days D from J2000.0, the ecliptic longitude
9
+ * L = q + 1.915° sin g + 0.020° sin 2g, the obliquity 23.439° − 0.00000036° D,
10
+ * and from them the declination and the equation of time;
11
+ * • the day counted from 0h of the civil date at the place's own meridian
12
+ * (the Julian day of the date less longitude/360);
13
+ * • each time computed once, with the sun taken at a fixed first guess of
14
+ * its hour (Fajr 5h, sunrise 6h, Dhuhr 12h, Asr 13h, sunset and Isha
15
+ * 18h), not iterated;
16
+ * • Fajr and Isha at 15°; sunrise and sunset at −0.833°; Asr at two shadow
17
+ * lengths beyond the noon shadow (Hanafi);
18
+ * • where Fajr would fall more than a quarter of the night before sunrise,
19
+ * or Isha more than a quarter of the night after sunset — or the sun does
20
+ * not reach 15° — the quarter of the night is taken (the night from
21
+ * sunset to sunrise);
22
+ * • a margin of 3 minutes, 5 minutes from 48° N: sunrise that much
23
+ * earlier, Dhuhr and Asr that much later, Maghrib that much after
24
+ * sunset; none on Fajr or Isha;
25
+ * • every time rounded to the nearest minute.
26
+ *
27
+ * With that the method gives every published time of the places read, with
28
+ * no correction list.
29
+ */
30
+ const RAD = Math.PI / 180;
31
+ const sin = (d) => Math.sin(d * RAD);
32
+ const cos = (d) => Math.cos(d * RAD);
33
+ const tan = (d) => Math.tan(d * RAD);
34
+ /** a − b·⌊a/b⌋: into [0, b). */
35
+ const fix = (a, b) => a - b * Math.floor(a / b);
36
+ /** Fajr's and Isha's depression, degrees. */
37
+ const TWILIGHT = 15;
38
+ const HORIZON = 0.833;
39
+ /** Shadow lengths beyond the noon shadow at Asr. */
40
+ const ASR_SHADOW = 2;
41
+ /** The latitude from which the margin is five minutes, not three. */
42
+ const NORTH = 48;
43
+ /** The first guess of each time's hour, at which its sun is taken. */
44
+ const GUESS = { fajr: 5, sunrise: 6, dhuhr: 12, asr: 13, sunset: 18, isha: 18 };
45
+ /** The Julian day of 0h UT of a civil date (month 1-based). */
46
+ function julian(year, month, day) {
47
+ if (month <= 2) {
48
+ year -= 1;
49
+ month += 12;
50
+ }
51
+ const a = Math.floor(year / 100);
52
+ const b = 2 - a + Math.floor(a / 4);
53
+ return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5;
54
+ }
55
+ /** The method's sun at a Julian day: declination (degrees), equation of time (hours). */
56
+ function sunAt(jd) {
57
+ const D = jd - 2451545.0;
58
+ const g = fix(357.529 + 0.98560028 * D, 360);
59
+ const q = fix(280.459 + 0.98564736 * D, 360);
60
+ const L = fix(q + 1.915 * sin(g) + 0.02 * sin(2 * g), 360);
61
+ const e = 23.439 - 0.00000036 * D;
62
+ const ra = Math.atan2(cos(e) * sin(L), cos(L)) / RAD / 15;
63
+ return { decl: Math.asin(sin(e) * sin(L)) / RAD, eqt: q / 15 - fix(ra, 24) };
64
+ }
65
+ /** The margin at a latitude, minutes. */
66
+ export function kazakhstanMargin(latitude) {
67
+ return latitude >= NORTH ? 5 : 3;
68
+ }
69
+ /**
70
+ * The times before rounding, minutes past 00:00 UT of the civil date (month
71
+ * 0-based), and the sunset the method prints beside Maghrib.
72
+ */
73
+ export function kazakhstanRawMinutes(latitude, longitude, year, month, day) {
74
+ const jd = julian(year, month + 1, day) - longitude / 360;
75
+ const midday = (t) => fix(12 - sunAt(jd + t).eqt, 24);
76
+ /** Hours (local mean time) at which the sun is `angle` degrees below the horizon; NaN where it is not. */
77
+ const at = (angle, t, before) => {
78
+ const { decl } = sunAt(jd + t);
79
+ const x = (-sin(angle) - sin(decl) * sin(latitude)) / (cos(decl) * cos(latitude));
80
+ if (!(Math.abs(x) <= 1))
81
+ return NaN;
82
+ const h = Math.acos(x) / RAD / 15;
83
+ return midday(t) + (before ? -h : h);
84
+ };
85
+ const asrAngle = (t) => -Math.atan(1 / (ASR_SHADOW + tan(Math.abs(latitude - sunAt(jd + t).decl)))) / RAD;
86
+ let fajr = at(TWILIGHT, GUESS.fajr / 24, true);
87
+ const sunrise = at(HORIZON, GUESS.sunrise / 24, true);
88
+ const dhuhr = midday(GUESS.dhuhr / 24);
89
+ const asr = at(asrAngle(GUESS.asr / 24), GUESS.asr / 24, false);
90
+ const sunset = at(HORIZON, GUESS.sunset / 24, false);
91
+ let isha = at(TWILIGHT, GUESS.isha / 24, false);
92
+ // the quarter of the night, where the angle is further than that or is not reached
93
+ const portion = (TWILIGHT / 60) * fix(sunrise - sunset, 24);
94
+ if (!Number.isFinite(fajr) || fix(sunrise - fajr, 24) > portion)
95
+ fajr = sunrise - portion;
96
+ if (!Number.isFinite(isha) || fix(isha - sunset, 24) > portion)
97
+ isha = sunset + portion;
98
+ const margin = kazakhstanMargin(latitude);
99
+ // local mean time (hours) to minutes past 00:00 UT of the date
100
+ const ut = (h) => (h - longitude / 15) * 60;
101
+ return {
102
+ fajr: ut(fajr),
103
+ sunrise: ut(sunrise) - margin,
104
+ dhuhr: ut(dhuhr) + margin,
105
+ asr: ut(asr) + margin,
106
+ sunset: ut(sunset),
107
+ maghrib: ut(sunset) + margin,
108
+ isha: ut(isha),
109
+ };
110
+ }
111
+ /**
112
+ * The Administration's times for the civil date (month 0-based) at the
113
+ * point, each on the minute it prints (rounded to the nearest); Asr is the
114
+ * Hanafi one its table carries. Null where a time does not form.
115
+ */
116
+ export function kazakhstanTimes(latitude, longitude, year, month, day) {
117
+ const r = kazakhstanRawMinutes(latitude, longitude, year, month, day);
118
+ const all = [r.fajr, r.sunrise, r.dhuhr, r.asr, r.maghrib, r.isha];
119
+ if (!all.every(Number.isFinite))
120
+ return null;
121
+ const start = Date.UTC(year, month, day);
122
+ const at = (m) => new Date(start + Math.floor(m + 0.5) * 60_000);
123
+ return { fajr: at(r.fajr), sunrise: at(r.sunrise), dhuhr: at(r.dhuhr), asr: at(r.asr), maghrib: at(r.maghrib), isha: at(r.isha) };
124
+ }
@@ -13,4 +13,5 @@ export declare const METHOD_REVISION: {
13
13
  readonly algeria: 1;
14
14
  readonly indonesia: 2;
15
15
  readonly turkiyetakvimi: 1;
16
+ readonly kazakhstan: 1;
16
17
  };
@@ -13,4 +13,5 @@ export const METHOD_REVISION = {
13
13
  algeria: 1,
14
14
  indonesia: 2,
15
15
  turkiyetakvimi: 1,
16
+ kazakhstan: 1,
16
17
  };
package/docs/methods.md CHANGED
@@ -240,6 +240,35 @@ cities.
240
240
  - The calendar's files give a city's point to the whole arc-minute and it
241
241
  computes at a finer one; each place is at the point its year shows.
242
242
 
243
+ ## Kazakhstan
244
+
245
+ `src/methods/kazakhstan.ts`. The Spiritual Administration of Muslims of
246
+ Kazakhstan's times, read off its cities from 40.8° to 54.9° N.
247
+
248
+ - A **short series for the sun**: the mean anomaly `g = 357.529° + 0.98560028° D`
249
+ and the mean longitude `q = 280.459° + 0.98564736° D`, with `D` the days
250
+ from J2000.0; the ecliptic longitude `L = q + 1.915° sin g + 0.020° sin 2g`;
251
+ the obliquity `23.439° − 0.00000036° D`; the declination and the equation
252
+ of time from those.
253
+ - The day is counted from **0h of the civil date at the place's own
254
+ meridian**: the Julian day of the date less `longitude / 360`.
255
+ - Each time is computed **once**, with the sun taken at a fixed first guess
256
+ of its hour — Fajr 5h, sunrise 6h, Dhuhr 12h, Asr 13h, sunset and Isha
257
+ 18h — and not iterated.
258
+ - **Fajr** and **Isha**: the sun 15° below the horizon.
259
+ - **Sunrise** and sunset: the sun 0.833° below the horizon.
260
+ - **Asr**: two shadow lengths beyond the noon shadow (Hanafi), the one Asr
261
+ the Administration prints.
262
+ - **Where Fajr would fall more than a quarter of the night before sunrise,
263
+ or Isha more than a quarter of the night after sunset** — or the sun does
264
+ not reach 15° — the quarter of the night is taken, the night being from
265
+ sunset to sunrise.
266
+ - A **margin** of 3 minutes, 5 minutes from 48° N: sunrise that much
267
+ earlier, Dhuhr and Asr that much later, Maghrib that much after sunset.
268
+ None on Fajr or Isha.
269
+ - Every time rounded to the **nearest** minute.
270
+ - The whole country is on UTC+5, and every place is printed on it.
271
+
243
272
  ## Checking a port
244
273
 
245
274
  1. Take `test/vectors.json`. For each case, compute the six times at the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mihrab-methods",
3
- "version": "1.4.0",
4
- "description": "The prayer-time authorities' own calculation methods, each reproducing its published timetable to the minute: Sweden (Diyanet, as Islamiska Förbundet prints it), Diyanet worldwide, Morocco (Habous), Algeria (Ministry of Religious Affairs), Indonesia (Kemenag), Türkiye Takvimi.",
3
+ "version": "1.5.0",
4
+ "description": "The prayer-time authorities' own calculation methods, each reproducing its published timetable to the minute: Sweden (Diyanet, as Islamiska Förbundet prints it), Diyanet worldwide, Morocco (Habous), Algeria (Ministry of Religious Affairs), Indonesia (Kemenag), Türkiye Takvimi, Kazakhstan (Spiritual Administration).",
5
5
  "license": "MPL-2.0",
6
6
  "author": "Hassan El Ghamri",
7
7
  "repository": {
@@ -29,6 +29,7 @@
29
29
  "./dist/*/algeria.js",
30
30
  "./dist/*/indonesia.js",
31
31
  "./dist/*/turkiyetakvimi.js",
32
+ "./dist/*/kazakhstan.js",
32
33
  "./dist/*/authorities/*.js",
33
34
  "./src/index.ts",
34
35
  "./src/sweden.ts",
@@ -37,6 +38,7 @@
37
38
  "./src/algeria.ts",
38
39
  "./src/indonesia.ts",
39
40
  "./src/turkiyetakvimi.ts",
41
+ "./src/kazakhstan.ts",
40
42
  "./src/authorities/*.ts"
41
43
  ],
42
44
  "engines": {
@@ -133,6 +135,16 @@
133
135
  "default": "./dist/cjs/turkiyetakvimi.js"
134
136
  }
135
137
  },
138
+ "./kazakhstan": {
139
+ "import": {
140
+ "types": "./dist/esm/kazakhstan.d.ts",
141
+ "default": "./dist/esm/kazakhstan.js"
142
+ },
143
+ "require": {
144
+ "types": "./dist/cjs/kazakhstan.d.ts",
145
+ "default": "./dist/cjs/kazakhstan.js"
146
+ }
147
+ },
136
148
  "./locate": {
137
149
  "import": {
138
150
  "types": "./dist/esm/locate.d.ts",
@@ -171,6 +183,9 @@
171
183
  "turkiyetakvimi": [
172
184
  "./dist/cjs/turkiyetakvimi.d.ts"
173
185
  ],
186
+ "kazakhstan": [
187
+ "./dist/cjs/kazakhstan.d.ts"
188
+ ],
174
189
  "locate": [
175
190
  "./dist/cjs/locate.d.ts"
176
191
  ]
@@ -11,7 +11,8 @@
11
11
  "morocco",
12
12
  "algeria",
13
13
  "indonesia",
14
- "turkiyetakvimi"
14
+ "turkiyetakvimi",
15
+ "kazakhstan"
15
16
  ]
16
17
  },
17
18
  "additionalProperties": {