راهنمای محاسبه اوقات شرعی

تحلیل کامل کلاس PrayTimes و الگوریتم‌های محاسبه اوقات اذان

مرحله اول: معرفی کلی سیستم

کلاس PrayTimes یک سیستم پیشرفته برای محاسبه اوقات شرعی است که بر اساس موقعیت جغرافیایی، تاریخ و روش‌های مختلف محاسبه عمل می‌کند.

ویژگی‌های کلیدی:

  • محاسبه دقیق بر اساس Julian Date
  • پشتیبانی از روش‌های مختلف محاسبه (MWL, ISNA, Karachi و...)
  • تنظیم خودکار برای عرض‌های جغرافیایی بالا
  • خروجی به صورت اعداد اعشاری (ساعت از روز)
// نمونه ایجاد instance از کلاس PrayTimes
PrayTimes prayTimes = PrayTimes(
    calendar: DateTime(2024, 3, 15),           // تاریخ
    coordinates: Coordinates(                   // موقعیت جغرافیایی
        latitude: 35.6892,                     // عرض جغرافیایی تهران
        longitude: 51.3890                     // طول جغرافیایی تهران
    ),
    method: CalculationMethod.Tehran,           // روش محاسبه
    highLatitudesMethod: HighLatitudesMethod.NightMiddle,
    in12Hours: false                           // فرمت 24 ساعته
);
هر زمان اذان به صورت یک عدد اعشاری بین 0 تا 24 نمایش داده می‌شود که نشان‌دهنده ساعت از ابتدای روز است.

مرحله دوم: ساختار کلاس و متدهای اصلی

متغیرهای اصلی کلاس:

class PrayTimes {
    // اوقات به صورت اعداد اعشاری (0-24)
    late double imsak;      // امساک
    late double fajr;       // فجر
    late double sunrise;    // طلوع آفتاب
    late double dhuhr;      // ظهر
    late double asr;        // عصر
    late double sunset;     // غروب آفتاب
    late double maghrib;    // مغرب
    late double isha;       // عشا
    late double midnight;   // نیمه شب

    List<double> allTimes = [];        // لیست تمام اوقات
    List<DateTime?> allinDateTime = []; // تبدیل به DateTime
}

مراحل محاسبه در Constructor:

1 محاسبه Julian Date:
jdate = julian(year, month, day) - longitude / (15.0 * 24.0)

Julian Date یک سیستم شمارش روزها از تاریخ مشخصی است که در نجوم استفاده می‌شود.

2 محاسبه اوقات اولیه:
// محاسبه هر یک از اوقات بر اساس زاویه خورشید
double fajr = sunAngleTime(jdate, method.fajr, _DEFAULT_FAJR, true, coordinates);
double sunrise = sunAngleTime(jdate, riseSetAngle(coordinates), _DEFAULT_SUNRISE, true, coordinates);
double dhuhr = midDay(jdate, _DEFAULT_DHUHR);
double asr = asrTime(jdate, asrMethod.asrFactor, _DEFAULT_ASR, coordinates);
double sunset = sunAngleTime(jdate, riseSetAngle(coordinates), _DEFAULT_SUNSET, false, coordinates);
double maghrib = sunAngleTime(jdate, method.maghrib, _DEFAULT_MAGHRIB, false, coordinates);
double isha = sunAngleTime(jdate, method.isha, _DEFAULT_ISHA, false, coordinates);
3 تنظیم TimeZone:
// محاسبه offset برای timezone و longitude
double offset = DateTime.now().timeZoneOffset.inMilliseconds / (60 * 60 * 1000.0);
double addToAll = offset - coordinates.longitude / 15.0;

// اعمال offset به تمام اوقات
fajr += addToAll;
sunrise += addToAll;
dhuhr += addToAll;
// ... سایر اوقات

مرحله سوم: الگوریتم‌های محاسبه دقیق

1. محاسبه موقعیت خورشید (sunPosition):

DeclEqt sunPosition(double jd) {
    double D = jd - 2451545.0;                    // روزهای گذشته از epoch
    double g = (357.529 + 0.98560028 * D) % 360;  // Mean anomaly
    double q = (280.459 + 0.98564736 * D) % 360;  // Mean longitude

    // محاسبه True longitude
    double L = (q + 1.915 * sin(dtr(g)) + 0.020 * sin(dtr(2.0 * g))) % 360;

    double e = 23.439 - 0.00000036 * D;           // Obliquity of ecliptic

    // محاسبه Right Ascension و Equation of Time
    double RA = rtd(atan2(cos(dtr(e)) * sin(dtr(L)), cos(dtr(L)))) / 15.0;
    double eqt = q / 15.0 - fixHour(RA);
    double decl = asin(sin(dtr(e)) * sin(dtr(L))); // Declination

    return DeclEqt(decl, eqt);
}

