diff --git a/v1/cpp/CMakeLists.txt b/v1/cpp/CMakeLists.txt new file mode 100644 index 0000000..cc166a5 --- /dev/null +++ b/v1/cpp/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.6) + +project(prayertimes) + +add_executable(prayertimes prayertimes.cpp) + +add_definitions(-Wall) diff --git a/v1/cpp/prayertimes.cpp b/v1/cpp/prayertimes.cpp new file mode 100644 index 0000000..431dfe1 --- /dev/null +++ b/v1/cpp/prayertimes.cpp @@ -0,0 +1,298 @@ +/*-------------------- In the name of God ----------------------*\ + + PrayerTimes 0.3 + Islamic prayer times calculator + +Developed by: + Mohammad Ebrahim Mohammadi Panah <ebrahim at mohammadi dot ir> + +------------------------------------------------------------------ + +Copyright 2009, Mohammad Ebrahim Mohammadi Panah + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You can get a copy of the GNU General Public License from +http://www.gnu.org/copyleft/gpl.html + +\*--------------------------------------------------------------*/ + +#include <ctime> +#include <cmath> +#include <cstring> +#include <unistd.h> +#include <getopt.h> + +#include "prayertimes.hpp" + +#define PROG_NAME "prayertimes" +#define PROG_NAME_FRIENDLY "PrayerTimes" +#define PROG_VERSION "0.3" + +static const char* TimeName[] = +{ + "Fajr", + "Sunrise", + "Dhuhr", + "Asr", + "Sunset", + "Maghrib", + "Isha", +}; + +void print_help(FILE* f) +{ + fputs(PROG_NAME_FRIENDLY " " PROG_VERSION "\n\n", stderr); + fputs("Usage: " PROG_NAME " options...\n" + "\n" + " Options\n" + " --help -h you're reading it\n" + " --version -v prints name and version, then exits\n" + " --date arg -d get prayer times for arbitrary date\n" + " --timezone arg -z get prayer times for arbitrary timezone\n" + " * --latitude arg -l latitude of desired location\n" + " * --longitude arg -n longitude of desired location\n" + " --calc-method arg -c select prayer time calculation method\n" + " --asr-juristic-method arg -a select Juristic method for calculating Asr prayer time\n" + " --high-lats-method arg -i select adjusting method for higher latitude\n" + " --dhuhr-minutes arg minutes after mid-way for calculating Dhuhr prayer time\n" + " ** --maghrib-minutes arg minutes after sunset for calculating Maghrib prayer time\n" + " ** --isha-minutes arg minutes after Maghrib for calculating Isha prayer time\n" + " ** --fajr-angle arg angle for calculating Fajr prayer time\n" + " ** --maghrib-angle arg angle for calculating Maghrib prayer time\n" + " ** --isha-angle arg angle for calculating Isha prayer time\n" + "\n" + " * These options are required\n" + " ** By providing any of these options the calculation method is set to custom\n" + "\n" + " Possible arguments for --calc-method\n" + " jafari Ithna Ashari\n" + " karachi University of Islamic Sciences, Karachi\n" + " isna Islamic Society of North America (ISNA)\n" + " mwl Muslim World League (MWL)\n" + " makkah Umm al-Qura, Makkah\n" + " egypt Egyptian General Authority of Survey\n" + " custom Custom Setting\n" + "\n" + " Possible arguments for --asr-juristic-method\n" + " shafii Shafii (standard)\n" + " hanafi Hanafi\n" + "\n" + " Possible arguments for --high-lats-method\n" + " none No adjustment\n" + " midnight middle of night\n" + " oneseventh 1/7th of night\n" + " anglebased angle/60th of night\n" + , stderr); + +} + +int main(int argc, char* argv[]) +{ + PrayerTimes prayer_times; + double latitude = NAN; // 35.7061 + double longitude = NAN; // 51.4358 + time_t date = time(NULL); + double timezone = NAN; + + // Parse options + for (;;) + { + static option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "date", required_argument, NULL, 'd' }, + { "timezone", required_argument, NULL, 'z' }, + { "latitude", required_argument, NULL, 'l' }, + { "longitude", required_argument, NULL, 'n' }, + { "calc-method", required_argument, NULL, 'c' }, + { "asr-juristic-method", required_argument, NULL, 'a' }, + { "high-lats-method", required_argument, NULL, 'i' }, + { "dhuhr-minutes", required_argument, NULL, 0 }, + { "maghrib-minutes", required_argument, NULL, 0 }, + { "isha-minutes", required_argument, NULL, 0 }, + { "fajr-angle", required_argument, NULL, 0 }, + { "maghrib-angle", required_argument, NULL, 0 }, + { "isha-angle", required_argument, NULL, 0 }, + { 0, 0, 0, 0 } + }; + + enum // long options missing a short form + { + DHUHR_MINUTES = 9, + MAGHRIB_MINUTES, + ISHA_MINUTES, + FAJR_ANGLE, + MAGHRIB_ANGLE, + ISHA_ANGLE, + }; + + int option_index = 0; + int c = getopt_long(argc, argv, "hvd:z:l:n:c:a:i:", long_options, &option_index); + + if (c == -1) + break; // Last option + + if (!optarg && c != 'h' && c != 'v') + { + fprintf(stderr, "Error: %s option requires an argument\n", long_options[option_index].name); + return 2; + } + + switch (c) + { + case 0: + double arg; + if (sscanf(optarg, "%lf", &arg) != 1) + { + fprintf(stderr, "Error: Invalid number '%s'\n", optarg); + return 2; + } + switch (option_index) + { + case DHUHR_MINUTES: + prayer_times.set_dhuhr_minutes(arg); + break; + case MAGHRIB_MINUTES: + prayer_times.set_maghrib_minutes(arg); + break; + case ISHA_MINUTES: + prayer_times.set_isha_minutes(arg); + break; + case FAJR_ANGLE: + prayer_times.set_fajr_angle(arg); + break; + case MAGHRIB_ANGLE: + prayer_times.set_maghrib_angle(arg); + break; + case ISHA_ANGLE: + prayer_times.set_isha_angle(arg); + break; + default: + fprintf(stderr, "Error: Invalid command line option\n"); + return 2; + } + break; + case 'h': // --help + print_help(stdout); + return 0; + case 'v': // --version + puts(PROG_NAME_FRIENDLY " " PROG_VERSION); + return 0; + case 'd': // --date + { + tm* new_date = getdate(optarg); + if (!new_date) + { + fprintf(stderr, "Error: Failed to parse '%s' as date (%m)\n", optarg); + return 2; + } + date = mktime(new_date); + break; + } + case 'z': // --timezone + if (sscanf(optarg, "%lf", &timezone) != 1) + { + fprintf(stderr, "Error: Invalid timezone '%s'\n", optarg); + return 2; + } + break; + case 'l': // --latitude + if (sscanf(optarg, "%lf", &latitude) != 1) + { + fprintf(stderr, "Error: Invalid latitude '%s'\n", optarg); + return 2; + } + break; + case 'n': // --longitude + if (sscanf(optarg, "%lf", &longitude) != 1) + { + fprintf(stderr, "Error: Invalid longitude '%s'\n", optarg); + return 2; + } + break; + case 'c': // --calc-method + if (strcmp(optarg, "jafari") == 0) + prayer_times.set_calc_method(PrayerTimes::Jafari); + else if (strcmp(optarg, "karachi") == 0) + prayer_times.set_calc_method(PrayerTimes::Karachi); + else if (strcmp(optarg, "isna") == 0) + prayer_times.set_calc_method(PrayerTimes::ISNA); + else if (strcmp(optarg, "mwl") == 0) + prayer_times.set_calc_method(PrayerTimes::MWL); + else if (strcmp(optarg, "makkah") == 0) + prayer_times.set_calc_method(PrayerTimes::Makkah); + else if (strcmp(optarg, "egypt") == 0) + prayer_times.set_calc_method(PrayerTimes::Egypt); + else if (strcmp(optarg, "custom") == 0) + prayer_times.set_calc_method(PrayerTimes::Custom); + else + { + fprintf(stderr, "Error: Unknown method '%s'\n", optarg); + return 2; + } + break; + case 'a': // --asr-juristic-method + if (strcmp(optarg, "shafii") == 0) + prayer_times.set_asr_method(PrayerTimes::Shafii); + else if (strcmp(optarg, "hanafi") == 0) + prayer_times.set_asr_method(PrayerTimes::Hanafi); + else + { + fprintf(stderr, "Error: Unknown method '%s'\n", optarg); + return 2; + } + break; + case 'i': // --high-lats-method + if (strcmp(optarg, "none") == 0) + prayer_times.set_high_lats_adjust_method(PrayerTimes::None); + else if (strcmp(optarg, "midnight") == 0) + prayer_times.set_high_lats_adjust_method(PrayerTimes::MidNight); + else if (strcmp(optarg, "oneseventh") == 0) + prayer_times.set_high_lats_adjust_method(PrayerTimes::OneSeventh); + else if (strcmp(optarg, "anglebased") == 0) + prayer_times.set_high_lats_adjust_method(PrayerTimes::AngleBased); + else + { + fprintf(stderr, "Error: Unknown method '%s'\n", optarg); + return 2; + } + break; + default: + fprintf(stderr, "Error: Unknown option '%c'\n", c); + print_help(stderr); + return 2; + } + } + + if (isnan(latitude) || isnan(longitude)) + { + fprintf(stderr, "Error: You must provide both latitude and longitude\n"); + return 2; + } + + fputs(PROG_NAME_FRIENDLY " " PROG_VERSION "\n\n", stderr); + + if (isnan(timezone)) + timezone = PrayerTimes::get_effective_timezone(date); + + double times[PrayerTimes::TimesCount]; + fprintf(stderr, "date : %s", ctime(&date)); + fprintf(stderr, "timezone : %.1lf\n", timezone); + fprintf(stderr, "latitude : %.5lf\n", latitude); + fprintf(stderr, "longitude : %.5lf\n", longitude); + puts(""); + prayer_times.get_prayer_times(date, latitude, longitude, timezone, times); + for (int i = 0; i < PrayerTimes::TimesCount; ++i) + printf("%8s : %s\n", TimeName[i], PrayerTimes::float_time_to_time24(times[i]).c_str()); + return 0; +} diff --git a/v1/cpp/prayertimes.hpp b/v1/cpp/prayertimes.hpp new file mode 100644 index 0000000..ac0fcdf --- /dev/null +++ b/v1/cpp/prayertimes.hpp @@ -0,0 +1,631 @@ +/*-------------------------- In the name of God ----------------------------*\ + + libprayertimes 1.0 + Islamic prayer times calculator library + Based on PrayTimes 1.1 JavaScript library + +----------------------------- Copyright Block -------------------------------- + +Copyright (C) 2007-2010 PrayTimes.org + +Developed By: Mohammad Ebrahim Mohammadi Panah <ebrahim at mohammadi dot ir> +Based on a JavaScript Code By: Hamid Zarrabi-Zadeh + +License: GNU General Public License, ver 3 + +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. + +------------------------------------------------------------------------------ + +User's Manual: +http://praytimes.org/manual + +Calculating Formulas: +http://praytimes.org/calculation + +Code Repository: +http://code.ebrahim.ir/prayertimes/ + +\*--------------------------------------------------------------------------*/ + +#include <cstdio> +#include <cmath> +#include <string> + +/* -------------------- PrayerTimes Class --------------------- */ + +class PrayerTimes +{ +public: + enum + { + VERSION_MAJOR = 1, + VERSION_MINOR = 0, + }; +/* --------------------- User Interface ----------------------- */ +/* + PrayerTimes(CalculationMethod calc_method = Jafari, + JuristicMethod asr_juristic = Shafii, + AdjustingMethod adjust_high_lats = MidNight, + double dhuhr_minutes = 0) + + get_prayer_times(date, latitude, longitude, timezone, ×) + get_prayer_times(year, month, day, latitude, longitude, timezone, ×) + + set_calc_method(method_id) + set_asr_method(method_id) + set_high_lats_adjust_method(method_id) // adjust method for higher latitudes + + set_fajr_angle(angle) + set_maghrib_angle(angle) + set_isha_angle(angle) + set_dhuhr_minutes(minutes) // minutes after mid-day + set_maghrib_minutes(minutes) // minutes after sunset + set_isha_minutes(minutes) // minutes after maghrib + + get_float_time_parts(time, &hours, &minutes) + float_time_to_time24(time) + float_time_to_time12(time) + float_time_to_time12ns(time) +*/ + + // Calculation Methods + enum CalculationMethod + { + Jafari, // Ithna Ashari + Karachi, // University of Islamic Sciences, Karachi + ISNA, // Islamic Society of North America (ISNA) + MWL, // Muslim World League (MWL) + Makkah, // Umm al-Qura, Makkah + Egypt, // Egyptian General Authority of Survey + Custom, // Custom Setting + + CalculationMethodsCount + }; + + // Juristic Methods + enum JuristicMethod + { + Shafii, // Shafii (standard) + Hanafi, // Hanafi + }; + + // Adjusting Methods for Higher Latitudes + enum AdjustingMethod + { + None, // No adjustment + MidNight, // middle of night + OneSeventh, // 1/7th of night + AngleBased, // angle/60th of night + }; + + // Time IDs + enum TimeID + { + Fajr, + Sunrise, + Dhuhr, + Asr, + Sunset, + Maghrib, + Isha, + + TimesCount + }; + +/* -------------------- Interface Functions -------------------- */ + + PrayerTimes(CalculationMethod calc_method = Jafari, + JuristicMethod asr_juristic = Shafii, + AdjustingMethod adjust_high_lats = MidNight, + double dhuhr_minutes = 0) + : calc_method(calc_method) + , asr_juristic(asr_juristic) + , adjust_high_lats(adjust_high_lats) + , dhuhr_minutes(dhuhr_minutes) + { + method_params[Jafari] = MethodConfig(16.0, false, 4.0, false, 14.0); // Jafari + method_params[Karachi] = MethodConfig(18.0, true, 0.0, false, 18.0); // Karachi + method_params[ISNA] = MethodConfig(15.0, true, 0.0, false, 15.0); // ISNA + method_params[MWL] = MethodConfig(18.0, true, 0.0, false, 17.0); // MWL + method_params[Makkah] = MethodConfig(19.0, true, 0.0, true, 90.0); // Makkah + method_params[Egypt] = MethodConfig(19.5, true, 0.0, false, 17.5); // Egypt + method_params[Custom] = MethodConfig(18.0, true, 0.0, false, 17.0); // Custom + } + + /* return prayer times for a given date */ + void get_prayer_times(int year, int month, int day, double _latitude, double _longitude, double _timezone, double times[]) + { + latitude = _latitude; + longitude = _longitude; + timezone = _timezone; + julian_date = get_julian_date(year, month, day) - longitude / (double) (15 * 24); + compute_day_times(times); + } + + /* return prayer times for a given date */ + void get_prayer_times(time_t date, double latitude, double longitude, double timezone, double times[]) + { + tm* t = localtime(&date); + get_prayer_times(1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, latitude, longitude, timezone, times); + } + + /* set the calculation method */ + void set_calc_method(CalculationMethod method_id) + { + calc_method = method_id; + } + + /* set the juristic method for Asr */ + void set_asr_method(JuristicMethod method_id) + { + asr_juristic = method_id; + } + + /* set adjusting method for higher latitudes */ + void set_high_lats_adjust_method(AdjustingMethod method_id) + { + adjust_high_lats = method_id; + } + + /* set the angle for calculating Fajr */ + void set_fajr_angle(double angle) + { + method_params[Custom].fajr_angle = angle; + calc_method = Custom; + } + + /* set the angle for calculating Maghrib */ + void set_maghrib_angle(double angle) + { + method_params[Custom].maghrib_is_minutes = false; + method_params[Custom].maghrib_value = angle; + calc_method = Custom; + } + + /* set the angle for calculating Isha */ + void set_isha_angle(double angle) + { + method_params[Custom].isha_is_minutes = false; + method_params[Custom].isha_value = angle; + calc_method = Custom; + } + + /* set the minutes after mid-day for calculating Dhuhr */ + void set_dhuhr_minutes(double minutes) + { + dhuhr_minutes = minutes; + } + + /* set the minutes after Sunset for calculating Maghrib */ + void set_maghrib_minutes(double minutes) + { + method_params[Custom].maghrib_is_minutes = true; + method_params[Custom].maghrib_value = minutes; + calc_method = Custom; + } + + /* set the minutes after Maghrib for calculating Isha */ + void set_isha_minutes(double minutes) + { + method_params[Custom].isha_is_minutes = true; + method_params[Custom].isha_value = minutes; + calc_method = Custom; + } + + /* get hours and minutes parts of a float time */ + static void get_float_time_parts(double time, int& hours, int& minutes) + { + time = fix_hour(time + 0.5 / 60); // add 0.5 minutes to round + hours = floor(time); + minutes = floor((time - hours) * 60); + } + + /* convert float hours to 24h format */ + static std::string float_time_to_time24(double time) + { + if (isnan(time)) + return std::string(); + int hours, minutes; + get_float_time_parts(time, hours, minutes); + return two_digits_format(hours) + ':' + two_digits_format(minutes); + } + + /* convert float hours to 12h format */ + static std::string float_time_to_time12(double time, bool no_suffix = false) + { + if (isnan(time)) + return std::string(); + int hours, minutes; + get_float_time_parts(time, hours, minutes); + const char* suffix = hours >= 12 ? " PM" : " AM"; + hours = (hours + 12 - 1) % 12 + 1; + return int_to_string(hours) + ':' + two_digits_format(minutes) + (no_suffix ? "" : suffix); + } + + /* convert float hours to 12h format with no suffix */ + static std::string float_time_to_time12ns(double time) + { + return float_time_to_time12(time, true); + } + +/* ---------------------- Time-Zone Functions ----------------------- */ + + /* compute local time-zone for a specific date */ + static double get_effective_timezone(time_t local_time) + { + tm* tmp = localtime(&local_time); + tmp->tm_isdst = 0; + time_t local = mktime(tmp); + tmp = gmtime(&local_time); + tmp->tm_isdst = 0; + time_t gmt = mktime(tmp); + return (local - gmt) / 3600.0; + } + + /* compute local time-zone for a specific date */ + static double get_effective_timezone(int year, int month, int day) + { + tm date = { 0 }; + date.tm_year = year - 1900; + date.tm_mon = month - 1; + date.tm_mday = day; + date.tm_isdst = -1; // determine it yourself from system + time_t local = mktime(&date); // seconds since midnight Jan 1, 1970 + return get_effective_timezone(local); + } + +private: +/* ------------------- Calc Method Parameters -------------------- */ + + struct MethodConfig + { + MethodConfig() + { + } + + MethodConfig(double fajr_angle, + bool maghrib_is_minutes, + double maghrib_value, + bool isha_is_minutes, + double isha_value) + : fajr_angle(fajr_angle) + , maghrib_is_minutes(maghrib_is_minutes) + , maghrib_value(maghrib_value) + , isha_is_minutes(isha_is_minutes) + , isha_value(isha_value) + { + } + + double fajr_angle; + bool maghrib_is_minutes; + double maghrib_value; // angle or minutes + bool isha_is_minutes; + double isha_value; // angle or minutes + }; + +/* ---------------------- Calculation Functions ----------------------- */ + + /* References: */ + /* http://www.ummah.net/astronomy/saltime */ + /* http://aa.usno.navy.mil/faq/docs/SunApprox.html */ + + typedef std::pair<double, double> DoublePair; + + /* compute declination angle of sun and equation of time */ + DoublePair sun_position(double jd) + { + double d = jd - 2451545.0; + double g = fix_angle(357.529 + 0.98560028 * d); + double q = fix_angle(280.459 + 0.98564736 * d); + double l = fix_angle(q + 1.915 * dsin(g) + 0.020 * dsin(2 * g)); + + // double r = 1.00014 - 0.01671 * dcos(g) - 0.00014 * dcos(2 * g); + double e = 23.439 - 0.00000036 * d; + + double dd = darcsin(dsin(e) * dsin(l)); + double ra = darctan2(dcos(e) * dsin(l), dcos(l)) / 15.0; + ra = fix_hour(ra); + double eq_t = q / 15.0 - ra; + + return DoublePair(dd, eq_t); + } + + /* compute equation of time */ + double equation_of_time(double jd) + { + return sun_position(jd).second; + } + + /* compute declination angle of sun */ + double sun_declination(double jd) + { + return sun_position(jd).first; + } + + /* compute mid-day (Dhuhr, Zawal) time */ + double compute_mid_day(double _t) + { + double t = equation_of_time(julian_date + _t); + double z = fix_hour(12 - t); + return z; + } + + /* compute time for a given angle G */ + double compute_time(double g, double t) + { + double d = sun_declination(julian_date + t); + double z = compute_mid_day(t); + double v = 1.0 / 15.0 * darccos((-dsin(g) - dsin(d) * dsin(latitude)) / (dcos(d) * dcos(latitude))); + return z + (g > 90.0 ? - v : v); + } + + /* compute the time of Asr */ + double compute_asr(int step, double t) // Shafii: step=1, Hanafi: step=2 + { + double d = sun_declination(julian_date + t); + double g = -darccot(step + dtan(fabs(latitude - d))); + return compute_time(g, t); + } + +/* ---------------------- Compute Prayer Times ----------------------- */ + + // array parameters must be at least of size TimesCount + + /* compute prayer times at given julian date */ + void compute_times(double times[]) + { + day_portion(times); + + times[Fajr] = compute_time(180.0 - method_params[calc_method].fajr_angle, times[Fajr]); + times[Sunrise] = compute_time(180.0 - 0.833, times[Sunrise]); + times[Dhuhr] = compute_mid_day(times[Dhuhr]); + times[Asr] = compute_asr(1 + asr_juristic, times[Asr]); + times[Sunset] = compute_time(0.833, times[Sunset]); + times[Maghrib] = compute_time(method_params[calc_method].maghrib_value, times[Maghrib]); + times[Isha] = compute_time(method_params[calc_method].isha_value, times[Isha]); + } + + + /* compute prayer times at given julian date */ + void compute_day_times(double times[]) + { + double default_times[] = { 5, 6, 12, 13, 18, 18, 18 }; // default times + for (int i = 0; i < TimesCount; ++i) + times[i] = default_times[i]; + + for (int i = 0; i < NUM_ITERATIONS; ++i) + compute_times(times); + + adjust_times(times); + } + + + /* adjust times in a prayer time array */ + void adjust_times(double times[]) + { + for (int i = 0; i < TimesCount; ++i) + times[i] += timezone - longitude / 15.0; + times[Dhuhr] += dhuhr_minutes / 60.0; // Dhuhr + if (method_params[calc_method].maghrib_is_minutes) // Maghrib + times[Maghrib] = times[Sunset] + method_params[calc_method].maghrib_value / 60.0; + if (method_params[calc_method].isha_is_minutes) // Isha + times[Isha] = times[Maghrib] + method_params[calc_method].isha_value / 60.0; + + if (adjust_high_lats != None) + adjust_high_lat_times(times); + } + + /* adjust Fajr, Isha and Maghrib for locations in higher latitudes */ + void adjust_high_lat_times(double times[]) + { + double night_time = time_diff(times[Sunset], times[Sunrise]); // sunset to sunrise + + // Adjust Fajr + double fajr_diff = night_portion(method_params[calc_method].fajr_angle) * night_time; + if (isnan(times[Fajr]) || time_diff(times[Fajr], times[Sunrise]) > fajr_diff) + times[Fajr] = times[Sunrise] - fajr_diff; + + // Adjust Isha + double isha_angle = method_params[calc_method].isha_is_minutes ? 18.0 : method_params[calc_method].isha_value; + double isha_diff = night_portion(isha_angle) * night_time; + if (isnan(times[Isha]) || time_diff(times[Sunset], times[Isha]) > isha_diff) + times[Isha] = times[Sunset] + isha_diff; + + // Adjust Maghrib + double maghrib_angle = method_params[calc_method].maghrib_is_minutes ? 4.0 : method_params[calc_method].maghrib_value; + double maghrib_diff = night_portion(maghrib_angle) * night_time; + if (isnan(times[Maghrib]) || time_diff(times[Sunset], times[Maghrib]) > maghrib_diff) + times[Maghrib] = times[Sunset] + maghrib_diff; + } + + + /* the night portion used for adjusting times in higher latitudes */ + double night_portion(double angle) + { + switch (adjust_high_lats) + { + case AngleBased: + return angle / 60.0; + case MidNight: + return 1.0 / 2.0; + case OneSeventh: + return 1.0 / 7.0; + default: + // Just to return something! + // In original library nothing was returned + // Maybe I should throw an exception + // It must be impossible to reach here + return 0; + } + } + + /* convert hours to day portions */ + void day_portion(double times[]) + { + for (int i = 0; i < TimesCount; ++i) + times[i] /= 24.0; + } + +/* ---------------------- Misc Functions ----------------------- */ + + /* compute the difference between two times */ + static double time_diff(double time1, double time2) + { + return fix_hour(time2 - time1); + } + + static std::string int_to_string(int num) + { + char tmp[16]; + tmp[0] = '\0'; + sprintf(tmp, "%d", num); + return std::string(tmp); + } + + /* add a leading 0 if necessary */ + static std::string two_digits_format(int num) + { + char tmp[16]; + tmp[0] = '\0'; + sprintf(tmp, "%2.2d", num); + return std::string(tmp); + } + +/* ---------------------- Julian Date Functions ----------------------- */ + + /* calculate julian date from a calendar date */ + double get_julian_date(int year, int month, int day) + { + if (month <= 2) + { + year -= 1; + month += 12; + } + + double a = floor(year / 100.0); + double b = 2 - a + floor(a / 4.0); + + return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524.5; + } + + /* convert a calendar date to julian date (second method) */ + double calc_julian_date(int year, int month, int day) + { + double j1970 = 2440588.0; + tm date = { 0 }; + date.tm_year = year - 1900; + date.tm_mon = month - 1; + date.tm_mday = day; + date.tm_isdst = -1; // determine it yourself from system + time_t ms = mktime(&date); // seconds since midnight Jan 1, 1970 + double days = floor(ms / (double) (60 * 60 * 24)); + return j1970 + days - 0.5; + } + +/* ---------------------- Trigonometric Functions ----------------------- */ + + /* degree sin */ + static double dsin(double d) + { + return sin(deg2rad(d)); + } + + /* degree cos */ + static double dcos(double d) + { + return cos(deg2rad(d)); + } + + /* degree tan */ + static double dtan(double d) + { + return tan(deg2rad(d)); + } + + /* degree arcsin */ + static double darcsin(double x) + { + return rad2deg(asin(x)); + } + + /* degree arccos */ + static double darccos(double x) + { + return rad2deg(acos(x)); + } + + /* degree arctan */ + static double darctan(double x) + { + return rad2deg(atan(x)); + } + + /* degree arctan2 */ + static double darctan2(double y, double x) + { + return rad2deg(atan2(y, x)); + } + + /* degree arccot */ + static double darccot(double x) + { + return rad2deg(atan(1.0 / x)); + } + + /* degree to radian */ + static double deg2rad(double d) + { + return d * M_PI / 180.0; + } + + /* radian to degree */ + static double rad2deg(double r) + { + return r * 180.0 / M_PI; + } + + /* range reduce angle in degrees. */ + static double fix_angle(double a) + { + a = a - 360.0 * floor(a / 360.0); + a = a < 0.0 ? a + 360.0 : a; + return a; + } + + /* range reduce hours to 0..23 */ + static double fix_hour(double a) + { + a = a - 24.0 * floor(a / 24.0); + a = a < 0.0 ? a + 24.0 : a; + return a; + } + +private: +/* ---------------------- Private Variables -------------------- */ + + MethodConfig method_params[CalculationMethodsCount]; + + CalculationMethod calc_method; // caculation method + JuristicMethod asr_juristic; // Juristic method for Asr + AdjustingMethod adjust_high_lats; // adjusting method for higher latitudes + double dhuhr_minutes; // minutes after mid-day for Dhuhr + + double latitude; + double longitude; + double timezone; + double julian_date; + +/* --------------------- Technical Settings -------------------- */ + + static const int NUM_ITERATIONS = 1; // number of iterations needed to compute times +};