#[macro_use]
mod utils;
use umalqura::*;
mod umalqura;
mod umalqura_array;
pub use chrono::Duration;
use chrono::{Date, NaiveDate, Utc};
use std::cmp::Ordering;
use std::fmt;
use std::ops::Index;
use std::ops::{Add, Sub};
struct Map<T, U, const N: usize>([(T, U); N]);
impl<T: PartialEq, U, const N: usize> Index<T> for Map<T, U, N> {
type Output = U;
fn index(&self, t: T) -> &Self::Output {
&self.0.iter().find(|elem| elem.0 == t).unwrap().1
}
}
static MONTH_DICT: &Map<usize, &str, 12> = &Map([
(1, "محرم"),
(2, "صفر"),
(3, "ربيع الأول"),
(4, "ربيع الثاني"),
(5, "جمادي الأولى"),
(6, "جمادي الآخرة"),
(7, "رجب"),
(8, "شعبان"),
(9, "رمضان"),
(10, "شوال"),
(11, "ذو القعدة"),
(12, "ذو الحجة"),
]);
static DAY_DICT: &Map<&str, &str, 7> = &Map([
("Saturday", "السبت"),
("Sunday", "الاحد"),
("Monday", "الاثنين"),
("Tuesday", "الثلاثاء"),
("Wednesday", "الاربعاء"),
("Thursday", "الخميس"),
("Friday", "الجمعة"),
]);
#[derive(Debug, PartialEq, Eq)]
pub struct HijriDate {
pub day: usize,
pub month: usize,
pub month_len: usize,
pub year: usize,
pub day_name: String,
pub month_name: String,
pub day_gr: usize,
pub month_gr: usize,
pub year_gr: usize,
pub day_name_en: String,
pub month_name_en: String,
date_gr: Date<Utc>,
}
impl fmt::Display for HijriDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}", &self.format("%Y %M %D"))?;
writeln!(f, "{}", &self.format("%gD %gM %gY"))?;
Ok(())
}
}
impl Add<Duration> for HijriDate {
type Output = HijriDate;
fn add(self, other: Duration) -> HijriDate {
HijriDate::chrno_to_hijri(self.date_gr + other).unwrap()
}
}
impl Sub<Duration> for HijriDate {
type Output = HijriDate;
fn sub(self, other: Duration) -> HijriDate {
HijriDate::chrno_to_hijri(self.date_gr - other).unwrap()
}
}
impl Sub<HijriDate> for HijriDate {
type Output = Duration;
fn sub(self, other: HijriDate) -> Duration {
self.date_gr - other.date_gr
}
}
impl PartialOrd for HijriDate {
fn partial_cmp(&self, other: &HijriDate) -> Option<Ordering> {
Some(self.date_gr.cmp(&other.date_gr))
}
}
impl HijriDate {
pub fn from_hijri(year: usize, month: usize, day: usize) -> Result<Self, String> {
valid_hijri_date(year, month, day)?;
let month_name = MONTH_DICT[month].to_string();
let (year_gr, month_gr, day_gr) = hijri_to_gregorian(year, month, day);
let date_gr = format!("{}-{}-{}", year_gr, month_gr, day_gr);
let date_gr = if let Ok(date_gr) = NaiveDate::parse_from_str(&date_gr, "%Y-%m-%d") {
Date::<Utc>::from_utc(date_gr, Utc)
} else {
bail!("Wrong gegorean date foramt")
};
let day_name_en = date_gr.format("%A").to_string();
let day_name = DAY_DICT[&day_name_en].to_string();
let month_name_en = date_gr.format("%B").to_string();
let (_, _, _, month_len) = gegorean_to_hijri(year_gr, month_gr, day_gr);
Ok(Self {
day,
month,
month_len,
year,
day_name,
month_name,
day_gr,
month_gr,
year_gr,
day_name_en,
month_name_en,
date_gr,
})
}
pub fn from_gr(year_gr: usize, month_gr: usize, day_gr: usize) -> Result<Self, String> {
valid_greorian_date(year_gr, month_gr, day_gr)?;
let date_gr = format!("{}-{}-{}", year_gr, month_gr, day_gr);
let date_gr = if let Ok(date_gr) = NaiveDate::parse_from_str(&date_gr, "%Y-%m-%d") {
Date::<Utc>::from_utc(date_gr, Utc)
} else {
bail!("Wrong gegorean date foramt")
};
let (year, month, day, month_len) = gegorean_to_hijri(year_gr, month_gr, day_gr);
let month_name = MONTH_DICT[month].to_string();
let day_name_en = date_gr.format("%A").to_string();
let day_name = DAY_DICT[day_name_en.as_str()].to_string();
let month_name_en = date_gr.format("%B").to_string();
Ok(Self {
day,
month,
month_len,
year,
day_name,
month_name,
day_gr,
month_gr,
year_gr,
day_name_en,
month_name_en,
date_gr,
})
}
pub fn today() -> Self {
let today = Utc::today();
Self::chrno_to_hijri(today).unwrap()
}
fn chrno_to_hijri(date: Date<Utc>) -> Result<Self, String> {
let (year_gr, month_gr, day_gr): (usize, usize, usize) = (
date.format("%Y")
.to_string()
.parse()
.map_err(|_| "Error parsing date")?,
date.format("%m")
.to_string()
.parse()
.map_err(|_| "Error parsing date")?,
date.format("%d")
.to_string()
.parse()
.map_err(|_| "Error parsing date")?,
);
HijriDate::from_gr(year_gr, month_gr, day_gr)
}
pub fn format(&self, f: &str) -> String {
f.replace("%Y", &self.year.to_string())
.replace("%m", &self.month.to_string())
.replace("%d", &self.day.to_string())
.replace("%D", &self.day_name)
.replace("%M", &self.month_name)
.replace("%l", &self.month_len.to_string())
.replace("%gY", &self.year_gr.to_string())
.replace("%gm", &self.month_gr.to_string())
.replace("%gd", &self.day_gr.to_string())
.replace("%gD", &self.day_name_en)
.replace("%gM", &self.month_name_en)
}
}
fn valid_hijri_date(year: usize, month: usize, day: usize) -> Result<(), String> {
if month > 12 {
bail!("enter a valid month, Err m = {}", month);
}
if day > 30 {
bail!("enter a valid day, Err d = {}", day);
}
if year < 1357 {
bail!("minumum handled hijri year is 1357");
}
if year > 1499 {
bail!("maximum handled hijri year is 1499");
}
Ok(())
}
fn valid_greorian_date(year_gr: usize, month_gr: usize, day_gr: usize) -> Result<(), String> {
if month_gr > 12 {
bail!("enter a valid month, Err m = {}", month_gr);
}
if day_gr > 31 {
bail!("enter a valid day, Err d = {}", day_gr);
}
if year_gr < 1938 {
bail!(
"minumum handled gregorian year is 1938, input year: {}",
year_gr
);
}
if year_gr > 2076 {
bail!("maximum handled gregorian year is 2076");
}
Ok(())
}