Created
January 14, 2014 21:51
-
-
Save khelll/8426512 to your computer and use it in GitHub Desktop.
Fix for the python code version 2 of the Pray Times algorithm http://praytimes.org/code/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# compatible with python 2.x and 3.x | |
import math | |
import re | |
''' | |
--------------------- Copyright Block ---------------------- | |
praytimes.py: Prayer Times Calculator (ver 2.3) | |
Copyright (C) 2007-2011 PrayTimes.org | |
Python Code: Saleem Shafi, Hamid Zarrabi-Zadeh and Khaled alHabache | |
Original js Code: 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 -------------------------- | |
>>> PT = PrayTimes('ISNA') | |
>>> times = PT.getTimes((2011, 2, 9), (43, -80), -5) | |
>>> times['sunrise'] | |
07:26 | |
''' | |
#----------------------- PrayTimes Class ------------------------ | |
class PrayTimes(): | |
#------------------------ Constants -------------------------- | |
# 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' | |
} | |
#---------------------- Default Settings -------------------- | |
calcMethod = 'MWL' | |
# do not change anything here; use adjust method instead | |
settings = { | |
"imsak" : '10 min', | |
"dhuhr" : '0 min', | |
"asr" : 'Standard', | |
"highLats" : 'NightMiddle' | |
} | |
timeFormat = '24h' | |
timeSuffixes = ['am', 'pm'] | |
invalidTime = '-----' | |
numIterations = 1 | |
offset = {} | |
#---------------------- Initialization ----------------------- | |
def __init__(self, calMethod = "MWL") : | |
# set methods defaults | |
for method, config in self.methods.items(): | |
for name, value in self.defaultParams.items(): | |
if not name in config['params'] or config['params'][name] is None: | |
config['params'][name] = value | |
# initialize settings | |
self.calcMethod = calMethod if method in self.methods else 'MWL' | |
params = self.methods[self.calcMethod]['params'] | |
for name, value in params.items(): | |
self.settings[name] = value | |
# init time offsets | |
for name in self.timeNames: | |
self.offset[name] = 0 | |
#-------------------- Interface Functions -------------------- | |
def setMethod(self, method): | |
if method in self.methods: | |
self.adjust(self.methods[method].params) | |
self.calcMethod = method | |
def adjust(self, params): | |
self.settings.update(params) | |
def tune(self, timeOffsets): | |
self.offsets.update(timeOffsets) | |
def getMethod(self): | |
return self.calcMethod | |
def getSettings(self): | |
return self.settings | |
def getOffsets(self): | |
return self.offset | |
def getDefaults(self): | |
return self.methods | |
# return prayer times for a given date | |
def getTimes(self, date, coords, timezone, dst = 0, format = None): | |
self.lat = coords[0] | |
self.lng = coords[1] | |
self.elv = coords[2] if len(coords)>2 else 0 | |
if format != None: | |
self.timeFormat = format | |
if type(date).__name__ == 'date': | |
date = (date.year, date.month, date.day) | |
self.timeZone = timezone + (1 if dst else 0) | |
self.jDate = self.julian(date[0], date[1], date[2]) - self.lng / (15 * 24.0) | |
return self.computeTimes() | |
# convert float time to the given format (see timeFormats) | |
def getFormattedTime(self, time, format, suffixes = None): | |
if math.isnan(time): | |
return self.invalidTime | |
if format == 'Float': | |
return time | |
if suffixes == None: | |
suffixes = self.timeSuffixes | |
time = self.fixhour(time+ 0.5/ 60) # add 0.5 minutes to round | |
hours = math.floor(time) | |
minutes = math.floor((time- hours)* 60) | |
suffix = suffixes[ 0 if hours < 12 else 1 ] if format == '12h' else '' | |
formattedTime = "%02d:%02d" % (hours, minutes) if format == "24h" else "%d:%02d" % ((hours+11)%12+1, minutes) | |
return formattedTime + suffix | |
#---------------------- Calculation Functions ----------------------- | |
# compute mid-day time | |
def midDay(self, time): | |
eqt = self.sunPosition(self.jDate + time)[1] | |
return self.fixhour(12 - eqt) | |
# compute the time at which sun reaches a specific angle below horizon | |
def sunAngleTime(self, angle, time, direction = None): | |
try: | |
decl = self.sunPosition(self.jDate + time)[0] | |
noon = self.midDay(time) | |
t = 1/15.0* self.arccos((-self.sin(angle)- self.sin(decl)* self.sin(self.lat))/ | |
(self.cos(decl)* self.cos(self.lat))) | |
return noon+ (-t if direction == 'ccw' else t) | |
except ValueError: | |
return float('nan') | |
# compute asr time | |
def asrTime(self, factor, time): | |
decl = self.sunPosition(self.jDate + time)[0] | |
angle = -self.arccot(factor + self.tan(abs(self.lat - decl))) | |
return self.sunAngleTime(angle, time) | |
# compute declination angle of sun and equation of time | |
# Ref: http://aa.usno.navy.mil/faq/docs/SunApprox.php | |
def sunPosition(self, jd): | |
D = jd - 2451545.0 | |
g = self.fixangle(357.529 + 0.98560028* D) | |
q = self.fixangle(280.459 + 0.98564736* D) | |
L = self.fixangle(q + 1.915* self.sin(g) + 0.020* self.sin(2*g)) | |
R = 1.00014 - 0.01671*self.cos(g) - 0.00014*self.cos(2*g) | |
e = 23.439 - 0.00000036* D | |
RA = self.arctan2(self.cos(e)* self.sin(L), self.cos(L))/ 15.0 | |
eqt = q/15.0 - self.fixhour(RA) | |
decl = self.arcsin(self.sin(e)* self.sin(L)) | |
return (decl, eqt) | |
# convert Gregorian date to Julian day | |
# Ref: Astronomical Algorithms by Jean Meeus | |
def julian(self, year, month, day): | |
if month <= 2: | |
year -= 1 | |
month += 12 | |
A = math.floor(year / 100) | |
B = 2 - A + math.floor(A / 4) | |
return math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + day + B - 1524.5 | |
#---------------------- Compute Prayer Times ----------------------- | |
# compute prayer times at given julian date | |
def computePrayerTimes(self, times): | |
times = self.dayPortion(times) | |
params = self.settings | |
imsak = self.sunAngleTime(self.eval(params['imsak']), times['imsak'], 'ccw') | |
fajr = self.sunAngleTime(self.eval(params['fajr']), times['fajr'], 'ccw') | |
sunrise = self.sunAngleTime(self.riseSetAngle(self.elv), times['sunrise'], 'ccw') | |
dhuhr = self.midDay(times['dhuhr']) | |
asr = self.asrTime(self.asrFactor(params['asr']), times['asr']) | |
sunset = self.sunAngleTime(self.riseSetAngle(self.elv), times['sunset']) | |
maghrib = self.sunAngleTime(self.eval(params['maghrib']), times['maghrib']) | |
isha = self.sunAngleTime(self.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 | |
def computeTimes(self): | |
times = { | |
'imsak': 5, 'fajr': 5, 'sunrise': 6, 'dhuhr': 12, | |
'asr': 13, 'sunset': 18, 'maghrib': 18, 'isha': 18 | |
} | |
# main iterations | |
for i in range(self.numIterations): | |
times = self.computePrayerTimes(times) | |
times = self.adjustTimes(times) | |
# add midnight time | |
if self.settings['midnight'] == 'Jafari': | |
times['midnight'] = times['sunset'] + self.timeDiff(times['sunset'], times['fajr']) / 2 | |
else: | |
times['midnight'] = times['sunset'] + self.timeDiff(times['sunset'], times['sunrise']) / 2 | |
times = self.tuneTimes(times) | |
return self.modifyFormats(times) | |
# adjust times in a prayer time array | |
def adjustTimes(self, times): | |
params = self.settings | |
tzAdjust = self.timeZone - self.lng / 15.0 | |
for t,v in times.items(): | |
times[t] += tzAdjust | |
if params['highLats'] != 'None': | |
times = self.adjustHighLats(times) | |
if self.isMin(params['imsak']): | |
times['imsak'] = times['fajr'] - self.eval(params['imsak']) / 60.0 | |
# need to ask about 'min' settings | |
if self.isMin(params['maghrib']): | |
times['maghrib'] = times['sunset'] + self.eval(params['maghrib']) / 60.0 | |
if self.isMin(params['isha']): | |
times['isha'] = times['maghrib'] + self.eval(params['isha']) / 60.0 | |
times['dhuhr'] += self.eval(params['dhuhr']) / 60.0 | |
return times | |
# get asr shadow factor | |
def asrFactor(self, asrParam): | |
methods = {'Standard': 1, 'Hanafi': 2} | |
return methods[asrParam] if asrParam in methods else self.eval(asrParam) | |
# return sun angle for sunset/sunrise | |
def riseSetAngle(self, elevation = 0): | |
elevation = 0 if elevation == None else elevation | |
return 0.833 + 0.0347 * math.sqrt(elevation) # an approximation | |
# apply offsets to the times | |
def tuneTimes(self, times): | |
for name, value in times.items(): | |
times[name] += self.offset[name] / 60.0 | |
return times | |
# convert times to given time format | |
def modifyFormats(self, times): | |
for name, value in times.items(): | |
times[name] = self.getFormattedTime(times[name], self.timeFormat) | |
return times | |
# adjust times for locations in higher latitudes | |
def adjustHighLats(self, times): | |
params = self.settings | |
nightTime = self.timeDiff(times['sunset'], times['sunrise']) # sunset to sunrise | |
times['imsak'] = self.adjustHLTime(times['imsak'], times['sunrise'], self.eval(params['imsak']), nightTime, 'ccw') | |
times['fajr'] = self.adjustHLTime(times['fajr'], times['sunrise'], self.eval(params['fajr']), nightTime, 'ccw') | |
times['isha'] = self.adjustHLTime(times['isha'], times['sunset'], self.eval(params['isha']), nightTime) | |
times['maghrib'] = self.adjustHLTime(times['maghrib'], times['sunset'], self.eval(params['maghrib']), nightTime) | |
return times | |
# adjust a time for higher latitudes | |
def adjustHLTime(self, time, base, angle, night, direction = None): | |
portion = self.nightPortion(angle, night) | |
diff = self.timeDiff(time, base) if direction == 'ccw' else self.timeDiff(base, time) | |
if math.isnan(time) or diff > portion: | |
time = base + (-portion if direction == 'ccw' else portion) | |
return time | |
# the night portion used for adjusting times in higher latitudes | |
def nightPortion(self, angle, night): | |
method = self.settings['highLats'] | |
portion = 1/2.0 # midnight | |
if method == 'AngleBased': | |
portion = 1/60.0 * angle | |
if method == 'OneSeventh': | |
portion = 1/7.0 | |
return portion * night | |
# convert hours to day portions | |
def dayPortion(self, times): | |
for i in times: | |
times[i] /= 24.0 | |
return times | |
#---------------------- Misc Functions ----------------------- | |
# compute the difference between two times | |
def timeDiff(self, time1, time2): | |
return self.fixhour(time2- time1) | |
# convert given string into a number | |
def eval(self, st): | |
val = re.split('[^0-9.+-]', str(st), 1)[0] | |
return float(val) if val else 0 | |
# detect if input contains 'min' | |
def isMin(self, arg): | |
return isinstance(arg, str) and arg.find('min') > -1 | |
#----------------- Degree-Based Math Functions ------------------- | |
def sin(self, d): return math.sin(math.radians(d)) | |
def cos(self, d): return math.cos(math.radians(d)) | |
def tan(self, d): return math.tan(math.radians(d)) | |
def arcsin(self, x): return math.degrees(math.asin(x)) | |
def arccos(self, x): return math.degrees(math.acos(x)) | |
def arctan(self, x): return math.degrees(math.atan(x)) | |
def arccot(self, x): return math.degrees(math.atan(1.0/x)) | |
def arctan2(self, y, x): return math.degrees(math.atan2(y, x)) | |
def fixangle(self, angle): return self.fix(angle, 360.0) | |
def fixhour(self, hour): return self.fix(hour, 24.0) | |
def fix(self, a, mode): | |
if math.isnan(a): | |
return a | |
a = a - mode * (math.floor(a / mode)) | |
return a + mode if a < 0 else a | |
#---------------------- prayTimes Object ----------------------- | |
prayTimes = PrayTimes() | |
#-------------------------- Test Code -------------------------- | |
# sample code to run in standalone mode only | |
if __name__ == "__main__": | |
from datetime import date | |
print('Prayer Times for today in Waterloo/Canada\n'+ ('='* 41)) | |
times = prayTimes.getTimes([2011,2,9], (43, -80), -5); | |
for i in ['Fajr', 'Sunrise', 'Dhuhr', 'Asr', 'Maghrib', 'Isha', 'Midnight']: | |
print(i+ ': '+ times[i.lower()]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gist.github.com/Deja-Vu1/c7ffb75bbaf93898b983d0a5fe26a24a
Fix for the python code version 2 of the Pray Times algorithm http://praytimes.org/code/