//--------------------- Copyright Block ---------------------- /* PrayTime.java: Prayer Times Calculator (ver 1.0) Copyright (C) 2007-2010 PrayTimes.org Java Code By: Hussain Ali Khan Original JS Code By: 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. */ package com.alimuzaffar.ramadanalarm.util; import android.content.Context; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.TimeZone; public class PrayTime { // ---------------------- Global Variables -------------------- private int calcMethod; // caculation method private int asrJuristic; // Juristic method for Asr private int dhuhrMinutes; // minutes after mid-day for Dhuhr private int adjustHighLats; // adjusting method for higher latitudes private int timeFormat; // time format private double lat; // latitude private double lng; // longitude private double timeZone; // time-zone private double JDate; // Julian date // ------------------------------------------------------------ // Calculation Methods public static final int JAFARI = 0; // Ithna Ashari public static final int KARACHI = 1; // University of Islamic Sciences, KARACHI public static final int ISNA = 2; // Islamic Society of North America (ISNA) public static final int MWL = 3; // Muslim World League (MWL) public static final int MAKKAH = 4; // Umm al-Qura, MAKKAH public static final int EGYPT = 5; // Egyptian General Authority of Survey public static final int CUSTOM = 7; // CUSTOM Setting public static final int TEHRAN = 6; // Institute of Geophysics, University of TEHRAN // Juristic Methods public static final int SHAFII = 0; // SHAFII (standard) public static final int HANAFI = 1; // HANAFI // Adjusting Methods for Higher Latitudes public static final int NONE = 0; // No adjustment public static final int MID_NIGHT = 1; // middle of night public static final int ONE_SEVENTH = 2; // 1/7th of night public static final int ANGLE_BASED = 3; // angle/60th of night // Time Formats public static final int TIME_24 = 0; // 24-hour format public static final int TIME_12 = 1; // 12-hour format public static final int TIME_12_NS = 2; // 12-hour format with no suffix public static final int FLOATING = 3; // floating point number // Time Names private ArrayList<String> timeNames; private String InvalidTime; // The string used for invalid times // --------------------- Technical Settings -------------------- private int numIterations; // number of iterations needed to compute times // ------------------- Calc Method Parameters -------------------- private HashMap<Integer, double[]> methodParams; /* * this.methodParams[methodNum] = new Array(fa, ms, mv, is, iv); * * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after * sunset) mv : maghrib parameter value (in angle or minutes) is : isha * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter value * (in angle or minutes) */ private double[] prayerTimesCurrent; private int[] offsets; public PrayTime() { // Initialize vars this.setCalcMethod(0); this.setAsrJuristic(0); this.setDhuhrMinutes(0); this.setAdjustHighLats(1); this.setTimeFormat(0); // Time Names timeNames = new ArrayList<String>(); timeNames.add("Fajr"); timeNames.add("Sunrise"); timeNames.add("Dhuhr"); timeNames.add("Asr"); timeNames.add("Sunset"); timeNames.add("Maghrib"); timeNames.add("Isha"); InvalidTime = "-----"; // The string used for invalid times // --------------------- Technical Settings -------------------- this.setNumIterations(1); // number of iterations needed to compute // times // ------------------- Calc Method Parameters -------------------- // Tuning offsets {fajr, sunrise, dhuhr, asr, sunset, maghrib, isha} offsets = new int[7]; offsets[0] = 0; offsets[1] = 0; offsets[2] = 0; offsets[3] = 0; offsets[4] = 0; offsets[5] = 0; offsets[6] = 0; /* * * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after * sunset) mv : maghrib parameter value (in angle or minutes) is : isha * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter * value (in angle or minutes) */ methodParams = new HashMap<Integer, double[]>(); // JAFARI double[] Jvalues = {16, 0, 4, 0, 14}; methodParams.put(JAFARI, Jvalues); // KARACHI double[] Kvalues = {18, 1, 0, 0, 18}; methodParams.put(KARACHI, Kvalues); // ISNA double[] Ivalues = {15, 1, 0, 0, 15}; methodParams.put(ISNA, Ivalues); // MWL double[] MWvalues = {18, 1, 0, 0, 17}; methodParams.put(MWL, MWvalues); // MAKKAH double[] MKvalues = {18.5, 1, 0, 1, 90}; methodParams.put(MAKKAH, MKvalues); // EGYPT double[] Evalues = {19.5, 1, 0, 0, 17.5}; methodParams.put(EGYPT, Evalues); // TEHRAN double[] Tvalues = {17.7, 0, 4.5, 0, 14}; methodParams.put(TEHRAN, Tvalues); // CUSTOM double[] Cvalues = {18, 1, 0, 0, 17}; methodParams.put(CUSTOM, Cvalues); } // ---------------------- Trigonometric Functions ----------------------- // range reduce angle in degrees. private double fixangle(double a) { a = a - (360 * (Math.floor(a / 360.0))); a = a < 0 ? (a + 360) : a; return a; } // range reduce hours to 0..23 private double fixhour(double a) { a = a - 24.0 * Math.floor(a / 24.0); a = a < 0 ? (a + 24) : a; return a; } // radian to degree private double radiansToDegrees(double alpha) { return ((alpha * 180.0) / Math.PI); } // deree to radian private double DegreesToRadians(double alpha) { return ((alpha * Math.PI) / 180.0); } // degree sin private double dsin(double d) { return (Math.sin(DegreesToRadians(d))); } // degree cos private double dcos(double d) { return (Math.cos(DegreesToRadians(d))); } // degree tan private double dtan(double d) { return (Math.tan(DegreesToRadians(d))); } // degree arcsin private double darcsin(double x) { double val = Math.asin(x); return radiansToDegrees(val); } // degree arccos private double darccos(double x) { double val = Math.acos(x); return radiansToDegrees(val); } // degree arctan private double darctan(double x) { double val = Math.atan(x); return radiansToDegrees(val); } // degree arctan2 private double darctan2(double y, double x) { double val = Math.atan2(y, x); return radiansToDegrees(val); } // degree arccot private double darccot(double x) { double val = Math.atan2(1.0, x); return radiansToDegrees(val); } // ---------------------- Time-Zone Functions ----------------------- // compute local time-zone for a specific date private double getTimeZone1() { TimeZone timez = TimeZone.getDefault(); double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; return hoursDiff; } // compute base time-zone of the system private double getBaseTimeZone() { TimeZone timez = TimeZone.getDefault(); double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; return hoursDiff; } // detect daylight saving in a given date private double detectDaylightSaving() { TimeZone timez = TimeZone.getDefault(); double hoursDiff = timez.getDSTSavings(); return hoursDiff; } // ---------------------- Julian Date Functions ----------------------- // calculate julian date from a calendar date private double julianDate(int year, int month, int day) { if (month <= 2) { year -= 1; month += 12; } double A = Math.floor(year / 100.0); double B = 2 - A + Math.floor(A / 4.0); double JD = Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5; return JD; } // convert a calendar date to julian date (second method) private double calcJD(int year, int month, int day) { double J1970 = 2440588.0; Date date = new Date(year, month - 1, day); double ms = date.getTime(); // # of milliseconds since midnight Jan 1, // 1970 double days = Math.floor(ms / (1000.0 * 60.0 * 60.0 * 24.0)); return J1970 + days - 0.5; } // ---------------------- Calculation Functions ----------------------- // References: // http://www.ummah.net/astronomy/saltime // http://aa.usno.navy.mil/faq/docs/SunApprox.html // compute declination angle of sun and equation of time private double[] sunPosition(double jd) { double D = jd - 2451545; double g = fixangle(357.529 + 0.98560028 * D); double q = fixangle(280.459 + 0.98564736 * D); double L = fixangle(q + (1.915 * dsin(g)) + (0.020 * dsin(2 * g))); // double R = 1.00014 - 0.01671 * [self dcos:g] - 0.00014 * [self dcos: // (2*g)]; double e = 23.439 - (0.00000036 * D); double d = darcsin(dsin(e) * dsin(L)); double RA = (darctan2((dcos(e) * dsin(L)), (dcos(L)))) / 15.0; RA = fixhour(RA); double EqT = q / 15.0 - RA; double[] sPosition = new double[2]; sPosition[0] = d; sPosition[1] = EqT; return sPosition; } // compute equation of time private double equationOfTime(double jd) { double eq = sunPosition(jd)[1]; return eq; } // compute declination angle of sun private double sunDeclination(double jd) { double d = sunPosition(jd)[0]; return d; } // compute mid-day (Dhuhr, Zawal) time private double computeMidDay(double t) { double T = equationOfTime(this.getJDate() + t); double Z = fixhour(12 - T); return Z; } // compute time for a given angle G private double computeTime(double G, double t) { double D = sunDeclination(this.getJDate() + t); double Z = computeMidDay(t); double Beg = -dsin(G) - dsin(D) * dsin(this.getLat()); double Mid = dcos(D) * dcos(this.getLat()); double V = darccos(Beg / Mid) / 15.0; return Z + (G > 90 ? -V : V); } // compute the time of Asr // SHAFII: step=1, HANAFI: step=2 private double computeAsr(double step, double t) { double D = sunDeclination(this.getJDate() + t); double G = -darccot(step + dtan(Math.abs(this.getLat() - D))); return computeTime(G, t); } // ---------------------- Misc Functions ----------------------- // compute the difference between two times private double timeDiff(double time1, double time2) { return fixhour(time2 - time1); } // -------------------- Interface Functions -------------------- // return prayer times for a given date private ArrayList<String> getDatePrayerTimes(int year, int month, int day, double latitude, double longitude, double tZone) { this.setLat(latitude); this.setLng(longitude); this.setTimeZone(tZone); this.setJDate(julianDate(year, month, day)); double lonDiff = longitude / (15.0 * 24.0); this.setJDate(this.getJDate() - lonDiff); return computeDayTimes(); } // return prayer times for a given date public ArrayList<String> getPrayerTimes(Calendar date, double latitude, double longitude, double tZone) { int year = date.get(Calendar.YEAR); int month = date.get(Calendar.MONTH); int day = date.get(Calendar.DATE); return getDatePrayerTimes(year, month + 1, day, latitude, longitude, tZone); } // set custom values for calculation parameters private void setCustomParams(double[] params) { for (int i = 0; i < 5; i++) { if (params[i] == -1) { params[i] = methodParams.get(this.getCalcMethod())[i]; methodParams.put(CUSTOM, params); } else { methodParams.get(CUSTOM)[i] = params[i]; } } this.setCalcMethod(CUSTOM); } // set the angle for calculating Fajr public void setFajrAngle(double angle) { double[] params = {angle, -1, -1, -1, -1}; setCustomParams(params); } // set the angle for calculating Maghrib public void setMaghribAngle(double angle) { double[] params = {-1, 0, angle, -1, -1}; setCustomParams(params); } // set the angle for calculating Isha public void setIshaAngle(double angle) { double[] params = {-1, -1, -1, 0, angle}; setCustomParams(params); } // set the minutes after Sunset for calculating Maghrib public void setMaghribMinutes(double minutes) { double[] params = {-1, 1, minutes, -1, -1}; setCustomParams(params); } // set the minutes after Maghrib for calculating Isha public void setIshaMinutes(double minutes) { double[] params = {-1, -1, -1, 1, minutes}; setCustomParams(params); } // convert double hours to 24h format public String floatToTime24(double time) { String result; if (Double.isNaN(time)) { return InvalidTime; } time = fixhour(time + 0.5 / 60.0); // add 0.5 minutes to round int hours = (int) Math.floor(time); double minutes = Math.floor((time - hours) * 60.0); if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { result = hours + ":0" + Math.round(minutes); } else if ((hours >= 0 && hours <= 9)) { result = hours + ":" + Math.round(minutes); } else if ((minutes >= 0 && minutes <= 9)) { result = hours + ":0" + Math.round(minutes); } else { result = hours + ":" + Math.round(minutes); } return result; } // convert double hours to 12h format public String floatToTime12(double time, boolean noSuffix) { if (Double.isNaN(time)) { return InvalidTime; } time = fixhour(time + 0.5 / 60); // add 0.5 minutes to round int hours = (int) Math.floor(time); double minutes = Math.floor((time - hours) * 60); String suffix, result; if (hours >= 12) { suffix = "pm"; } else { suffix = "am"; } hours = ((((hours + 12) - 1) % (12)) + 1); /*hours = (hours + 12) - 1; int hrs = (int) hours % 12; hrs += 1;*/ if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { result = hours + ":0" + Math.round(minutes); } else if ((hours >= 0 && hours <= 9)) { result = hours + ":" + Math.round(minutes); } else if ((minutes >= 0 && minutes <= 9)) { result = hours + ":0" + Math.round(minutes); } else { result = hours + ":" + Math.round(minutes); } if (!noSuffix) { result += " " + suffix; } return result; } // convert double hours to 12h format with no suffix public String floatToTime12NS(double time) { return floatToTime12(time, true); } // ---------------------- Compute Prayer Times ----------------------- // compute prayer times at given julian date private double[] computeTimes(double[] times) { double[] t = dayPortion(times); double Fajr = this.computeTime( 180 - methodParams.get(this.getCalcMethod())[0], t[0]); double Sunrise = this.computeTime(180 - 0.833, t[1]); double Dhuhr = this.computeMidDay(t[2]); double Asr = this.computeAsr(1 + this.getAsrJuristic(), t[3]); double Sunset = this.computeTime(0.833, t[4]); double Maghrib = this.computeTime( methodParams.get(this.getCalcMethod())[2], t[5]); double Isha = this.computeTime( methodParams.get(this.getCalcMethod())[4], t[6]); double[] CTimes = {Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha}; return CTimes; } // compute prayer times at given julian date private ArrayList<String> computeDayTimes() { double[] times = {5, 6, 12, 13, 18, 18, 18}; // default times for (int i = 1; i <= this.getNumIterations(); i++) { times = computeTimes(times); } times = adjustTimes(times); times = tuneTimes(times); return adjustTimesFormat(times); } // adjust times in a prayer time array private double[] adjustTimes(double[] times) { for (int i = 0; i < times.length; i++) { times[i] += this.getTimeZone() - this.getLng() / 15; } times[2] += this.getDhuhrMinutes() / 60; // Dhuhr if (methodParams.get(this.getCalcMethod())[1] == 1) // Maghrib { times[5] = times[4] + methodParams.get(this.getCalcMethod())[2] / 60; } if (methodParams.get(this.getCalcMethod())[3] == 1) // Isha { times[6] = times[5] + methodParams.get(this.getCalcMethod())[4] / 60; } if (this.getAdjustHighLats() != NONE) { times = adjustHighLatTimes(times); } return times; } // convert times array to given time format private ArrayList<String> adjustTimesFormat(double[] times) { ArrayList<String> result = new ArrayList<String>(); if (this.getTimeFormat() == FLOATING) { for (double time : times) { result.add(String.valueOf(time)); } return result; } for (int i = 0; i < 7; i++) { if (this.getTimeFormat() == TIME_12) { result.add(floatToTime12(times[i], false)); } else if (this.getTimeFormat() == TIME_12_NS) { result.add(floatToTime12(times[i], true)); } else { result.add(floatToTime24(times[i])); } } return result; } // adjust Fajr, Isha and Maghrib for locations in higher latitudes private double[] adjustHighLatTimes(double[] times) { double nightTime = timeDiff(times[4], times[1]); // sunset to sunrise // Adjust Fajr double FajrDiff = nightPortion(methodParams.get(this.getCalcMethod())[0]) * nightTime; if (Double.isNaN(times[0]) || timeDiff(times[0], times[1]) > FajrDiff) { times[0] = times[1] - FajrDiff; } // Adjust Isha double IshaAngle = (methodParams.get(this.getCalcMethod())[3] == 0) ? methodParams.get(this.getCalcMethod())[4] : 18; double IshaDiff = this.nightPortion(IshaAngle) * nightTime; if (Double.isNaN(times[6]) || this.timeDiff(times[4], times[6]) > IshaDiff) { times[6] = times[4] + IshaDiff; } // Adjust Maghrib double MaghribAngle = (methodParams.get(this.getCalcMethod())[1] == 0) ? methodParams.get(this.getCalcMethod())[2] : 4; double MaghribDiff = nightPortion(MaghribAngle) * nightTime; if (Double.isNaN(times[5]) || this.timeDiff(times[4], times[5]) > MaghribDiff) { times[5] = times[4] + MaghribDiff; } return times; } // the night portion used for adjusting times in higher latitudes private double nightPortion(double angle) { double calc = 0; if (adjustHighLats == ANGLE_BASED) calc = (angle) / 60.0; else if (adjustHighLats == MID_NIGHT) calc = 0.5; else if (adjustHighLats == ONE_SEVENTH) calc = 0.14286; return calc; } // convert hours to day portions private double[] dayPortion(double[] times) { for (int i = 0; i < 7; i++) { times[i] /= 24; } return times; } // Tune timings for adjustments // Set time offsets public void tune(int[] offsetTimes) { for (int i = 0; i < offsetTimes.length; i++) { // offsetTimes length // should be 7 in order // of Fajr, Sunrise, // Dhuhr, Asr, Sunset, // Maghrib, Isha this.offsets[i] = offsetTimes[i]; } } private double[] tuneTimes(double[] times) { for (int i = 0; i < times.length; i++) { times[i] = times[i] + this.offsets[i] / 60.0; } return times; } /** * @param args */ public static void main(String[] args) { double latitude = -33.8736779; double longitude = 151.196515; //Get NY time zone instance TimeZone defaultTz = TimeZone.getDefault(); //Get NY calendar object with current date/time Calendar defaultCalc = Calendar.getInstance(defaultTz); //Get offset from UTC, accounting for DST int defaultTzOffsetMs = defaultCalc.get(Calendar.ZONE_OFFSET) + defaultCalc.get(Calendar.DST_OFFSET); double timezone = defaultTzOffsetMs / (1000 * 60 * 60); // Test Prayer times here PrayTime prayers = new PrayTime(); prayers.setTimeFormat(prayers.TIME_12); prayers.setCalcMethod(prayers.KARACHI); prayers.setAsrJuristic(prayers.SHAFII); prayers.setAdjustHighLats(prayers.ANGLE_BASED); int[] offsets = {0, 0, 0, 0, 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} prayers.tune(offsets); Date now = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(now); ArrayList<String> prayerTimes = prayers.getPrayerTimes(cal, latitude, longitude, timezone); ArrayList<String> prayerNames = prayers.getTimeNames(); for (int i = 0; i < prayerTimes.size(); i++) { System.out.println(prayerNames.get(i) + " - " + prayerTimes.get(i)); } } public static LinkedHashMap<String, String> getPrayerTimes(Context context, int index, double lat, double lng) { return getPrayerTimes(context, index, lat, lng, -1); } public static LinkedHashMap<String, String> getPrayerTimes(Context context, int index, double lat, double lng, int timeFormat) { AppSettings settings = AppSettings.getInstance(context); double latitude = lat; double longitude = lng; //Get time zone instance TimeZone defaultTz = TimeZone.getDefault(); //Get calendar object with current date/time Calendar defaultCalc = Calendar.getInstance(defaultTz); //Get offset from UTC, accounting for DST int defaultTzOffsetMs = defaultCalc.get(Calendar.ZONE_OFFSET) + defaultCalc.get(Calendar.DST_OFFSET); double timezone = defaultTzOffsetMs / (1000 * 60 * 60); // Test Prayer times here PrayTime prayers = new PrayTime(); if (timeFormat == -1) { prayers.setTimeFormat(settings.getTimeFormatFor(index)); } else { prayers.setTimeFormat(timeFormat); } prayers.setCalcMethod(settings.getCalcMethodSetFor(index)); prayers.setAsrJuristic(settings.getAsrMethodSetFor(index)); prayers.setAdjustHighLats(settings.getHighLatitudeAdjustmentFor(index)); int[] offsets = {0, 0, 0, 0, 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} prayers.tune(offsets); Date now = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(now); ArrayList<String> prayerTimes = prayers.getPrayerTimes(cal, latitude, longitude, timezone); ArrayList<String> prayerNames = prayers.getTimeNames(); LinkedHashMap<String, String> result = new LinkedHashMap<>(); for (int i = 0; i < prayerTimes.size(); i++) { System.out.println(prayerNames.get(i) + " - " + prayerTimes.get(i)); result.put(prayerNames.get(i), prayerTimes.get(i)); } return result; } public int getCalcMethod() { return calcMethod; } public void setCalcMethod(int calcMethod) { this.calcMethod = calcMethod; } public int getAsrJuristic() { return asrJuristic; } public void setAsrJuristic(int asrJuristic) { this.asrJuristic = asrJuristic; } public int getDhuhrMinutes() { return dhuhrMinutes; } public void setDhuhrMinutes(int dhuhrMinutes) { this.dhuhrMinutes = dhuhrMinutes; } public int getAdjustHighLats() { return adjustHighLats; } public void setAdjustHighLats(int adjustHighLats) { this.adjustHighLats = adjustHighLats; } public int getTimeFormat() { return timeFormat; } public void setTimeFormat(int timeFormat) { this.timeFormat = timeFormat; } public double getLat() { return lat; } public void setLat(double lat) { this.lat = lat; } public double getLng() { return lng; } public void setLng(double lng) { this.lng = lng; } public double getTimeZone() { return timeZone; } public void setTimeZone(double timeZone) { this.timeZone = timeZone; } public double getJDate() { return JDate; } public void setJDate(double jDate) { JDate = jDate; } private int getNumIterations() { return numIterations; } private void setNumIterations(int numIterations) { this.numIterations = numIterations; } public ArrayList<String> getTimeNames() { return timeNames; } }