unit PrayTimeLib; { ************************************************** Part of this lib which is in charge of calculating prayer times is reproduced from PrayTime javascript lib (v1.1): http://www.cs.uwaterloo.ca/~hzarrabi/praytime/doc/manual For any possible future update or change see: http://pmc.khone.ir ************************************************** This lib is licensed under the GNU LESSER GENERAL PUBLIC LICENSE ************************************************** } interface type prTimes = record Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha : Double; end; prTimesStr = record Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha : string; end; function getDatePrayerTimes(g_y, g_m, g_d : Word; latitude, longitude : Double; tZone : Double = 0; toMin : Boolean = False) : prTimesStr; procedure setCalcMethod(mtd : Word); procedure setAsrMethod(asmtd : Word); procedure setFajrAngle(angl : Double); procedure setMaghribAngle(angl : Double); procedure setIshaAngle(angl : Double); procedure setDayLightSaving(act : Boolean); function getDayLightSaving(): Boolean; implementation uses Math, SysUtils, Windows; const // Calculation Methods Jafari = 0; // Ithna Ashari Karachi = 1; // University of Islamic Sciences, Karachi ISNA = 2; // Islamic Society of North America (ISNA) MWL = 3; // Muslim World League (MWL) Makkah = 4; // Umm al-Qura, Makkah Egypt = 5; // Egyptian General Authority of Survey Custom = 6; // Custom Setting // Adjusting Methods for Higher Latitudes None = 0; // No adjustment MidNight = 1; // middle of night OneSeventh = 2; // 1/7th of night AngleBased = 3; // angle/60th of night InvalidTime = '00:00'; var calcMethod : Word = 0; // caculation method asrJuristic : Word = 0; // Juristic method for Asr dhuhrMinutes : Word = 0; // minutes after mid-day for Dhuhr adjustHighLats: Word = 1; // adjusting method for higher latitudes dayLightSavingChange : Boolean = False; JDate, lat, lng, timeZone : Double; methodParams : array [0..6] of array [0..4] of Double = ( (16, 0, 4, 0, 14), (18, 1, 0, 0, 18), (15, 1, 0, 0, 15), (18, 1, 0, 0, 17), (19, 1, 0, 1, 90), (19.5, 1, 0, 0, 17.5), (18, 1, 0, 0, 17) ); function fixAngle(a : Double) : Double; begin a := a - 360.0 * (Floor(a / 360.0)); if a < 0 then a := a + 360; Result := a; end; function dSin(a : Double) : Double; begin Result := Sin(DegToRad(a)); end; function dCos(a : Double) : Double; begin Result := Cos(DegToRad(a)); end; function dArcsin(a : Double) : Double; begin Result := RadToDeg(ArcSin(a)); end; function dArctan2(y, x : Double) : Double; begin Result := RadToDeg(ArcTan2(y, x)); end; function dArccos(a : Double) : Double; begin Result := RadToDeg(ArcCos(a)); end; function dArccot(a : Double) : Double; begin Result := RadToDeg(ArcCot(a)); end; function dTan(a : Double) : Double; begin Result := Tan(DegToRad(a)); end; function fixHour(a : Double) : Double; begin a := a - 24.0 * (Floor(a / 24.0)); if a < 0 then a := a + 24; Result := a; end; //Converts Gregorian date to Julian date function julianDate(g_y, g_m, g_d : Word) : Double; var A, B : Integer; begin if g_m < 2 then begin g_y := g_y - 1; g_m := g_m + 12; end; A := Floor(g_y / 100); B := 2 - A + Floor(A / 4); Result := Floor(365.25 * (g_y + 4716)) + Floor(30.6001 * (g_m + 1)) + g_d + B - 1524.5; end; //Compute declination angle of sun and equation of time procedure sunPosition(juD : Double; var sd, EqT : Double); var D, g, q, L, R, e, RA : Double; begin D := juD - 2451545.0; g := fixAngle(357.529 + 0.98560028* D); q := fixAngle(280.459 + 0.98564736* D); L := fixAngle(q + 1.915* dSin(g) + 0.020* dSin(2*g)); R := 1.00014 - 0.01671* dCos(g) - 0.00014* dCos(2*g); e := 23.439 - 0.00000036 * D; sd := dArcsin(dSin(e) * dSin(L)); RA := dArctan2(dCos(e)* dSin(L), dCos(L))/ 15; RA := fixHour(RA); EqT := q/15 - RA; end; function sunDeclination(juD : Double) : Double; var sd, EqT : Double; begin sunPosition(juD, sd, EqT); Result := sd; end; function equationOfTime(juD : Double) : Double; var sd, EqT : Double; begin sunPosition(juD, sd, EqT); Result := EqT; end; function computeMidDay(t : Double) : Double; var bT : Double; begin bT := equationOfTime(JDate + t); Result := fixHour(12- bT); end; function timeDiff(t1, t2 : Double) : Double; begin Result := fixhour(t2- t1); end; function floatToTime24(a : Double) : string; var hours, minutes : string; begin if (isNaN(a)) then Result := InvalidTime; a := fixHour(a + 0.5 / 60); // add 0.5 minutes to round hours := IntToStr(Floor(a)); minutes := IntToStr(Floor((a - Floor(a))* 60)); if Length(hours) < 2 then hours := '0' + hours; if Length(minutes) < 2 then minutes := '0' + minutes; Result := hours + ':' + minutes; end; function floatToTimeMin(a : Double) : string; begin if (isNaN(a)) then Result := InvalidTime; a := fixHour(a + 0.5 / 60); // add 0.5 minutes to round Result := IntToStr(Floor(a) * 60 + Floor((a - Floor(a))* 60)); end; function dayPortion(times : prTimes) : prTimes; begin Result.Fajr := times.Fajr / 24; Result.Sunrise := times.Sunrise / 24; Result.Dhuhr := times.Dhuhr / 24; Result.Asr := times.Asr / 24; Result.Sunset := times.Sunset / 24; Result.Maghrib := times.Maghrib / 24; Result.Isha := times.Isha / 24; end; function nightPortion(a : Double) : Double; begin Result := 1; if (adjustHighLats = AngleBased) then Result := 1/60* a; if (adjustHighLats = MidNight) then Result := 1/2; if (adjustHighLats = OneSeventh) then Result := 1/7; end; function computeTime(G, t : Double) : Double; var D, Z, V : Double; begin D := sunDeclination(JDate + t); Z := computeMidDay(t); V := 1/15* dArccos((-dSin(G)- dSin(D)* dSin(lat)) / (dCos(D)* dCos(lat))); if G > 90 then Result := Z - V else Result := Z + V; end; function computeAsr(step, t : Double) : Double; // Shafii: step=1, Hanafi: step=2 var D, G : Double; begin D := sunDeclination(JDate + t); G := -dArccot(step + dTan(Abs(lat - D))); Result := computeTime(G, t); end; function computeTimes(times : prTimes) : prTimes; var t : prTimes; begin t := dayPortion(times); Result.Fajr := computeTime(180- methodParams[calcMethod][0], t.Fajr); Result.Sunrise := computeTime(180- 0.833, t.Sunrise); Result.Dhuhr := computeMidDay(t.Dhuhr); Result.Asr := computeAsr(1 + asrJuristic, t.Asr); Result.Sunset := computeTime(0.833, t.Sunset); Result.Maghrib := computeTime(methodParams[calcMethod][2], t.Maghrib); Result.Isha := computeTime(methodParams[calcMethod][4], t.Isha); end; function adjustHighLatTimes(times : prTimes) : prTimes; var nightTime, FajrDiff, IshaAngle, IshaDiff, MaghribAngle, MaghribDiff : Double; begin nightTime := timeDiff(times.Sunset, times.Sunrise); // sunset to sunrise // Adjust Fajr FajrDiff := nightPortion(methodParams[calcMethod][0])* nightTime; if (isNaN(times.Fajr)) or (timeDiff(times.Fajr, times.Sunrise) > FajrDiff) then times.Fajr := times.Sunrise - FajrDiff; // Adjust Isha if (methodParams[calcMethod][3] = 0) then IshaAngle := methodParams[calcMethod][4] else IshaAngle := 18; IshaDiff := nightPortion(IshaAngle)* nightTime; if (isNaN(times.Isha)) or (timeDiff(times.Sunset, times.Isha) > IshaDiff) then times.Isha := times.Sunset + IshaDiff; // Adjust Maghrib if (methodParams[calcMethod][1] = 0) then MaghribAngle := methodParams[calcMethod][2] else MaghribAngle := 4; MaghribDiff := nightPortion(MaghribAngle)* nightTime; if (isNaN(times.Maghrib)) or (timeDiff(times.Sunset, times.Maghrib) > MaghribDiff) then times.Maghrib := times.Sunset + MaghribDiff; Result := times; end; function adjustTimes(times : prTimes) : prTimes; begin times.Fajr := times.Fajr + timeZone- lng / 15; times.Sunrise := times.Sunrise + timeZone- lng / 15; times.Dhuhr := times.Dhuhr + timeZone- lng / 15; times.Asr := times.Asr + timeZone- lng / 15; times.Sunset := times.Sunset + timeZone- lng / 15; times.Maghrib := times.Maghrib + timeZone- lng / 15; times.Isha := times.Isha + timeZone- lng / 15; times.Dhuhr := times.Dhuhr + dhuhrMinutes / 60; if (methodParams[calcMethod][1] = 1) then // Maghrib times.Maghrib := times.Sunset + methodParams[calcMethod][2] / 60; if (methodParams[calcMethod][3] = 1) then // Isha times.Isha := times.Maghrib + methodParams[calcMethod][4] / 60; if (adjustHighLats <> None) then times := adjustHighLatTimes(times); Result := times; end; // I need just 24H model, maybe later convert others function adjustTimesFormat(times : prTimes; toMin : Boolean = False) : prTimesStr; begin if not toMin then begin Result.Fajr := floatToTime24(times.Fajr); Result.Sunrise := floatToTime24(times.Sunrise); Result.Dhuhr := floatToTime24(times.Dhuhr); Result.Asr := floatToTime24(times.Asr); Result.Sunset := floatToTime24(times.Sunset); Result.Maghrib := floatToTime24(times.Maghrib); Result.Isha := floatToTime24(times.Isha); end else begin Result.Fajr := floatToTimeMin(times.Fajr); Result.Sunrise := floatToTimeMin(times.Sunrise); Result.Dhuhr := floatToTimeMin(times.Dhuhr); Result.Asr := floatToTimeMin(times.Asr); Result.Sunset := floatToTimeMin(times.Sunset); Result.Maghrib := floatToTimeMin(times.Maghrib); Result.Isha := floatToTimeMin(times.Isha); end; end; function computeDayTimes(toMin : Boolean = False) : prTimesStr; var times : prTimes; begin //default times times.Fajr := 5; times.Sunrise := 6; times.Dhuhr := 12; times.Asr := 13; times.Sunset := 18; times.Maghrib := 18; times.Isha := 18; times := computeTimes(times); Result := adjustTimesFormat(adjustTimes(times), toMin); end; function effectiveTimeZone(a : Double) : Double; var TZoneInfo: TTimeZoneInformation; begin if a = 0 then begin GetTimeZoneInformation(TZoneInfo); Result := - TZoneInfo.Bias / 60; end else Result := a; end; function getDatePrayerTimes(g_y, g_m, g_d : Word; latitude, longitude : Double; tZone : Double = 0; toMin : Boolean = False) : prTimesStr; begin lat := latitude; lng := longitude; timeZone := effectiveTimeZone(tZone); if dayLightSavingChange and not ( ( (g_m > 9) or ( (g_m = 9) and (g_d > 21) ) ) or ( (g_m < 3) or ( ( g_m = 3) and (g_d < 21) ) ) ) then timeZone := timeZone + 1; JDate := julianDate(g_y, g_m, g_d)- longitude / (15* 24); Result := computeDayTimes(toMin); end; procedure setCalcMethod(mtd : Word); begin if (mtd < 7) then calcMethod := mtd; end; procedure setAsrMethod(asmtd : Word); begin if asmtd < 2 then asrJuristic := asmtd; end; procedure setFajrAngle(angl : Double); begin methodParams[6][0] := angl; end; procedure setMaghribAngle(angl : Double); begin methodParams[6][1] := 0; methodParams[6][2] := angl; end; procedure setIshaAngle(angl : Double); begin methodParams[6][3] := 0; methodParams[6][4] := angl; end; procedure setDayLightSaving(act : Boolean); begin dayLightSavingChange := act; end; function getDayLightSaving(): Boolean; begin Result := dayLightSavingChange; end; end.