use jiff::civil;
use crate::hijri::cal::{
gregorian_to_julian, hijri_to_julian, julian_to_gregorian, julian_to_hijri,
};
use crate::time;
const ARABIC_MONTHS: [&str; 12] = [
"محرم",
"صفر",
"ربيع الأول",
"ربيع الثاني",
"جمادى الأولى",
"جمادى الثانية",
"رجب",
"شعبان",
"رمضان",
"شوال",
"ذو القعدة",
"ذو الحجة",
];
const ENGLISH_MONTHS: [&str; 12] = [
"Moharram",
"Safar",
"Rabie-I",
"Rabie-II",
"Jumada-I",
"Jumada-II",
"Rajab",
"Shaban",
"Ramadan",
"Shawwal",
"Delqada",
"Delhijja",
];
#[derive(Debug, Clone)]
pub struct HijriDate {
pub year: i16,
pub month: i8,
pub month_arabic: String,
pub month_english: String,
pub day: i8,
}
impl HijriDate {
pub fn new(year: i16, month: i8, day: i8) -> Result<Self, crate::Error> {
if !(1..=12).contains(&month) {
return Err(crate::Error::InvalidMonth(month));
}
Ok(Self {
year,
month,
day,
month_arabic: Self::month_arabic(month),
month_english: Self::month_english(month),
})
}
pub fn to_julian(&self) -> i32 {
let date = civil::date(self.year, self.month, self.day);
hijri_to_julian(date)
}
pub fn to_gregorian(&self) -> civil::Date {
let julian = self.to_julian();
julian_to_gregorian(julian as f32)
}
pub fn next_date(self) -> Self {
let julian = self.to_julian();
Self::from_julian(julian + 1, 0)
}
pub fn today(correction_val: i32) -> Self {
Self::from_gregorian(time::today(), correction_val)
}
pub fn from_julian(julian_date: i32, correction_val: i32) -> Self {
let date = julian_to_hijri(julian_date, correction_val);
Self {
year: date.year(),
month: date.month(),
day: date.day(),
month_arabic: Self::month_arabic(date.month()),
month_english: Self::month_english(date.month()),
}
}
fn month_arabic(month: i8) -> String {
ARABIC_MONTHS[(month - 1) as usize].to_string()
}
fn month_english(month: i8) -> String {
ENGLISH_MONTHS[(month - 1) as usize].to_string()
}
pub fn from_gregorian(date: civil::Date, correction_val: i32) -> Self {
let date = julian_to_hijri(gregorian_to_julian(date) as i32, correction_val);
Self {
year: date.year(),
month: date.month(),
day: date.day(),
month_arabic: Self::month_arabic(date.month()),
month_english: Self::month_english(date.month()),
}
}
}
#[cfg(test)]
mod tests {
use jiff::civil;
use super::*;
fn hijri_date() -> Result<HijriDate, crate::Error> {
HijriDate::new(1442, 8, 25)
}
#[test]
fn hijri() -> Result<(), crate::Error> {
let hijri_date = hijri_date()?;
assert_eq!(hijri_date.day, 25);
assert_eq!(hijri_date.month, 8);
assert_eq!(hijri_date.year, 1442);
Ok(())
}
#[test]
fn tomorrow() -> Result<(), crate::Error> {
let hijri_date = hijri_date()?;
let tomorrow = hijri_date.next_date();
assert_eq!(tomorrow.day, 26);
assert_eq!(tomorrow.month, 8);
assert_eq!(tomorrow.year, 1442);
Ok(())
}
#[test]
fn to_gregorian() -> Result<(), crate::Error> {
let hijri_date = HijriDate::new(1442, 8, 25)?;
let gregorian = hijri_date.to_gregorian();
assert_eq!(gregorian, civil::date(2021, 4, 13));
Ok(())
}
#[test]
fn from_gregorian() -> Result<(), crate::Error> {
let hijri_from_gregorian = HijriDate::from_gregorian(civil::date(2021, 4, 9), 0);
assert_eq!(hijri_from_gregorian.day, 25); assert_eq!(hijri_from_gregorian.month, 8);
assert_eq!(hijri_from_gregorian.month_arabic, "شعبان".to_string());
assert_eq!(hijri_from_gregorian.month_english, "Shaban".to_string());
assert_eq!(hijri_from_gregorian.year, 1442);
Ok(())
}
#[test]
fn from_gregorian_1() -> Result<(), crate::Error> {
let hijri_from_gregorian = HijriDate::from_gregorian(civil::date(2020, 4, 18), 0);
assert_eq!(hijri_from_gregorian.day, 23); assert_eq!(hijri_from_gregorian.month, 8);
assert_eq!(hijri_from_gregorian.month_english, "Shaban".to_string());
assert_eq!(hijri_from_gregorian.year, 1441);
Ok(())
}
#[test]
fn from_julian() -> Result<(), crate::Error> {
let hijri_from_julian = HijriDate::from_julian(2459313, 0);
assert_eq!(hijri_from_julian.day, 25);
assert_eq!(hijri_from_julian.month, 8);
assert_eq!(hijri_from_julian.month_arabic, "شعبان".to_string());
assert_eq!(hijri_from_julian.month_english, "Shaban".to_string());
assert_eq!(hijri_from_julian.year, 1442);
Ok(())
}
#[test]
fn min_month() -> Result<(), crate::Error> {
let hijri_date = HijriDate::new(1442, 1, 25)?;
assert_eq!(hijri_date.month_arabic, "محرم".to_string());
assert_eq!(hijri_date.month_english, "Moharram".to_string());
Ok(())
}
#[test]
fn max_month() -> Result<(), crate::Error> {
let hijri_date = HijriDate::new(1442, 12, 25)?;
assert_eq!(hijri_date.month_arabic, "ذو الحجة".to_string());
assert_eq!(hijri_date.month_english, "Delhijja".to_string());
Ok(())
}
#[test]
fn out_of_index_month() -> Result<(), crate::Error> {
let err = HijriDate::new(1442, 13, 25).unwrap_err().to_string();
assert_eq!(err, "No such month: 13");
let err = HijriDate::new(1442, 0, 25).unwrap_err().to_string();
assert_eq!(err, "No such month: 0");
Ok(())
}
#[test]
fn min_year() -> Result<(), crate::Error> {
let hijri_date = HijriDate::new(1, 1, 1)?;
assert_eq!(hijri_date.day, 1);
assert_eq!(hijri_date.month, 1);
assert_eq!(hijri_date.year, 1);
assert_eq!(hijri_date.month_arabic, "محرم".to_string());
assert_eq!(hijri_date.month_english, "Moharram".to_string());
Ok(())
}
#[test]
fn max_year() -> Result<(), crate::Error> {
let hijri_date = HijriDate::new(2000, 1, 1)?;
assert_eq!(hijri_date.day, 1);
assert_eq!(hijri_date.month, 1);
assert_eq!(hijri_date.year, 2000);
assert_eq!(hijri_date.month_arabic, "محرم".to_string());
assert_eq!(hijri_date.month_english, "Moharram".to_string());
Ok(())
}
}