Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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
share|improve this question
add comment

Know someone who can answer? Share a link to this question via email, Google+, Twitter, or Facebook.

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.