مرحله اول: معرفی کلی سیستم
کلاس 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° | 4° | شیعه |
// نمونه تعریف روش محاسبه در 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"
ساعت = 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; }
خلاصه و نکات مهم
نکات کلیدی:
- دقت محاسبات: تمام محاسبات بر اساس فرمولهای نجومی دقیق انجام میشود
- انعطافپذیری: پشتیبانی از روشهای مختلف محاسبه برای مناطق مختلف جهان
- تنظیم خودکار: تنظیم خودکار برای عرضهای جغرافیایی بالا
- خروجی استاندارد: خروجی به صورت اعداد اعشاری قابل تبدیل به هر فرمت
برای استفاده بهینه، توصیه میشود اوقات را برای چندین روز آینده محاسبه و ذخیره کنید تا از محاسبات مکرر جلوگیری شود.
دقت کنید که تغییر موقعیت جغرافیایی یا روش محاسبه نیاز به محاسبه مجدد تمام اوقات دارد.
منابع و مراجع:
- الگوریتمهای نجومی برای محاسبه موقعیت خورشید
- استانداردهای بینالمللی اوقات شرعی
- فرمولهای ریاضی برای تبدیل مختصات جغرافیایی