2. محاسبه زمان بر اساس زاویه خورشید (sunAngleTime):

double sunAngleTime(double jdate, MinuteOrAngleDouble angle, double time,
                   bool ccw, Coordinates coordinates) {
    double decl = sunPosition(jdate + time).declination;
    double noon = dtr(midDay(jdate, time));

    // فرمول اصلی محاسبه زمان بر اساس زاویه
    double t = acos((-sin(dtr(angle.value)) -
                    sin(decl) * sin(dtr(coordinates.latitude))) /
                   (cos(decl) * cos(dtr(coordinates.latitude)))) / 15.0;

    return rtd(noon + (ccw ? -t : t));
}
پارامتر ccw (Counter Clock Wise) تعیین می‌کند که آیا زمان قبل از ظهر (true) یا بعد از ظهر (false) محاسبه شود.

3. محاسبه زمان عصر (asrTime):

double asrTime(double jdate, double factor, double time, Coordinates coordinates) {
    double decl = sunPosition(jdate + time).declination;

    // محاسبه زاویه بر اساس فاکتور عصر (1 برای استاندارد، 2 برای حنفی)
    double angle = -atan(1 / (factor + tan(abs(dtr(coordinates.latitude) - decl))));

    return sunAngleTime(jdate, MinuteOrAngleDouble.deg(rtd(angle)), time, false, coordinates);
}

مرحله چهارم: روش‌های محاسبه مختلف

هر روش محاسبه دارای زوایای مختلفی برای فجر، مغرب و عشا است:

روش محاسبه زاویه فجر زاویه عشا مغرب کاربرد
MWL (Muslim World League) 18° 17° غروب + دقیقه اروپا، آمریکا
ISNA (North America) 15° 15° غروب + دقیقه آمریکای شمالی
University of Karachi 18° 18° غروب + دقیقه پاکستان، هند
Umm Al-Qura (مکه) 18.5° 90 دقیقه بعد مغرب غروب + دقیقه عربستان سعودی
Egyptian Authority 19.5° 17.5° غروب + دقیقه مصر، خاورمیانه
Institute of Tehran 17.7° 14° 4.5° ایران
Ithna Ashari 16° 14° شیعه
// نمونه تعریف روش محاسبه در enum
enum CalculationMethod {
    IthnaAshari,    // اثنی عشری
    Karachi,        // کراچی
    NorthAmerica,   // آمریکای شمالی
    MWL,            // رابطه جهانی اسلامی
    UmmAlQura,      // ام القری
    Egyptian,       // مصری
    Tehran,         // تهران
}

مرحله پنجم: تنظیمات عرض‌های جغرافیایی بالا

در عرض‌های جغرافیایی بالا (بالای 49 درجه)، ممکن است برخی اوقات قابل محاسبه نباشند. برای حل این مشکل از روش‌های مختلفی استفاده می‌شود:

روش‌های تنظیم:

1 NightMiddle (وسط شب):

فجر و عشا بر اساس نیمه شب محاسبه می‌شوند.

portion = 1/2 * nightTime
2 AngleBased (بر اساس زاویه):

بر اساس زاویه مشخص شده محاسبه می‌شود.

portion = angle/60 * nightTime
3 OneSeventh (یک هفتم شب):

یک هفتم از طول شب استفاده می‌شود.

portion = 1/7 * nightTime
// تنظیم اوقات برای عرض‌های جغرافیایی بالا
if (highLatitudesMethod != HighLatitudesMethod.None) {
    double nightTime = timeDiff(sunset, sunrise);

    fajr = adjustHLTime(highLatitudesMethod, fajr, sunrise,
                       method.fajr.value, nightTime, true);
    isha = adjustHLTime(highLatitudesMethod, isha, sunset,
                       method.isha.value, nightTime, false);
}
در کد پروژه، اگر عرض جغرافیایی کمتر از 49 درجه باشد، به طور خودکار از روش NightMiddle استفاده می‌شود.

مرحله ششم: تبدیل اعداد اعشاری به زمان

خروجی کلاس PrayTimes اعداد اعشاری هستند که نشان‌دهنده ساعت از ابتدای روز می‌باشند. این اعداد باید به فرمت زمان قابل خواندن تبدیل شوند.

نحوه تبدیل:

1 جدا کردن ساعت و دقیقه:
String get floatToTime24 {
    var time = this;
    if (time == null || time.isNaN) return "----";

    time = _fixHour(time + 0.5 / 60.0); // اضافه کردن 0.5 دقیقه برای گرد کردن
    int hours = (time).floor();          // بخش صحیح = ساعت
    double minutes = ((time - hours) * 60.0).floorToDouble(); // بخش اعشاری × 60 = دقیقه

    // فرمت کردن با صفر اضافی
    return "${hours.toString().padLeft(2, '0')}:${minutes.round().toString().padLeft(2, '0')}";
}
2 مثال عملی:
اگر fajr = 5.25 باشد:
ساعت = 5 (بخش صحیح)
دقیقه = 0.25 × 60 = 15
نتیجه = "05:15"

