The JS file that I'm trying to convert it into Lua is found here: http://praytimes.org/code/v2/js/PrayTimes.js
You can tell from Lua code I wrote that I'm not a Lua expert but this is as far as I can go and I'm sure I have made something wrong here and there: http://pastebin.com/6BpC0ZPB (it is also shown below).
Tips and comments are more than welcome!
local M = {}
function M.PrayTimes(method)
------------------------ Constants --------------------------
-- Time Names
local timeNames = {
"Imsak",
"Fajr",
"Sunrise",
"Dhuhr",
"Asr",
"Sunset",
"Maghrib",
"Isha",
"Midnight"
}
-- Methods ID
methodsID = { "MWL","ISNA","Egypt","Makkah","Karachi","Tehran","Jafari"}
-- Calculation Methods
methods = {
{
id= "MWL",
name= "Muslim World League",
params= { fajr= 18, isha= 17 }
},
{
id= "ISNA",
name= "Islamic Society of North America (ISNA)",
params= { fajr= 15, isha= 15 }
},
{
id= "Egypt",
name= "Egyptian General Authority of Survey",
params= { fajr= 19.5, isha= 17.5 }
},
{
id= "Makkah",
name= "Umm Al-Qura University - Makkah",
params= { fajr= 18.5, isha= "90 min" } -- fajr was 19 degrees before 1430 hijri
},
{
id= "Karachi",
name= "University of Islamic Sciences - Karachi",
params= { fajr= 18, isha= 18 }
},
{
id= "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
},
{
id= "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 = 1
-- 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 = {}
params = {}
----------------------- Local Variables ---------------------
local lat, lng, elv -- coordinates
local timeZone, jDate -- time variables
---------------------- Initialization -----------------------
-- set methods defaults
local defParams = defaultParams
for i=1, #methods do
local params = methods[i].params
for j=1, #defParams do
if params[j] == "undefined" then
params[j] = defParams[j]
end
end
end
-- initialize settings
if methods[method] ~= nil then
calcMethod = method
else
calcMethod = calcMethod
end
local params = methods[calcMethod].params
for id=1, #params do
setting[id] = params[id]
end
-- init time offsets
for i=1, #timeNames do
offset[i] = 0
end
----------------------- Public Functions ------------------------
return {
-- set calculation method
function setMethod(method)
if (methods[method]) then
adjust(methods[method].params)
calcMethod = method
end
end
-- set calculating parameters
function adjust(params)
for id=1, #params do
setting[id] = params[id]
end
end
-- set time offsets
function tune(timeOffsets)
for i=1, #timeOffsets) do
offset[i] = timeOffsets[i]
end
end
-- get current calculation method
function getMethod() return calcMethod end
-- get current setting
function getSetting() return setting end
-- get current time offsets
function getOffsets() return offset end
-- get default calc parametrs
function getDefaults() return methods end
-- return prayer times for a given date
function getTimes(date, coords, timezone, dst, format)
lat = 1* coords[0]
lng = 1* coords[1]
if coords[2] ~= nil then
elv = 1* coords[2]
else
elv = 0
end
timeFormat = format || timeFormat
if (date.constructor === Date) then
date = [date.getFullYear(), date.getMonth()+ 1, date.getDate()] ------------------- change later
end
if (typeof(timezone) == "undefined" || timezone == "auto") then
timezone = getTimeZone(date)
end
if (typeof(dst) == "undefined" || dst == "auto") then
dst = getDst(date)
end
timeZone = 1* timezone+ (1* dst ? 1 : 0)
jDate = julian(date[0], date[1], date[2])- lng/ (15* 24)
return computeTimes()
end
-- convert float time to the given format (see timeFormats)
function getFormattedTime(time, format, suffixes)
if (math.isnan(time)) then
return invalidTime
end
if (format == "Float") then
return time
end
suffixes = suffixes or timeSuffixes
time = fixHour(time+ 0.5/ 60) -- add 0.5 minutes to round
local hours = math.floor(time)
local minutes = math.floor((time- hours)* 60)
if format == "12h" then
if hours < 12 then
suffix = 0
else
suffix = 1
end
else
suffix = nil
end
if format == "12h" then
twoDigitsFormat(hours)
else
hour = ((hours+ 12 -1)% 12+ 1)
end
if suffix ~= nil then
suffix = " " .. suffix
else
suffix = ""
end
return hour .. ":" .. twoDigitsFormat(minutes) .. suffix
end
------------------------ Calculation Functions -----------------------
-- compute mid-day time
function midDay(time)
local eqt = sunPosition(jDate+ time).equation
local noon = fixHour(12- eqt)
return noon
end
-- compute the time at which sun reaches a specific angle below horizon
function sunAngleTime(angle, time, direction)
local decl = sunPosition(jDate+ time).declination
local noon = midDay(time)
local t = 1/15* Darccos((-Dsin(angle)- Dsin(decl)* Dsin(lat))/(Dcos(decl)* Dcos(lat)))
if (direction == 'ccw') then
t = -t
else
t = t
end
return noon+ t;
end
-- compute asr time
function asrTime(factor, time)
local decl = sunPosition(jDate+ time).declination
local angle = -Darccot(factor+ Dtan(Math.abs(lat- decl)))
return sunAngleTime(angle, time)
end
-- compute declination angle of sun and equation of time
-- Ref: http://aa.usno.navy.mil/faq/docs/SunApprox.php
function sunPosition(jd)
local D = jd - 2451545.0
local g = fixAngle(357.529 + 0.98560028* D)
local q = fixAngle(280.459 + 0.98564736* D)
local L = fixAngle(q + 1.915* Dsin(g) + 0.020* Dsin(2*g))
local R = 1.00014 - 0.01671* Dcos(g) - 0.00014* Dcos(2*g)
local e = 23.439 - 0.00000036* D
local RA = Darctan2(Dcos(e)* Dsin(L), Dcos(L))/ 15
local eqt = q/15 - fixHour(RA)
local decl = Darcsin(Dsin(e)* Dsin(L))
return {declination= decl, equation= eqt}
end
-- convert Gregorian date to Julian day
-- Ref: Astronomical Algorithms by Jean Meeus
julian: function(year, month, day) {
if (month <= 2) the
year = year - 1
month = month + 12
end
local A = math.floor(year/ 100)
local B = 2- A+ math.floor(A/ 4)
local JD = math.floor(365.25* (year+ 4716))+ math.floor(30.6001* (month+ 1))+ day+ B- 1524.5
return JD
end
------------------------ Compute Prayer Times -----------------------
-- compute prayer times at given julian date
function computePrayerTimes(times)
times = dayPortion(times)
local params = setting
local imsak = sunAngleTime(eval(params.imsak), times.imsak, 'ccw')
local fajr = sunAngleTime(eval(params.fajr), times.fajr, 'ccw')
local sunrise = sunAngleTime(riseSetAngle(), times.sunrise, 'ccw')
local dhuhr = midDay(times.dhuhr)
local asr = asrTime(asrFactor(params.asr), times.asr)
local sunset = sunAngleTime(riseSetAngle(), times.sunset)
local maghrib = sunAngleTime(eval(params.maghrib), times.maghrib)
local isha = sunAngleTime(eval(params.isha), times.isha)
return {
imsak= imsak, fajr= fajr, sunrise= sunrise, dhuhr= dhuhr,
asr= asr, sunset= sunset, maghrib= maghrib, isha= isha
}
end
-- compute prayer times
function computeTimes()
-- default times
local times = {
imsak: 5, fajr: 5, sunrise: 6, dhuhr: 12,
asr: 13, sunset: 18, maghrib: 18, isha: 18
}
-- main iterations
for i=1, #numIterations do
times = computePrayerTimes(times)
end
times = adjustTimes(times)
-- add midnight time
if setting.midnight == 'Jafari') then
times.midnight = times.sunset+ timeDiff(times.sunset, times.fajr)/ 2
else
times.midnight = times.sunset+ timeDiff(times.sunset, times.sunrise)/ 2
end
times = tuneTimes(times)
return modifyFormats(times)
end
-- adjust times
function adjustTimes(times)
local params = setting;
for i=1, #times do
times[i] = times[i] + timeZone- lng/ 15
end
if (params.highLats ~= "None") then
times = adjustHighLats(times)
end
if (isMin(params.imsak)) then
times.imsak = times.fajr- eval(params.imsak)/ 60
end
if (isMin(params.maghrib)) then
times.maghrib = times.sunset+ eval(params.maghrib)/ 60
end
if (isMin(params.isha)) then
times.isha = times.maghrib+ eval(params.isha)/ 60
end
times.dhuhr += eval(params.dhuhr)/ 60
return times
end
-- get asr shadow factor
function asrFactor(asrParam)
local factor = {Standard= 1, Hanafi= 2}[asrParam];
return factor or eval(asrParam)
end
-- return sun angle for sunset/sunrise
function riseSetAngle() {
local angle = 0.0347* math.sqrt(elv) -- an approximation
return 0.833+ angle
end
-- apply offsets to the times
function tuneTimes(times)
for i=1, #times do
times[i] = times[i] + offset[i]/ 60
end
return times
end
-- convert times to given time format
function modifyFormats(times)
for i=1, #times do
times[i] = getFormattedTime(times[i], timeFormat);
end
return times
end
-- adjust times for locations in higher latitudes
function adjustHighLats(times)
local params = setting
local nightTime = timeDiff(times.sunset, times.sunrise)
times.imsak = adjustHLTime(times.imsak, times.sunrise, eval(params.imsak), nightTime, 'ccw')
times.fajr = adjustHLTime(times.fajr, times.sunrise, eval(params.fajr), nightTime, 'ccw')
times.isha = adjustHLTime(times.isha, times.sunset, eval(params.isha), nightTime)
times.maghrib = adjustHLTime(times.maghrib, times.sunset, eval(params.maghrib), nightTime)
return times
end
-- adjust a time for higher latitudes
function adjustHLTime(time, base, angle, night, direction) {
local portion = nightPortion(angle, night);
local timeDiff
if direction == "ccw" then
timeDiff = timeDiff(time, base)
portion = -portion
else
timeDiff = timeDiff(base, time)
portion = portion
end
if (math.isnan(time) or timeDiff > portion) then
time = base .. portion
end
return time
end
-- the night portion used for adjusting times in higher latitudes
function nightPortion(angle, night)
local method = setting.highLats
local portion = 1/2 -- MidNight
if (method == "AngleBased") then
portion = 1/60* angle
elseif (method == "OneSeventh") then
portion = 1/7
end
return portion* night
end
-- convert hours to day portions
function dayPortion(times)
for i=1, #times do
times[i] = times[i] / 24
end
return times
end
------------------------ Time Zone Functions -----------------------
-- get local time zone
function getTimeZone(date)
local year = date[0]
local t1 = gmtOffset([year, 0, 1])
local t2 = gmtOffset([year, 6, 1])
return math.min(t1, t2)
end
-- get daylight saving for a given date
function getDst(date)
return 1* (gmtOffset(date) ~= getTimeZone(date));
end
-- GMT offset for a given date
function gmtOffset(date)
local localDate = new Date(date[0], date[1]- 1, date[2], 12, 0, 0, 0)
local GMTString = localDate.toGMTString()
local GMTDate = new Date(GMTString.substring(0, GMTString.lastIndexOf(' ')- 1))
local hoursDiff = (localDate- GMTDate) / (1000* 60* 60)
return hoursDiff
end
------------------------ Misc Functions -----------------------
-- convert given string into a number
function eval(str)
tonumber(str)
return str
end
-- detect if input contains 'min'
function isMin(arg)
onlyDigit=arg:match('(%d+)')
return onlyDigit
end
-- compute the difference between two times
function timeDiff(time1, time2)
return fixHour(time2- time1)
end
-- add a leading 0 if necessary
function twoDigitsFormat(num)
if num < 10 then
num = "0" .. num
else
-- nothing
end
return num
end
} -- end return
end
return M
------------------------ Degree-Based Math Class -----------------------
function dtr(d) return (d * math.pi) / 180.0 end
function rtd(r) return (r * 180.0) / math.pi end
function Dsin(d) return math.sin(dtr(d)) end
function Dcos(d) return math.cos(dtr(d)) end
function Dtan(d) return math.tan(dtr(d)) end
function Darcsin(d) return rtd(math.asin(d)) end
function Darccos(d) return rtd(math.acos(d)) end
function Darctan(d) return rtd(math.atan(d)) end
function Darccot(x) return rtd(math.atan(1/x)) end
function Darctan2(y, x) return rtd(math.atan2(y, x)) end
function fixAngle(a) return fix(a, 360); end
function fixHour(a) return fix(a, 24); end
function fix(a, b)
a = a- b* (math.floor(a/ b));
if a < 0 then
a = a+ b
else
a = a
end
return a
end
-- table.indexOf( array, object ) returns the index
-- of object in array. Returns 'nil' if not in array.
--[[
Example Usage:
local t = {1,3,5,7,9}
print( table.indexOf( t, 9 ) ) -- output: 5
]]--
table.indexOf = function( t, object )
local result
if "table" == type( t ) then
for i=1,#t do
if object == t[i] then
result = i
break
end
end
end
return result
end