mod umalqura;
use umalqura::*;
mod umalqura_array;
mod utils;
pub use chrono::Duration;
use chrono::{Date, NaiveDate, Utc};
use once_cell::sync::Lazy;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::ops::{Add, Sub};
static MONTH_DICT: Lazy<HashMap<usize, String>> = Lazy::new(|| {
[
(1, "محرم"),
(2, "صفر"),
(3, "ربيع الأول"),
(4, "ربيع الثاني"),
(5, "جمادي الأولى"),
(6, "جمادي الآخرة"),
(7, "رجب"),
(8, "شعبان"),
(9, "رمضان"),
(10, "شوال"),
(11, "ذو القعدة"),
(12, "ذو الحجة"),
]
.iter()
.map(|(i, n)| (*i, (*n).to_string()))
.collect()
});
static DAY_DICT: Lazy<HashMap<String, String>> = Lazy::new(|| {
[
("Saturday", "السبت"),
("Sunday", "الاحد"),
("Monday", "الاثنين"),
("Tuesday", "الثلاثاء"),
("Wednesday", "الاربعاء"),
("Thursday", "الخميس"),
("Friday", "الجمعة"),
]
.iter()
.map(|(ng, n)| ((*ng).to_string(), (*n).to_string()))
.collect()
});
#[derive(Debug, PartialEq)]
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 {
let hijri_date = HijriDate::chrno_to_hijri(self.date_gr + other);
assert!(hijri_date.is_ok());
hijri_date.unwrap()
}
}
impl Sub<Duration> for HijriDate {
type Output = HijriDate;
fn sub(self, other: Duration) -> HijriDate {
let hijri_date = HijriDate::chrno_to_hijri(self.date_gr - other);
assert!(hijri_date.is_ok());
hijri_date.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> {
let month_name = MONTH_DICT[&month].clone();
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].clone();
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> {
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].clone();
let day_name_en = date_gr.format("%A").to_string();
let day_name = DAY_DICT[&day_name_en].clone();
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();
let hijri_today = Self::chrno_to_hijri(today);
assert!(hijri_today.is_ok());
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(())
}