import os, datetime, math, threading, json
import customtkinter as ctk
from tkinter import messagebox
import ephem
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# ---> SILAKAN COPAS HIJRI_DB LENGKAP DI SINI <---
HIJRI_DB = {
1447: [["Muharam", "", "", 30], ["Safar", "", "", 29], ["Rabiulawal", "", "", 30], ["Rabiulakhir", "", "", 30], ["Jumadilawal", "", "", 29], ["Jumadilakhir", "", "", 30], ["Rajab", "", "", 30], ["Syakban", "", "", 29], ["Ramadan", "Rabu Legi", "18-Feb-2026", 30]],
1448: [["Muharam", "", "", 30], ["Safar", "", "", 29], ["Rabiulawal", "", "", 30], ["Rabiulakhir", "", "", 30], ["Jumadilawal", "", "", 29], ["Jumadilakhir", "", "", 30], ["Rajab", "", "", 30], ["Syakban", "", "", 29], ["Ramadan", "Senin", "08-Feb-2027", 29]],
}
try:
DB_JSON_PATH = os.path.join(BASE_DIR, "db_hijriah.json")
if os.path.exists(DB_JSON_PATH):
with open(DB_JSON_PATH, "r", encoding="utf-8") as f:
HIJRI_DB.update({int(k): v for k, v in json.load(f).items()})
except Exception: pass
class Menu23App(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Tabel Elongasi Hilal Ramadhan (Modul 23)")
self.geometry("1100x650")
ctk.set_appearance_mode("Dark")
self.setup_ui()
def get_header(self, width):
return "\n".join(line.center(width) for line in ["By the Name of Allah", "KHGT Times 7.2 - Data Elongasi Ramadhan"])
def setup_ui(self):
self.sidebar = ctk.CTkFrame(self, width=250)
self.sidebar.pack(side="left", fill="y", padx=10, pady=10)
ctk.CTkLabel(self.sidebar, text="TABEL ELONGASI HILAL\n(RAMADHAN)", font=("Segoe UI", 16, "bold"), text_color="#00E5FF").pack(pady=20)
self.btn_hitung = ctk.CTkButton(self.sidebar, text="▶ PROSES DATA", font=("Segoe UI", 12, "bold"), command=self.hitung_elongasi_ramadhan, height=45)
self.btn_hitung.pack(pady=20, fill="x", padx=20)
self.lbl_status = ctk.CTkLabel(self.sidebar, text="Sistem Siap", text_color="#00E676")
self.lbl_status.pack()
self.textbox = ctk.CTkTextbox(self, font=("Consolas", 13), wrap="none")
self.textbox.pack(side="right", fill="both", expand=True, padx=10, pady=10)
self.textbox.insert("1.0", "Klik '▶ PROSES DATA' untuk mengekstrak tabel jarak sudut (Elongasi).")
self.textbox.configure(state="disabled")
def hitung_elongasi_ramadhan(self):
threading.Thread(target=self._proses_tabel, daemon=True).start()
def _proses_tabel(self):
self.after(0, lambda: self.lbl_status.configure(text="Mengekstrak Data Elongasi...", text_color="#FFAB40"))
self.after(0, lambda: self.btn_hitung.configure(state="disabled"))
self.after(0, lambda: self.textbox.configure(state="normal"))
self.after(0, lambda: self.textbox.delete("1.0", "end"))
output_lines = [
self.get_header(130),
"[ DATA ELONGASI HILAL 50 TAHUN (RAMADHAN 1447H - 1496H) ]".center(130),
"Metode: Akselerasi Database KHGT vs Toposentrik Sabang".center(130),
"=" * 130,
f"{'Tahun':<7} | {'Ijtimak (UTC)':<16} | {'KHGT: Parameter Global (Geo)':<40} | {'MABIMS: Sabang (Topo)':<30} | {'Status'}",
"-" * 130
]
sabang = ephem.Observer()
sabang.lat, sabang.lon = math.radians(5.8942), math.radians(95.3184)
sabang.elevation, sabang.pressure, sabang.temp = 0, 1010, 25
sun, moon = ephem.Sun(), ephem.Moon()
bulan_map = {"Jan":1, "Feb":2, "Mar":3, "Apr":4, "May":5, "Jun":6, "Jul":7, "Aug":8, "Sep":9, "Oct":10, "Nov":11, "Dec":12}
for thn_h in range(1447, 1497):
try:
if thn_h not in HIJRI_DB: continue
# Index 8 = Ramadhan
parts = HIJRI_DB[thn_h][8][2].split('-')
dt_khgt = datetime.datetime(int(parts[2]), bulan_map.get(parts[1], 1), int(parts[0]))
dt_rukyat = dt_khgt - datetime.timedelta(days=1)
ijtimak = ephem.previous_new_moon(ephem.Date(dt_rukyat) + 2)
str_ijtimak = ijtimak.datetime().strftime("%d-%b %H:%M")
# Format KHGT Eln didahulukan
str_khgt = "Terpenuhi (Eln >= 8°, Alt >= 5°)"
sabang.date = ephem.Date(dt_rukyat.replace(hour=0, minute=0, second=0))
try: waktu_sunset = sabang.next_setting(sun)
except: waktu_sunset = ephem.Date(dt_rukyat.replace(hour=11, minute=30, second=0))
sabang.date = waktu_sunset
sun.compute(sabang); moon.compute(sabang)
alt_s = math.degrees(moon.alt)
eln_s = math.degrees(ephem.separation(sun, moon))
umur_s = (waktu_sunset - ijtimak) * 24
if alt_s >= 3.0 and eln_s >= 6.4 and umur_s > 0:
status_s, kesimpulan = "Lolos", "Serentak"
else:
status_s, kesimpulan = "Belum", "Beda (MABIMS Mundur)"
# Format SABANG Eln didahulukan
str_sabang = f"Eln:{eln_s:5.2f}°, Alt:{alt_s:5.2f}° [{status_s}]"
output_lines.append(f"{thn_h} H | {str_ijtimak:<16} | {str_khgt:<40} | {str_sabang:<30} | {kesimpulan}")
if (thn_h - 1446) % 10 == 0 and thn_h != 1496: output_lines.append("-" * 130)
except Exception as err:
output_lines.append(f"{thn_h} H | Error: {str(err)}")
output_lines.append("=" * 130)
self.after(0, lambda: self.textbox.insert("1.0", "\n".join(output_lines)))
self.after(0, lambda: self.textbox.configure(state="disabled"))
self.after(0, lambda: self.lbl_status.configure(text="Selesai", text_color="#00E676"))
self.after(0, lambda: self.btn_hitung.configure(state="normal"))
if __name__ == "__main__":
app = Menu23App()
app.mainloop()