|
//--------------------- Copyright Block ---------------------- |
|
/* |
|
|
|
PrayTimes.js: Prayer Times Calculator (ver 2.3) |
|
Copyright (C) 2007-2011 PrayTimes.org |
|
|
|
Developer: 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 credit is given to the original work |
|
with a link back to PrayTimes.org. |
|
|
|
This program is distributed in the hope that it will |
|
be useful, but WITHOUT ANY WARRANTY. |
|
|
|
PLEASE DO NOT REMOVE THIS COPYRIGHT BLOCK. |
|
|
|
*/ |
|
|
|
|
|
//--------------------- Help and Manual ---------------------- |
|
/* |
|
|
|
User's Manual: |
|
http://praytimes.org/manual |
|
|
|
Calculation Formulas: |
|
http://praytimes.org/calculation |
|
|
|
|
|
|
|
//------------------------ User Interface ------------------------- |
|
|
|
|
|
getTimes (date, coordinates [, timeZone [, dst [, timeFormat]]]) |
|
|
|
setMethod (method) // set calculation method |
|
adjust (parameters) // adjust calculation parameters |
|
tune (offsets) // tune times by given offsets |
|
|
|
getMethod () // get calculation method |
|
getSetting () // get current calculation parameters |
|
getOffsets () // get current time offsets |
|
|
|
|
|
//------------------------- Sample Usage -------------------------- |
|
|
|
|
|
var PT = new PrayTimes('ISNA'); |
|
var times = PT.getTimes(new Date(), [43, -80], -5); |
|
document.write('Sunrise = '+ times.sunrise) |
|
|
|
|
|
*/ |
|
|
|
|
|
//----------------------- PrayTimes Class ------------------------ |
|
|
|
|
|
function PrayTimes(method) { |
|
|
|
|
|
//------------------------ Constants -------------------------- |
|
var |
|
|
|
// Time Names |
|
timeNames = { |
|
imsak : 'Imsak', |
|
fajr : 'Fajr', |
|
sunrise : 'Sunrise', |
|
dhuhr : 'Dhuhr', |
|
asr : 'Asr', |
|
sunset : 'Sunset', |
|
maghrib : 'Maghrib', |
|
isha : 'Isha', |
|
midnight : 'Midnight' |
|
}, |
|
|
|
|
|
// Calculation Methods |
|
methods = { |
|
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' } }, // fajr was 19 degrees before 1430 hijri |
|
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' } }, // isha is not explicitly specified in this method |
|
Jafari: { |
|
name: 'Shia Ithna-Ashari, Leva Institute, Qum', |
|
params: { fajr: 16, isha: 14, maghrib: 4, midnight: 'Jafari' } } |
|
}, |
|
|
|
|
|
// Default Parameters in Calculation Methods |
|
defaultParams = { |
|
maghrib: '0 min', midnight: 'Standard' |
|
|
|
}, |
|
|
|
|
|
//----------------------- Parameter Values ---------------------- |
|
/* |
|
|
|
// Asr Juristic Methods |
|
asrJuristics = [ |
|
'Standard', // Shafi`i, Maliki, Ja`fari, Hanbali |
|
'Hanafi' // Hanafi |
|
], |
|
|
|
|
|
// Midnight Mode |
|
midnightMethods = [ |
|
'Standard', // Mid Sunset to Sunrise |
|
'Jafari' // Mid Sunset to Fajr |
|
], |
|
|
|
|
|
// Adjust Methods for Higher Latitudes |
|
highLatMethods = [ |
|
'NightMiddle', // middle of night |
|
'AngleBased', // angle/60th of night |
|
'OneSeventh', // 1/7th of night |
|
'None' // No adjustment |
|
], |
|
|
|
|
|
// Time Formats |
|
timeFormats = [ |
|
'24h', // 24-hour format |
|
'12h', // 12-hour format |
|
'12hNS', // 12-hour format with no suffix |
|
'Float' // floating point number |
|
], |
|
*/ |
|
|
|
|
|
//---------------------- Default Settings -------------------- |
|
|
|
calcMethod = 'MWL', |
|
|
|
// do not change anything here; use adjust method instead |
|
setting = { |
|
imsak : '10 min', |
|
dhuhr : '0 min', |
|
asr : 'Standard', |
|
highLats : 'NightMiddle' |
|
}, |
|
|
|
timeFormat = '24h', |
|
timeSuffixes = ['am', 'pm'], |
|
invalidTime = '-----', |
|
|
|
numIterations = 1, |
|
offset = {}, |
|
|
|
|
|
//----------------------- Local Variables --------------------- |
|
|
|
lat, lng, elv, // coordinates |
|
timeZone, jDate; // time variables |
|
|
|
|
|
//---------------------- Initialization ----------------------- |
|
|
|
|
|
// set methods defaults |
|
var defParams = defaultParams; |
|
for (var i in methods) { |
|
var params = methods[i].params; |
|
for (var j in defParams) |
|
if ((typeof(params[j]) == 'undefined')) |
|
params[j] = defParams[j]; |
|
}; |
|
|
|
// initialize settings |
|
calcMethod = methods[method] ? method : calcMethod; |
|
var params = methods[calcMethod].params; |
|
for (var id in params) |
|
setting[id] = params[id]; |
|
|
|
// init time offsets |
|
for (var i in timeNames) |
|
offset[i] = 0; |
|
|
|
|
|
|
|
//----------------------- Public Functions ------------------------ |
|
return { |
|
|
|
|
|
// set calculation method |
|
setMethod: function(method) { |
|
if (methods[method]) { |
|
this.adjust(methods[method].params); |
|
calcMethod = method; |
|
} |
|
}, |
|
|
|
|
|
// set calculating parameters |
|
adjust: function(params) { |
|
for (var id in params) |
|
setting[id] = params[id]; |
|
}, |
|
|
|
|
|
// set time offsets |
|
tune: function(timeOffsets) { |
|
for (var i in timeOffsets) |
|
offset[i] = timeOffsets[i]; |
|
}, |
|
|
|
|
|
// get current calculation method |
|
getMethod: function() { return calcMethod; }, |
|
|
|
// get current setting |
|
getSetting: function() { return setting; }, |
|
|
|
// get current time offsets |
|
getOffsets: function() { return offset; }, |
|
|
|
// get default calc parametrs |
|
getDefaults: function() { return methods; }, |
|
|
|
|
|
// return prayer times for a given date |
|
getTimes: function(date, coords, timezone, dst, format) { |
|
lat = 1* coords[0]; |
|
lng = 1* coords[1]; |
|
elv = coords[2] ? 1* coords[2] : 0; |
|
timeFormat = format || timeFormat; |
|
if (date.constructor === Date) |
|
date = [date.getFullYear(), date.getMonth()+ 1, date.getDate()]; |
|
if (typeof(timezone) == 'undefined' || timezone == 'auto') |
|
timezone = this.getTimeZone(date); |
|
if (typeof(dst) == 'undefined' || dst == 'auto') |
|
dst = this.getDst(date); |
|
timeZone = 1* timezone+ (1* dst ? 1 : 0); |
|
jDate = this.julian(date[0], date[1], date[2])- lng/ (15* 24); |
|
|
|
return this.computeTimes(); |
|
}, |
|
|
|
|
|
// convert float time to the given format (see timeFormats) |
|
getFormattedTime: function(time, format, suffixes) { |
|
if (isNaN(time)) |
|
return invalidTime; |
|
if (format == 'Float') return time; |
|
suffixes = suffixes || timeSuffixes; |
|
|
|
time = DMath.fixHour(time+ 0.5/ 60); // add 0.5 minutes to round |
|
var hours = Math.floor(time); |
|
var minutes = Math.floor((time- hours)* 60); |
|
var suffix = (format == '12h') ? suffixes[hours < 12 ? 0 : 1] : ''; |
|
var hour = (format == '24h') ? this.twoDigitsFormat(hours) : ((hours+ 12 -1)% 12+ 1); |
|
return hour+ ':'+ this.twoDigitsFormat(minutes)+ (suffix ? ' '+ suffix : ''); |
|
}, |
|
|
|
|
|
//---------------------- Calculation Functions ----------------------- |
|
|
|
|
|
// compute mid-day time |
|
midDay: function(time) { |
|
var eqt = this.sunPosition(jDate+ time).equation; |
|
var noon = DMath.fixHour(12- eqt); |
|
return noon; |
|
}, |
|
|
|
|
|
// compute the time at which sun reaches a specific angle below horizon |
|
sunAngleTime: function(angle, time, direction) { |
|
var decl = this.sunPosition(jDate+ time).declination; |
|
var noon = this.midDay(time); |
|
var t = 1/15* DMath.arccos((-DMath.sin(angle)- DMath.sin(decl)* DMath.sin(lat))/ |
|
(DMath.cos(decl)* DMath.cos(lat))); |
|
return noon+ (direction == 'ccw' ? -t : t); |
|
}, |
|
|
|
|
|
// compute asr time |
|
asrTime: function(factor, time) { |
|
var decl = this.sunPosition(jDate+ time).declination; |
|
var angle = -DMath.arccot(factor+ DMath.tan(Math.abs(lat- decl))); |
|
return this.sunAngleTime(angle, time); |
|
}, |
|
|
|
|
|
// compute declination angle of sun and equation of time |
|
// Ref: http://aa.usno.navy.mil/faq/docs/SunApprox.php |
|
sunPosition: function(jd) { |
|
var D = jd - 2451545.0; |
|
var g = DMath.fixAngle(357.529 + 0.98560028* D); |
|
var q = DMath.fixAngle(280.459 + 0.98564736* D); |
|
var L = DMath.fixAngle(q + 1.915* DMath.sin(g) + 0.020* DMath.sin(2*g)); |
|
|
|
var R = 1.00014 - 0.01671* DMath.cos(g) - 0.00014* DMath.cos(2*g); |
|
var e = 23.439 - 0.00000036* D; |
|
|
|
var RA = DMath.arctan2(DMath.cos(e)* DMath.sin(L), DMath.cos(L))/ 15; |
|
var eqt = q/15 - DMath.fixHour(RA); |
|
var decl = DMath.arcsin(DMath.sin(e)* DMath.sin(L)); |
|
|
|
return {declination: decl, equation: eqt}; |
|
}, |
|
|
|
|
|
// convert Gregorian date to Julian day |
|
// Ref: Astronomical Algorithms by Jean Meeus |
|
julian: function(year, month, day) { |
|
if (month <= 2) { |
|
year -= 1; |
|
month += 12; |
|
}; |
|
var A = Math.floor(year/ 100); |
|
var B = 2- A+ Math.floor(A/ 4); |
|
|
|
var JD = Math.floor(365.25* (year+ 4716))+ Math.floor(30.6001* (month+ 1))+ day+ B- 1524.5; |
|
return JD; |
|
}, |
|
|
|
|
|
//---------------------- Compute Prayer Times ----------------------- |
|
|
|
|
|
// compute prayer times at given julian date |
|
computePrayerTimes: function(times) { |
|
times = this.dayPortion(times); |
|
var params = setting; |
|
|
|
var imsak = this.sunAngleTime(this.eval(params.imsak), times.imsak, 'ccw'); |
|
var fajr = this.sunAngleTime(this.eval(params.fajr), times.fajr, 'ccw'); |
|
var sunrise = this.sunAngleTime(this.riseSetAngle(), times.sunrise, 'ccw'); |
|
var dhuhr = this.midDay(times.dhuhr); |
|
var asr = this.asrTime(this.asrFactor(params.asr), times.asr); |
|
var sunset = this.sunAngleTime(this.riseSetAngle(), times.sunset);; |
|
var maghrib = this.sunAngleTime(this.eval(params.maghrib), times.maghrib); |
|
var isha = this.sunAngleTime(this.eval(params.isha), times.isha); |
|
|
|
return { |
|
imsak: imsak, fajr: fajr, sunrise: sunrise, dhuhr: dhuhr, |
|
asr: asr, sunset: sunset, maghrib: maghrib, isha: isha |
|
}; |
|
}, |
|
|
|
|
|
// compute prayer times |
|
computeTimes: function() { |
|
// default times |
|
var times = { |
|
imsak: 5, fajr: 5, sunrise: 6, dhuhr: 12, |
|
asr: 13, sunset: 18, maghrib: 18, isha: 18 |
|
}; |
|
|
|
// main iterations |
|
for (var i=1 ; i<=numIterations ; i++) |
|
times = this.computePrayerTimes(times); |
|
|
|
times = this.adjustTimes(times); |
|
|
|
// add midnight time |
|
times.midnight = (setting.midnight == 'Jafari') ? |
|
times.sunset+ this.timeDiff(times.sunset, times.fajr)/ 2 : |
|
times.sunset+ this.timeDiff(times.sunset, times.sunrise)/ 2; |
|
|
|
times = this.tuneTimes(times); |
|
return this.modifyFormats(times); |
|
}, |
|
|
|
|
|
// adjust times |
|
adjustTimes: function(times) { |
|
var params = setting; |
|
for (var i in times) |
|
times[i] += timeZone- lng/ 15; |
|
|
|
if (params.highLats != 'None') |
|
times = this.adjustHighLats(times); |
|
|
|
if (this.isMin(params.imsak)) |
|
times.imsak = times.fajr- this.eval(params.imsak)/ 60; |
|
if (this.isMin(params.maghrib)) |
|
times.maghrib = times.sunset+ this.eval(params.maghrib)/ 60; |
|
if (this.isMin(params.isha)) |
|
times.isha = times.maghrib+ this.eval(params.isha)/ 60; |
|
times.dhuhr += this.eval(params.dhuhr)/ 60; |
|
|
|
return times; |
|
}, |
|
|
|
|
|
// get asr shadow factor |
|
asrFactor: function(asrParam) { |
|
var factor = {Standard: 1, Hanafi: 2}[asrParam]; |
|
return factor || this.eval(asrParam); |
|
}, |
|
|
|
|
|
// return sun angle for sunset/sunrise |
|
riseSetAngle: function() { |
|
//var earthRad = 6371009; // in meters |
|
//var angle = DMath.arccos(earthRad/(earthRad+ elv)); |
|
var angle = 0.0347* Math.sqrt(elv); // an approximation |
|
return 0.833+ angle; |
|
}, |
|
|
|
|
|
// apply offsets to the times |
|
tuneTimes: function(times) { |
|
for (var i in times) |
|
times[i] += offset[i]/ 60; |
|
return times; |
|
}, |
|
|
|
|
|
// convert times to given time format |
|
modifyFormats: function(times) { |
|
for (var i in times) |
|
times[i] = this.getFormattedTime(times[i], timeFormat); |
|
return times; |
|
}, |
|
|
|
|
|
// adjust times for locations in higher latitudes |
|
adjustHighLats: function(times) { |
|
var params = setting; |
|
var nightTime = this.timeDiff(times.sunset, times.sunrise); |
|
|
|
times.imsak = this.adjustHLTime(times.imsak, times.sunrise, this.eval(params.imsak), nightTime, 'ccw'); |
|
times.fajr = this.adjustHLTime(times.fajr, times.sunrise, this.eval(params.fajr), nightTime, 'ccw'); |
|
times.isha = this.adjustHLTime(times.isha, times.sunset, this.eval(params.isha), nightTime); |
|
times.maghrib = this.adjustHLTime(times.maghrib, times.sunset, this.eval(params.maghrib), nightTime); |
|
|
|
return times; |
|
}, |
|
|
|
|
|
// adjust a time for higher latitudes |
|
adjustHLTime: function(time, base, angle, night, direction) { |
|
var portion = this.nightPortion(angle, night); |
|
var timeDiff = (direction == 'ccw') ? |
|
this.timeDiff(time, base): |
|
this.timeDiff(base, time); |
|
if (isNaN(time) || timeDiff > portion) |
|
time = base+ (direction == 'ccw' ? -portion : portion); |
|
return time; |
|
}, |
|
|
|
|
|
// the night portion used for adjusting times in higher latitudes |
|
nightPortion: function(angle, night) { |
|
var method = setting.highLats; |
|
var portion = 1/2 // MidNight |
|
if (method == 'AngleBased') |
|
portion = 1/60* angle; |
|
if (method == 'OneSeventh') |
|
portion = 1/7; |
|
return portion* night; |
|
}, |
|
|
|
|
|
// convert hours to day portions |
|
dayPortion: function(times) { |
|
for (var i in times) |
|
times[i] /= 24; |
|
return times; |
|
}, |
|
|
|
|
|
//---------------------- Time Zone Functions ----------------------- |
|
|
|
|
|
// get local time zone |
|
getTimeZone: function(date) { |
|
var year = date[0]; |
|
var t1 = this.gmtOffset([year, 0, 1]); |
|
var t2 = this.gmtOffset([year, 6, 1]); |
|
return Math.min(t1, t2); |
|
}, |
|
|
|
|
|
// get daylight saving for a given date |
|
getDst: function(date) { |
|
return 1* (this.gmtOffset(date) != this.getTimeZone(date)); |
|
}, |
|
|
|
|
|
// GMT offset for a given date |
|
gmtOffset: function(date) { |
|
var localDate = new Date(date[0], date[1]- 1, date[2], 12, 0, 0, 0); |
|
var GMTString = localDate.toGMTString(); |
|
var GMTDate = new Date(GMTString.substring(0, GMTString.lastIndexOf(' ')- 1)); |
|
var hoursDiff = (localDate- GMTDate) / (1000* 60* 60); |
|
return hoursDiff; |
|
}, |
|
|
|
|
|
//---------------------- Misc Functions ----------------------- |
|
|
|
// convert given string into a number |
|
eval: function(str) { |
|
return 1* (str+ '').split(/[^0-9.+-]/)[0]; |
|
}, |
|
|
|
|
|
// detect if input contains 'min' |
|
isMin: function(arg) { |
|
return (arg+ '').indexOf('min') != -1; |
|
}, |
|
|
|
|
|
// compute the difference between two times |
|
timeDiff: function(time1, time2) { |
|
return DMath.fixHour(time2- time1); |
|
}, |
|
|
|
|
|
// add a leading 0 if necessary |
|
twoDigitsFormat: function(num) { |
|
return (num <10) ? '0'+ num : num; |
|
} |
|
|
|
}} |
|
|
|
|
|
|
|
//---------------------- Degree-Based Math Class ----------------------- |
|
|
|
|
|
var DMath = { |
|
|
|
dtr: function(d) { return (d * Math.PI) / 180.0; }, |
|
rtd: function(r) { return (r * 180.0) / Math.PI; }, |
|
|
|
sin: function(d) { return Math.sin(this.dtr(d)); }, |
|
cos: function(d) { return Math.cos(this.dtr(d)); }, |
|
tan: function(d) { return Math.tan(this.dtr(d)); }, |
|
|
|
arcsin: function(d) { return this.rtd(Math.asin(d)); }, |
|
arccos: function(d) { return this.rtd(Math.acos(d)); }, |
|
arctan: function(d) { return this.rtd(Math.atan(d)); }, |
|
|
|
arccot: function(x) { return this.rtd(Math.atan(1/x)); }, |
|
arctan2: function(y, x) { return this.rtd(Math.atan2(y, x)); }, |
|
|
|
fixAngle: function(a) { return this.fix(a, 360); }, |
|
fixHour: function(a) { return this.fix(a, 24 ); }, |
|
|
|
fix: function(a, b) { |
|
a = a- b* (Math.floor(a/ b)); |
|
return (a < 0) ? a+ b : a; |
|
} |
|
} |
|
|
|
|
|
//---------------------- Init Object ----------------------- |
|
|
|
|
|
var prayTimes = new PrayTimes(); |