You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

14 KiB

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

این مستند برای آشنایی همکاران فنی (مانند تیم فلاتر) و غیر فنی با سازوکار تقویم پروژه «امام جواد» و همچنین منطق محاسبه اوقات شرعی تهیه شده است. مطالب شامل معرفی ساختار دیتای تقویم، توضیح کامل API تنظیمات قمری (adjustemnts)، نحوه بهره‌گیری در سناریوهای واقعی و نمونه‌کدهای کاربردی است.

نمای کلی سیستم تقویم

  • ماژول: apps/dobodbi_calendar
  • مدل اصلی: CalendarOccasions
    • فیلدهای کلیدی: title عنوان مناسبت، occasion_type نوع تاریخ (میلادی یا قمری)، dates لیست تاریخ‌ها، event_type دسته‌بندی (ملی، مذهبی و ...)، is_yearly تکرار سالانه، تایم‌استمپ‌های created_at و updated_at.
  • سریالایزر: CalendarSerializer که تاریخ‌ها را به ساختار قابل مصرف برای کلاینت تبدیل می‌کند و نوع مناسبت را در فیلد type قرار می‌دهد.
  • نمای لیستی: CalendarList با مسیر /api/calendar/occasions/ که بدون صفحه‌بندی پاسخ می‌دهد و آخرین زمان به‌روزرسانی را برمی‌گرداند تا همگام‌سازی کلاینت راحت‌تر انجام شود.
  • تنظیمات پویا: با استفاده از dynamic_preferences و کلید calendar__Adjustment ذخیره می‌شود. مقدار خام این تنظیمات در پایگاه داده نگهداری شده و از طریق پنل ادمین قابل ویرایش است.

API تنظیمات قمری (Adjustemnts)

  • مسیر: GET /api/calendar/adjustemnts/
  • منبع داده: ترجمهٔ مقدار ذخیره شده در dynamic_preferences برای کلید calendar__Adjustment.
  • کارکرد کلی: ارائهٔ سناریوهای از پیش تعریف‌شده برای تطبیق تقویم قمری با واقعیت رؤیت هلال یا تقویم رسمی کشورها.

ساختار پاسخ نمونه

[
  {
    "adjust": 0,
    "current": 0,
    "map": {
      "1444": [354, 30, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30, 29],
      "1445": [354, 30, 30, 30, 29, 30, 29, 29, 30, 29, 30, 29, 29],
      "1446": [355, 30, 30, 30, 29, 30, 30, 29, 30, 29, 29, 29, 30],
      "1447": [355, 29, 30, 30, 29, 30, 30, 29, 30, 29, 29, 30, 29]
    }
  },
  {
    "adjust": -1,
    "current": 0,
    "map": { "...": "..." }
  },
  {
    "adjust": 1,
    "current": 0,
    "map": { "...": "..." }
  }
]

معنی فیلدها

فیلد توضیح
adjust مقدار جبرانی: -1 یک روز کم می‌کند، 0 حالت مرجع، +1 یک روز اضافه می‌کند.
current وضعیت فعال که می‌تواند توسط کلاینت یا پنل ادمین برای علامت‌گذاری حالت انتخابی استفاده شود (در نمونه فعلی صفر است).
map دیکشنری سال قمری ↦ آرایه شامل ۱ + ۱۲ مقدار: عدد اول تعداد روزهای سال (۳۵۴ یا ۳۵۵)، ۱۲ عدد بعدی طول هر ماه قمری.

انتخاب حالت مناسب با مثال

حالت زمان استفاده پیشنهادی مثال عملی
adjust = 0 حالت مرجع و عمومی نمایش تقویم در سامانه آموزشی بدون نیاز به تصحیح محلی.
adjust = -1 مناطقی که شروع ماه قمری را یک روز زودتر اعلام می‌کنند یا پس از رصد مشخص می‌شود هلال یک روز زودتر دیده شده اگر در ایران رمضان ۲۹ روز اعلام شود اما محاسبه‌گر ۳۰ روزه باشد، با -1 آخرین روز حذف می‌شود.
adjust = +1 مناطقی که آغاز ماه را دیرتر اعلام می‌کنند یا برای همگام‌سازی با تقویم رسمی نیاز به افزودن روز دارند زمانی که کشوری عید قربان را یک روز دیرتر می‌گیرد؛ با +1 روز اضافه می‌شود.

سناریوی قدم‌به‌قدم: تعیین تاریخ عید فطر

  1. دریافت داده: configs = GET /api/calendar/adjustemnts/.
  2. انتخاب حالت: defaultConfig = configs.find(c => c.adjust === 0).
  3. استخراج سال هدف: ramadanProfile = defaultConfig.map['1445'].
  4. محاسبه طول رمضان: مقدار شاخص ۹ (ماه نهم). اگر ۲۹ بود → عید فطر روز ۲۹ رمضان، اگر ۳۰ بود → روز ۳۰.
  5. در صورت اختلاف رسمی، کافی است پیکربندی دیگری را انتخاب کنید (مثلاً adjust = +1) تا روز اضافه لحاظ شود.