تبدیل به فرمت 12 ساعته:

String get floatToTime12 {
    // ... محاسبه ساعت و دقیقه مشابه بالا

    if (hours >= 12 && hours < 24) {
        var hourss = hours - 12;
        if (hourss == 0) hourss = 12;  // 12 PM نه 0 PM
        // فرمت کردن...
    } else {
        var hourss = hours;
        if (hourss == 0) hourss = 12;  // 12 AM نه 0 AM
        // فرمت کردن...
    }
}

String get amPm {
    int hours = (this).floor();
    return hours >= 12 && hours < 24 ? 'PM' : 'AM';
}

مرحله هفتم: نمونه کد کامل و کاربردی

نمونه استفاده کامل:

// تعریف موقعیت جغرافیایی تهران
Coordinates tehranCoords = Coordinates(
    latitude: 35.6892,
    longitude: 51.3890,
    elevation: 1200  // ارتفاع از سطح دریا (متر)
);

// ایجاد instance برای تاریخ امروز
PrayTimes prayTimes = PrayTimes(
    calendar: DateTime.now(),
    coordinates: tehranCoords,
    method: CalculationMethod.Tehran,
    highLatitudesMethod: HighLatitudesMethod.NightMiddle,
    in12Hours: false
);

// دریافت اوقات
print("فجر: ${prayTimes.fajr.floatToTime24}");      // مثال: "05:15"
print("طلوع: ${prayTimes.sunrise.floatToTime24}");   // مثال: "06:45"
print("ظهر: ${prayTimes.dhuhr.floatToTime24}");      // مثال: "12:30"
print("عصر: ${prayTimes.asr.floatToTime24}");        // مثال: "15:20"
print("مغرب: ${prayTimes.maghrib.floatToTime24}");   // مثال: "18:15"
print("عشا: ${prayTimes.isha.floatToTime24}");       // مثال: "19:45"

// تبدیل به DateTime برای استفاده در برنامه
DateTime fajrDateTime = DateTime(
    prayTimes.calendar.year,
    prayTimes.calendar.month,
    prayTimes.calendar.day
).add(Duration(
    hours: prayTimes.fajr.floor(),
    minutes: ((prayTimes.fajr - prayTimes.fajr.floor()) * 60).round()
));

ایجاد لیست اوقات برای چندین روز:

List<PrayTimeModel> getPrayTimesForMonth(DateTime startDate, Coordinates coords) {
    List<PrayTimeModel> allPrayTimes = [];

    for (int i = 0; i < 30; i++) {
        DateTime currentDate = startDate.add(Duration(days: i));

        PrayTimes pt = PrayTimes(
            calendar: currentDate,
            coordinates: coords,
            method: CalculationMethod.Tehran,
            highLatitudesMethod: HighLatitudesMethod.NightMiddle,
            in12Hours: false,
        );

        // اضافه کردن هر وقت به لیست
        for (int j = 0; j < pt.allTimes.length; j++) {
            allPrayTimes.add(PrayTimeModel(
                enumTime: EnumTime.values[j],
                name: EnumTime.values[j].name,
                timeInString: pt.allTimes[j].floatToTime24,
                dateTime: currentDate.add(Duration(
                    hours: pt.allTimes[j].floor(),
                    minutes: ((pt.allTimes[j] - pt.allTimes[j].floor()) * 60).round()
                )),
            ));
        }
    }

    return allPrayTimes;
}

خلاصه و نکات مهم

نکات کلیدی:

  • دقت محاسبات: تمام محاسبات بر اساس فرمول‌های نجومی دقیق انجام می‌شود
  • انعطاف‌پذیری: پشتیبانی از روش‌های مختلف محاسبه برای مناطق مختلف جهان
  • تنظیم خودکار: تنظیم خودکار برای عرض‌های جغرافیایی بالا
  • خروجی استاندارد: خروجی به صورت اعداد اعشاری قابل تبدیل به هر فرمت
برای استفاده بهینه، توصیه می‌شود اوقات را برای چندین روز آینده محاسبه و ذخیره کنید تا از محاسبات مکرر جلوگیری شود.
دقت کنید که تغییر موقعیت جغرافیایی یا روش محاسبه نیاز به محاسبه مجدد تمام اوقات دارد.

منابع و مراجع:

  • الگوریتم‌های نجومی برای محاسبه موقعیت خورشید
  • استانداردهای بین‌المللی اوقات شرعی
  • فرمول‌های ریاضی برای تبدیل مختصات جغرافیایی