salah times done maybe

This commit is contained in:
2025-08-16 22:03:11 +05:00
parent a447d80f36
commit 0f8c9b5e55
4 changed files with 474 additions and 17 deletions

View File

@@ -0,0 +1,364 @@
// PrayTimes-JS: Prayer Times Calculator (ver 2.3)
// https://github.com/abodeo/prayertimes
//--------------------- Copyright Block ----------------------
/*
PrayTimes-JS: Prayer Times Calculator (ver 2.3)
Copyright (C) 2007-2011 PrayTimes.org
JS Code By: Hussain Ali Khan
Original JS Code By: Hamid Zarrabi-Zadeh
Original C++ Code By: Hamid Zarrabi-Zadeh
License: GNU LGPL v3.0
TERMS OF USE:
Permission is granted to use this code, with or without
modification, in any website or application provided that
this copyright block is reproduced in its entirety. This
notice must be placed at the beginning of the code block
and may not be altered in any way. This notice must remain
as part of the code block at all times.
*/
import HijriDate from 'hijri-date';
type PrayerTimes = {
fajr: string;
sunrise: string;
dhuhr: string;
asr: string;
sunset: string;
maghrib: string;
isha: string;
imsak?: string;
midnight?: string;
[key: string]: string | undefined;
};
type Coordinates = [number, number];
type DateComponents = {
year: number;
month: number;
day: number;
};
type CalculationMethod = {
name: string;
params: {
fajr: number | string;
isha: number | string;
maghrib?: number | string;
midnight?: string;
};
};
type TimeNames =
| 'imsak'
| 'fajr'
| 'sunrise'
| 'dhuhr'
| 'asr'
| 'sunset'
| 'maghrib'
| 'isha'
| 'midnight';
class PrayTimes {
private lat: number = 0;
private lng: number = 0;
private elv: number = 0;
private timeZone: number = 0;
private jDate: number = 0;
private calcMethod: string = 'MWL';
private setting: { [key: string]: any } = {};
private methods: { [key: string]: CalculationMethod } = {
MWL: {
name: 'Muslim World League',
params: { fajr: 18, isha: 17 },
},
ISNA: {
name: 'Islamic Society of North America (ISNA)',
params: { fajr: 15, isha: 15 },
},
Egypt: {
name: 'Egyptian General Authority of Survey',
params: { fajr: 19.5, isha: 17.5 },
},
Makkah: {
name: 'Umm Al-Qura University, Makkah',
params: { fajr: 18.5, isha: '90 min' },
},
Karachi: {
name: 'University of Islamic Sciences, Karachi',
params: { fajr: 18, isha: 18 },
},
Tehran: {
name: 'Institute of Geophysics, University of Tehran',
params: { fajr: 17.7, isha: 14, maghrib: 4.5, midnight: 'Jafari' },
},
Jafari: {
name: 'Shia Ithna-Ashari, Leva Institute, Qum',
params: { fajr: 16, isha: 14, maghrib: 4, midnight: 'Jafari' },
},
};
private defaultParams = {
maghrib: '0 min',
midnight: 'Standard',
};
private timeSuffixes: string[] = ['am', 'pm'];
private invalidTime: string = '-----';
private numIterations: number = 1;
private offset: { [key: string]: number } = {};
private timeNames: { [key in TimeNames]: string } = {
imsak: 'Imsak',
fajr: 'Fajr',
sunrise: 'Sunrise',
dhuhr: 'Dhuhr',
asr: 'Asr',
sunset: 'Sunset',
maghrib: 'Maghrib',
isha: 'Isha',
midnight: 'Midnight',
};
constructor(method: string = 'Makkah') {
this.setMethod(method);
}
setMethod(method: string): void {
if (this.methods[method]) {
this.calcMethod = method;
const params = this.methods[method].params;
this.setting = { ...this.defaultParams, ...params };
}
}
getTimes(
date: Date,
coords: Coordinates,
timezone: number,
elv: number = 0,
format: string = '24h',
): PrayerTimes {
this.lat = coords[0];
this.lng = coords[1];
this.elv = elv;
this.timeZone = timezone;
this.jDate = this.julian(date.getFullYear(), date.getMonth() + 1, date.getDate()) - this.lng / (15 * 24);
return this.computeTimes(format);
}
private julian(year: number, month: number, day: number): number {
if (month <= 2) {
year -= 1;
month += 12;
}
const A = Math.floor(year / 100);
const B = 2 - A + Math.floor(A / 4);
return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
}
private computeTimes(format: string): PrayerTimes {
const times: { [key: string]: number } = {
imsak: 5,
fajr: 5,
sunrise: 6,
dhuhr: 12,
asr: 13,
sunset: 18,
maghrib: 18,
isha: 18,
};
const dayPortion = this.sunPosition(this.jDate + 0.5);
times.dhuhr = this.midDay(this.jDate);
times.sunrise = this.sunAngleTime(this.jDate, this.riseSetAngle(), 'ccw');
times.sunset = this.sunAngleTime(this.jDate, this.riseSetAngle(), 'cw');
times.asr = this.asrTime(this.jDate, 1);
times.maghrib = this.sunAngleTime(this.jDate, this.eval(this.setting.maghrib), 'cw');
if (this.calcMethod === 'Makkah') {
const gDate = new Date((this.jDate - 2440587.5) * 86400000);
const hijriDate = new HijriDate(gDate.getTime());
const isRamadan = hijriDate.getMonth() === 9; // Ramadan is the 9th month in Hijri
// For Umm al-Qura, Isha is 90 minutes after Maghrib, 120 during Ramadan
times.isha = times.maghrib + (isRamadan ? 2 : 1.5);
} else {
times.isha = this.sunAngleTime(this.jDate, this.eval(this.setting.isha), 'cw');
}
times.fajr = this.sunAngleTime(this.jDate, this.eval(this.setting.fajr), 'ccw');
const result = this.adjustTimes(times);
return this.formatTimes(result, format);
}
private sunPosition(jd: number): [number, number] {
const D = jd - 2451545.0;
const g = this.fixAngle(357.529 + 0.98560028 * D);
const q = this.fixAngle(280.459 + 0.98564736 * D);
const L = this.fixAngle(q + 1.915 * this.dSin(g) + 0.02 * this.dSin(2 * g));
const R = 1.00014 - 0.01671 * this.dCos(g) - 0.00014 * this.dCos(2 * g);
const e = 23.439 - 0.00000036 * D;
const RA = this.dAtan2(this.dCos(e) * this.dSin(L), this.dCos(L)) / 15;
const dec = this.dAsin(this.dSin(e) * this.dSin(L));
const EqT = q / 15 - this.fixHour(RA);
return [dec, EqT];
}
private sunAngleTime(jd: number, angle: number, direction: string): number {
const [dec, EqT] = this.sunPosition(jd);
const t =
(1 / 15) *
this.dACos(
(-this.dSin(angle) - this.dSin(dec) * this.dSin(this.lat)) /
(this.dCos(dec) * this.dCos(this.lat)),
);
return this.midDay(jd) + (direction === 'cw' ? t : -t) - EqT;
}
private asrTime(jd: number, factor: number): number {
const [dec] = this.sunPosition(jd);
const angle = -this.dACot(factor + this.dTan(Math.abs(dec - this.lat)));
return this.sunAngleTime(jd, angle, 'cw');
}
private riseSetAngle(): number {
return 0.833 + 0.0347 * Math.sqrt(this.elv);
}
private midDay(jd: number): number {
const [, EqT] = this.sunPosition(jd);
return 12 - EqT;
}
private adjustTimes(times: { [key: string]: number }): { [key: string]: number } {
const adjustedTimes = { ...times };
for (const i in adjustedTimes) {
adjustedTimes[i] += this.timeZone - this.lng / 15;
}
return adjustedTimes;
}
private formatTimes(times: { [key: string]: number }, format: string): PrayerTimes {
const formattedTimes: PrayerTimes = {} as PrayerTimes;
for (const i in times) {
if (format === '24h') {
formattedTimes[i] = this.floatToTime24(times[i]);
} else {
formattedTimes[i] = this.floatToTime12(times[i]);
}
}
return formattedTimes;
}
private floatToTime24(time: number): string {
if (isNaN(time)) {
return this.invalidTime;
}
time = this.fixHour(time + 0.5 / 60); // add 0.5 minutes to round
const hours = Math.floor(time);
const minutes = Math.floor((time - hours) * 60);
return this.twoDigitsFormat(hours) + ':' + this.twoDigitsFormat(minutes);
}
private floatToTime12(time: number, noSuffix: boolean = false): string {
if (isNaN(time)) {
return this.invalidTime;
}
time = this.fixHour(time + 0.5 / 60); // add 0.5 minutes to round
let hours = Math.floor(time);
const minutes = Math.floor((time - hours) * 60);
const suffix = hours >= 12 ? 'pm' : 'am';
hours = (hours + 12 - 1) % 12 + 1;
return hours + ':' + this.twoDigitsFormat(minutes) + (noSuffix ? '' : ' ' + suffix);
}
private twoDigitsFormat(num: number): string {
return num < 10 ? '0' + num : num.toString();
}
private eval(str: string | number | undefined): number {
if (str === undefined) {
return 0;
}
if (typeof str === 'number') {
return str;
}
const [value, unit] = str.split(' ');
return unit === 'min' ? parseInt(value) / 60 : parseInt(value);
}
private dSin(d: number): number {
return Math.sin(this.dToR(d));
}
private dCos(d: number): number {
return Math.cos(this.dToR(d));
}
private dTan(d: number): number {
return Math.tan(this.dToR(d));
}
private dAsin(d: number): number {
return this.rToD(Math.asin(d));
}
private dACos(d: number): number {
return this.rToD(Math.acos(d));
}
private dAtan(d: number): number {
return this.rToD(Math.atan(d));
}
private dAtan2(y: number, x: number): number {
return this.rToD(Math.atan2(y, x));
}
private dACot(x: number): number {
return this.rToD(Math.atan(1 / x));
}
private dToR(d: number): number {
return (d * Math.PI) / 180.0;
}
private rToD(r: number): number {
return (r * 180.0) / Math.PI;
}
private fixAngle(a: number): number {
a = a - 360.0 * Math.floor(a / 360.0);
return a < 0 ? a + 360.0 : a;
}
private fixHour(a: number): number {
a = a - 24.0 * Math.floor(a / 24.0);
return a < 0 ? a + 24.0 : a;
}
}
export const cities = {
Makkah: {
coords: [21.4225, 39.8262] as Coordinates,
timezone: 3,
},
Medina: {
coords: [24.4667, 39.6] as Coordinates,
timezone: 3,
},
Jeddah: {
coords: [21.5433, 39.1728] as Coordinates,
timezone: 3,
},
};
export default new PrayTimes('Makkah');