سناریوی همگام‌سازی تقویم در اپلیکیشن

  1. در اولین اجرا، هرسه پیکربندی را ذخیره کنید.
  2. متناسب با موقعیت کاربر (یا انتخاب کاربر)، adjust مناسب را فعال نگه دارید. مقدار انتخابی را می‌توانید در کلاینت یا سرور ذخیره کنید.
  3. برای تبدیل «روز سال قمری» به روز میلادی:
    • اختلاف روز تا ابتدای سال قمری را با جمع ۱۲ ماه محاسبه کنید.
    • با استفاده از تاریخ میلادی مرجع (مثلاً شروع سال قمری در تقویم رسمی)، اختلاف را اعمال کنید تا تاریخ میلادی به‌دست آید.
  4. اگر داده‌های CalendarOccasions نوع lunar داشتند، از نقشه انتخاب‌شده برای محاسبه تاریخ معادل استفاده کنید و مقدار نهایی را به رابط کاربری نمایش دهید.

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

مبنای پروژه فایل prayer_times_calculation_guide.html است که مراحل استفاده از الگوریتم PrayTimes را تشریح کرده است. خلاصه فرآیند:

  1. ورودی‌ها: تاریخ میلادی، مختصات (عرض/طول جغرافیایی، ارتفاع)، روش محاسبه (مثلاً Tehran، MWL)، روش جبران عرض‌های بالا و فرمت خروجی (۱۲ یا ۲۴ ساعته).
  2. محاسبه تاریخ ژولیَن (Julian Date): jdate = julian(year, month, day) - longitude / (15 × 24).
  3. موقعیت خورشید: با تابعی مشابه sunPosition(jdate) زاویه میل خورشید (declination) و معادله زمان (equation of time) به‌دست می‌آید.
  4. محاسبه اوقات پایه:
    • فجر، طلوع، مغرب، عشا: با sunAngleTime و زاویه‌های مخصوص هر روش محاسبه می‌شوند.
    • ظهر: با midDay.
    • عصر: با asrTime و فاکتور مربوط به مذهب (1 برای شافعی/جعفری، 2 برای حنفی).
  5. تنظیم اختلاف طول جغرافیایی و منطقه زمانی:
    • offset = timezoneHours - longitude / 15
    • جمع این مقدار با همه زمان‌های محاسبه‌شده.
  6. جبران عرض‌های جغرافیایی بالا: در صورت انتخاب روش‌هایی مثل NightMiddle یا AngleBased، طول شب محاسبه و زمان‌های فجر/عشا تعدیل می‌شوند.
  7. تبدیل خروجی اعشاری به ساعت:
    • ساعت = بخش صحیح عدد.
    • دقیقه = (عدد - ساعت) × 60.
    • در صورت نیاز به فرمت ۱۲ ساعته، AM/PM مطابق قواعد اضافه می‌شود.

چه ابزاری برای محاسبه استفاده کنیم؟

  • بک‌اند (Python/Django): در صورت نیاز به محاسبه سمت سرور می‌توانید از پیاده‌سازی استاندارد PrayTimes یا کتابخانه‌های معتبری مانند praytimes (نسخه پایتونی) بهره ببرید. نتیجه را می‌توان کش کرد و فقط در صورت تغییر مختصات یا تاریخ دوباره محاسبه نمود.
  • کلاینت فلاتر: استفاده از کلاس سفارشی پروژه یا پکیج‌های آماده نظیر adhan_dart برای محاسبات سریع و دقیق توصیه می‌شود.
  • وب/جاوااسکریپت: کتابخانه‌هایی مثل adhan یا نسخهٔ رسمی PrayTimes.js به‌خوبی نیاز را پوشش می‌دهند.

نمونه کد فلاتر

دریافت و استفاده از تنظیمات قمری

import 'dart:convert';
import 'package:http/http.dart' as http;

class LunarAdjustConfig {
  final int adjust;
  final Map<String, List<int>> map;

  LunarAdjustConfig({required this.adjust, required this.map});

  factory LunarAdjustConfig.fromJson(Map<String, dynamic> json) {
    return LunarAdjustConfig(
      adjust: json['adjust'] as int,
      map: (json['map'] as Map<String, dynamic>).map(
        (key, value) => MapEntry(key, List<int>.from(value)),
      ),
    );
  }
}

Future<List<LunarAdjustConfig>> fetchAdjustments() async {
  final res = await http.get(Uri.parse('https://example.com/api/calendar/adjustemnts/'));
  final data = jsonDecode(res.body) as List<dynamic>;
  return data.map((item) => LunarAdjustConfig.fromJson(item)).toList();
}

