SALAH 0.2: prayer times for Casablanca (Morocco method), chord-tapered round layout

This commit is contained in:
2026-09-09 17:12:23 +01:00
commit e86921d966
14 changed files with 10982 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
// Prayer time math tests — reference: AlAdhan API method 21 (Morocco, Fajr 19, Isha 17, school STANDARD)
// Casablanca (33.5731, -7.5898) 2026-09-09 reference times:
// Fajr 05:40, Sunrise 07:10, Dhuhr 13:33, Asr 17:02, Maghrib 19:50, Isha 21:05
'use strict'
const assert = require('assert/strict')
const prayer = require('../src/common/prayer.js')
// Casablanca is UTC+1 in September 2026; emulate the watch by passing tz offset explicitly.
const TZ = 1
const CASA = { lat: 33.5731, lon: -7.5898 }
function timesFor(y, m, d) {
return prayer.times(new Date(Date.UTC(y, m - 1, d, 12, 0, 0)), CASA.lat, CASA.lon, TZ)
}
function assertClose(actualMin, refHM, label, tol = 2) {
const [rh, rm] = refHM.split(':').map(Number)
const ref = rh * 60 + rm
const diff = Math.abs(actualMin - ref)
assert.ok(diff <= tol, `${label}: got ${prayer.format(actualMin)} (${actualMin}), expected ${refHM} — off by ${diff.toFixed(1)} min`)
}
let passed = 0
function test(name, fn) {
try { fn(); passed++; console.log(' ok -', name) }
catch (e) { console.error(' FAIL -', name, '\n ', e.message); process.exitCode = 1 }
}
console.log('prayer.js tests')
test('times match AlAdhan Morocco method for Casablanca 2026-09-09', () => {
const t = timesFor(2026, 9, 9)
assertClose(t.fajr, '05:40', 'Fajr')
assertClose(t.sunrise, '07:10', 'Sunrise')
assertClose(t.dhuhr, '13:33', 'Dhuhr')
assertClose(t.asr, '17:02', 'Asr')
assertClose(t.maghrib, '19:50', 'Maghrib')
assertClose(t.isha, '21:05', 'Isha')
})
test('times match for a midwinter date 2026-01-15 (AlAdhan cross-check)', () => {
// AlAdhan method 21, Casablanca, 15-01-2026: Fajr 07:02, Sunrise 08:35, Dhuhr 13:45, Asr 16:26, Maghrib 18:50, Isha 20:08
const t = timesFor(2026, 1, 15)
assertClose(t.fajr, '07:02', 'Fajr')
assertClose(t.sunrise, '08:35', 'Sunrise')
assertClose(t.dhuhr, '13:45', 'Dhuhr')
assertClose(t.asr, '16:26', 'Asr')
assertClose(t.maghrib, '18:50', 'Maghrib')
assertClose(t.isha, '20:08', 'Isha')
})
test('times match for a summer date 2026-07-01 (AlAdhan cross-check)', () => {
// AlAdhan method 21, Casablanca, 01-07-2026: Fajr 04:34, Sunrise 06:24, Dhuhr 13:39, Asr 17:19, Maghrib 20:50, Isha 22:21
const t = timesFor(2026, 7, 1)
assertClose(t.fajr, '04:34', 'Fajr')
assertClose(t.sunrise, '06:24', 'Sunrise')
assertClose(t.dhuhr, '13:39', 'Dhuhr')
assertClose(t.asr, '17:19', 'Asr')
assertClose(t.maghrib, '20:50', 'Maghrib')
assertClose(t.isha, '22:21', 'Isha')
})
test('format pads minutes', () => {
assert.equal(prayer.format(9 * 60 + 5), '09:05')
assert.equal(prayer.format(13 * 60 + 33), '13:33')
})
test('format wraps past midnight (next-day fajr)', () => {
// 25h15 -> 01:15 next day
assert.equal(prayer.format(25 * 60 + 15), '01:15')
})
test('nextPrayer: before fajr -> next is fajr', () => {
const t = timesFor(2026, 9, 9)
const n = prayer.nextPrayer(t, 4 * 60)
assert.equal(n.key, 'fajr')
assert.ok(n.minutesLeft > 0 && n.minutesLeft < 1440, 'minutesLeft ' + n.minutesLeft)
// 04:00 -> fajr 05:40 ≈ 100 min
assert.ok(Math.abs(n.minutesLeft - 100) <= 2, 'minutesLeft ' + n.minutesLeft)
})
test('nextPrayer: between dhuhr and asr -> next is asr', () => {
const t = timesFor(2026, 9, 9)
const n = prayer.nextPrayer(t, 14 * 60)
assert.equal(n.key, 'asr')
assert.ok(n.current === 'dhuhr')
})
test('nextPrayer: after isha -> wraps to fajr (+1d) and minutesLeft crosses midnight', () => {
const t = timesFor(2026, 9, 9)
const n = prayer.nextPrayer(t, 23 * 60)
assert.equal(n.key, 'fajr')
assert.ok(n.nextDay === true, 'nextDay flag')
// 23:00 -> fajr 05:40 next day = 6h40 = 400 min
assert.ok(Math.abs(n.minutesLeft - 400) <= 2, 'minutesLeft ' + n.minutesLeft)
})
test('nextPrayer: exactly at dhuhr time -> next is asr (current dhuhr)', () => {
const t = timesFor(2026, 9, 9)
const n = prayer.nextPrayer(t, Math.round(t.dhuhr))
assert.equal(n.key, 'asr')
})
test('minutesLeft is exact', () => {
const t = timesFor(2026, 9, 9)
const n = prayer.nextPrayer(t, 17 * 60)
// 17:00 -> asr 17:02 = 2 min
assert.ok(Math.abs(n.minutesLeft - 2) <= 1, 'minutesLeft ' + n.minutesLeft)
})
console.log(process.exitCode ? 'FAILED' : 'ALL PASSED (' + passed + ')')