Skrip Node.js β Digunakan Semasa Pembangunan
Skrip-skrip ini dijalankan terus dalam terminal Node.js untuk menyiasat punca perbezaan antara pengiraan dan data JAKIM. Salin dan jalankan dengan node script.js.
Probe Longitud
Probe Ihtiyati
Kesan Bug Formula
Fetch JAKIM (Node)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// SKRIP 1: Probe Longitud
// Tujuan: Cari koordinat yang menghasilkan Subuh = 06:03 (JHR02)
// Digunakan pada: 18 Mac 2026
// Penemuan: Titik rujukan bukan pusat JB (~103.75Β°E) tapi sempadan
// barat zon (~103.62Β°E, kawasan Kulai)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function sinD(d){ return Math.sin(d*Math.PI/180); }
function cosD(d){ return Math.cos(d*Math.PI/180); }
function acosD(x){ return Math.acos(x)*180/Math.PI; }
function jd(y,m,d){
if(m<=2){y--;m+=12;}
const A=Math.floor(y/100), B=2-A+Math.floor(A/4);
return Math.floor(365.25*(y+4716))+Math.floor(30.6001*(m+1))+d+B-1524.5;
}
function calcFajr(lat, lng, tz){
const J = jd(2026,3,18);
const T = 2*Math.PI*(J-2451545)/365.25; // T dalam radian
const DELTA = 0.37877
+ 23.264 * sinD(57.297*T - 79.547) // 57.297 menukar radianβdarjah
+ 0.3812 * sinD(2*57.297*T - 82.682)
+ 0.17132 * sinD(3*57.297*T - 59.722);
const U=(J-2451545)/36525, L0=280.46607+36000.7698*U;
const ET=(-(1789+237*U)*sinD(L0)-(7146-62*U)*cosD(L0)
+(9934-14*U)*sinD(2*L0)-(29+5*U)*cosD(2*L0)
+(74+10*U)*sinD(3*L0)+(320-4*U)*cosD(3*L0)
-212*sinD(4*L0))/1000;
const TT = 12 + tz - (lng/15) - (ET/60);
const cosH = (sinD(-20) - sinD(lat)*sinD(DELTA)) / (cosD(lat)*cosD(DELTA));
const HA = acosD(cosH);
const fajrMin = Math.floor((TT - HA/15 + 10/60) * 60); // +10min ihtiyati
const h=Math.floor(fajrMin/60), m=fajrMin%60;
return String(h).padStart(2,'0')+':'+String(m).padStart(2,'0');
}
// Probe longitud 103.0 β 104.2, lat tetap 1.55, tz=8
// Sasaran: 06:03 (JHR02, 18 Mac 2026)
console.log('--- Probe Longitud (lat=1.55, tz=8) ---');
for(let lng=103.0; lng<=104.2; lng+=0.05){
console.log(`lng=${lng.toFixed(2)} β Subuh=${calcFajr(1.55,lng,8)} [sasaran 06:03]`);
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// SKRIP 2: Probe Ihtiyati
// Tujuan: Tentukan nilai ihtiyati tepat untuk setiap waktu
// Input: Koordinat + waktu JAKIM yang diketahui
// Output: Ihtiyati optimum yang menghasilkan semua waktu betul
// Penemuan: Subuh+10, Zohor+2, Asar+3, Maghrib+1, Isyak+2
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function sinD(d){return Math.sin(d*Math.PI/180);}
function cosD(d){return Math.cos(d*Math.PI/180);}
function acosD(x){return Math.acos(x)*180/Math.PI;}
function atanD(x){return Math.atan(x)*180/Math.PI;}
function tanD(d){return Math.tan(d*Math.PI/180);}
function rawTimes(lat, lng, tz, date, elev=10){
const [y,m,d]=date;
let Y=y, M=m;
if(M<=2){Y--;M+=12;}
const A=Math.floor(Y/100), B=2-A+Math.floor(A/4);
const J=Math.floor(365.25*(Y+4716))+Math.floor(30.6001*(M+1))+d+B-1524.5;
const T=2*Math.PI*(J-2451545)/365.25;
const DELTA=0.37877+23.264*sinD(57.297*T-79.547)+0.3812*sinD(2*57.297*T-82.682)+0.17132*sinD(3*57.297*T-59.722);
const U=(J-2451545)/36525, L0=280.46607+36000.7698*U;
const ET=(-(1789+237*U)*sinD(L0)-(7146-62*U)*cosD(L0)+(9934-14*U)*sinD(2*L0)-(29+5*U)*cosD(2*L0)+(74+10*U)*sinD(3*L0)+(320-4*U)*cosD(3*L0)-212*sinD(4*L0))/1000;
const TT=12+tz-(lng/15)-(ET/60);
const HOR=-0.8333-0.0347*Math.sqrt(elev);
const ASR=atanD(1/(1+tanD(Math.abs(lat-DELTA))));
const ha=(alt)=>{const c=(sinD(alt)-sinD(lat)*sinD(DELTA))/(cosD(lat)*cosD(DELTA));return acosD(c);};
return{fajr:TT-ha(-20)/15,syuruk:TT-ha(HOR)/15,dhuhr:TT,asr:TT+ha(ASR)/15,maghrib:TT+ha(HOR)/15,isyak:TT+ha(-18)/15};
}
const raw = rawTimes(1.48, 103.62, 8, [2026,3,18]);
const jakim = {fajr:363, dhuhr:796, asr:981, maghrib:1158, isyak:1227}; // menit
const keys = ['fajr','dhuhr','asr','maghrib','isyak'];
for(const k of keys){
for(let iht=0; iht<=15; iht++){
const result = Math.floor((raw[k] + iht/60)*60);
if(result === jakim[k])
console.log(`β ${k}: ihtiyati=${iht}min raw=${(raw[k]*60).toFixed(2)}min hasil=${result} jakim=${jakim[k]}`);
}
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// SKRIP 3: Mengesan Bug Formula
// Tujuan: Buktikan ralat apabila T ditukar ke darjah sebelum
// dimasukkan ke sinD() dalam formula deklinasi
// Bug: sinD(57.297 * T_degrees) β sinD(57.297 * T_radians)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function sinD(d){return Math.sin(d*Math.PI/180);}
const J = 2451545 + (2026-2000)*365.25 + 77; // ~18 Mac 2026
const T_rad = 2*Math.PI*(J-2451545)/365.25; // betul: radian
const T_deg = T_rad * 180/Math.PI; // salah: ditukar ke darjah
const delta_betul = 0.37877
+ 23.264 * sinD(57.297*T_rad - 79.547) // T dalam radian β BETUL
+ 0.3812 * sinD(2*57.297*T_rad - 82.682)
+ 0.17132 * sinD(3*57.297*T_rad - 59.722);
const delta_buggy = 0.37877
+ 23.264 * sinD(57.297*T_deg - 79.547) // T dalam darjah β BUG
+ 0.3812 * sinD(2*57.297*T_deg - 82.682)
+ 0.17132 * sinD(3*57.297*T_deg - 59.722);
console.log('T (radian):', T_rad.toFixed(6));
console.log('T (darjah):', T_deg.toFixed(4));
console.log('Deklinasi BETUL :', delta_betul.toFixed(4), 'Β° (nilai sah ~-1Β° untuk Mac)');
console.log('Deklinasi BUG :', delta_buggy.toFixed(4), 'Β° (nilai tersasar)');
console.log('Perbezaan:', (delta_betul - delta_buggy).toFixed(4), 'Β°');
console.log('');
console.log('Kesan pada Subuh (JHR02, 18 Mac 2026):');
console.log(' Formula betul β ~05:54 + 10min ihtiyati = 06:04 β JAKIM 06:03');
console.log(' Formula buggy β ~05:39 + 10min ihtiyati = 05:49 β jauh berbeza');
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// SKRIP 4: Fetch Data JAKIM API (Node.js)
// Tujuan: Ambil waktu solat JAKIM untuk dijadikan data rujukan
// Nota: Tidak ada CORS di Node.js, jalankan dengan: node fetch-jakim.js
// Keperluan: Node.js 18+ (fetch built-in)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const ZONES_TO_CHECK = [
{ zone: 'JHR02', lat: 1.48, lng: 103.62 },
{ zone: 'WLY01', lat: 3.14, lng: 101.58 },
{ zone: 'PNG01', lat: 5.42, lng: 100.18 },
{ zone: 'SGR01', lat: 3.13, lng: 101.38 },
];
async function fetchZone(zone) {
const url = `https://www.e-solat.gov.my/index.php?r=esolatApi/takwimsolat&period=today&zone=${zone}`;
const res = await fetch(url);
if(!res.ok) throw new Error(`HTTP ${res.status} for ${zone}`);
return res.json();
}
function extractTimes(data) {
const prayerData = data?.prayerTime?.[0];
if(!prayerData) return null;
return {
date: prayerData.date,
imsak: prayerData.imsak,
fajr: prayerData.fajr,
syuruk: prayerData.syuruk,
dhuhr: prayerData.dhuhr,
asr: prayerData.asr,
maghrib: prayerData.maghrib,
isha: prayerData.isha,
};
}
(async () => {
for (const {zone, lat, lng} of ZONES_TO_CHECK) {
try {
const data = await fetchZone(zone);
const times = extractTimes(data);
console.log(`\n=== ${zone} (lat=${lat}, lng=${lng}) ===`);
console.log(` Tarikh : ${times?.date}`);
console.log(` Imsak : ${times?.imsak}`);
console.log(` Subuh : ${times?.fajr}`);
console.log(` Syuruk : ${times?.syuruk}`);
console.log(` Zohor : ${times?.dhuhr}`);
console.log(` Asar : ${times?.asr}`);
console.log(` Maghrib: ${times?.maghrib}`);
console.log(` Isyak : ${times?.isha}`);
} catch(e) {
console.error(`β ${zone}: ${e.message}`);
}
}
})();