DateTime applyLunarOffset({
  required DateTime hijriYearStart,
  required LunarAdjustConfig config,
  required String hijriYear,
  required int hijriMonthIndex,
  required int hijriDay,
}) {
  final months = config.map[hijriYear] ?? [];
  if (months.length != 13) {
    throw ArgumentError('ساختار سال قمری نامعتبر است');
  }

  final daysFromYearStart = months
      .sublist(1, hijriMonthIndex)
      .fold<int>(0, (acc, value) => acc + value);

  final totalOffset = daysFromYearStart + (hijriDay - 1) + config.adjust;
  return hijriYearStart.add(Duration(days: totalOffset));
}

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

import 'package:adhan_dart/adhan_dart.dart';

PrayerTimes calculatePrayerTimes({
  required DateTime date,
  required Coordinates coordinates,
}) {
  final params = CalculationMethod.tehran();
  params.madhab = Madhab.shafi;
  final times = PrayerTimes(coordinates, date, params);
  return times;
}

void main() async {
  final configs = await fetchAdjustments();
  final defaultConfig = configs.firstWhere((c) => c.adjust == 0, orElse: () => configs.first);

  final hijriStart = DateTime(2023, 7, 19); // تاریخ میلادی شروع سال 1445 به تقویم رسمی
  final eidDate = applyLunarOffset(
    hijriYearStart: hijriStart,
    config: defaultConfig,
    hijriYear: '1445',
    hijriMonthIndex: 10, // ماه شوال (پس از رمضان)
    hijriDay: 1,
  );

  final times = calculatePrayerTimes(
    date: eidDate,
    coordinates: Coordinates(35.6892, 51.3890),
  );

  print('اذان صبح: ${times.fajrTime}');
  print('اذان مغرب: ${times.maghribTime}');
}

نمونه کد جاوااسکریپت (وب یا Node.js)

دریافت تنظیمات و محاسبه تاریخ قمری

import fetch from 'node-fetch';

async function fetchAdjustments() {
  const res = await fetch('https://example.com/api/calendar/adjustemnts/');
  return res.json();
}

function applyLunarOffset({ hijriYearStart, config, hijriYear, hijriMonthIndex, hijriDay }) {
  const months = config.map[hijriYear];
  if (!months || months.length !== 13) {
    throw new Error('ساختار سال قمری نامعتبر است');
  }

  const daysBeforeMonth = months.slice(1, hijriMonthIndex).reduce((sum, value) => sum + value, 0);
  const totalOffset = daysBeforeMonth + (hijriDay - 1) + config.adjust;

  const result = new Date(hijriYearStart);
  result.setDate(result.getDate() + totalOffset);
  return result;
}

// مثال استفاده
const configs = await fetchAdjustments();
const preferred = configs.find((c) => c.adjust === 1) ?? configs[0];
const eidAlAdha = applyLunarOffset({
  hijriYearStart: new Date('2024-06-08'), // شروع سال 1446 در تقویم رسمی منطقه هدف
  config: preferred,
  hijriYear: '1446',
  hijriMonthIndex: 12, // ذی‌الحجه
  hijriDay: 10,
});

console.log('تاریخ میلادی عید قربان:', eidAlAdha.toISOString().slice(0, 10));

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

import { PrayerTimes, Coordinates, CalculationMethod } from 'adhan';

function calculatePrayerTimes(date, lat, lng) {
  const params = CalculationMethod.Tehran();
  const coordinates = new Coordinates(lat, lng);
  const times = new PrayerTimes(coordinates, date, params);

  return {
    fajr: times.fajr.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
    sunrise: times.sunrise.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
    dhuhr: times.dhuhr.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
    asr: times.asr.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
    maghrib: times.maghrib.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
    isha: times.isha.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
  };
}

const todayTimes = calculatePrayerTimes(new Date(), 35.6892, 51.3890);
console.log(todayTimes);

جمع‌بندی

  • تقویم پروژه از ترکیب مدل CalendarOccasions و تنظیمات پویا calendar__Adjustment تشکیل شده و API ویژهٔ adjustemnts سه سناریوی جبران قمری را ارائه می‌کند.
  • برای محاسبهٔ اوقات شرعی می‌توان از الگوریتم مبتنی بر PrayTimes استفاده کرد که با ورودی‌های تاریخ، مختصات و روش محاسبه، زمان‌های اذان را بازمی‌گرداند.
  • نمونه کدهای فلاتر و جاوااسکریپت نشان می‌دهند چگونه می‌توان در کلاینت هم تاریخ‌های قمری را به میلادی تبدیل کرد و هم اوقات شرعی را به‌صورت محلی محاسبه نمود.