thisweek_core/calendar/
persian.rs

1use crate::language::str_to_vec;
2use crate::weekdays::convert_weekday;
3use crate::weekdays::is_weekday_weekend_holiday;
4use crate::{language::Language, week_info::Date, week_info::DateView};
5use chrono::{DateTime, Datelike, Local};
6use serde::Serialize;
7
8use crate::calendar::{Calendar, CalendarSpecificDateView, CalendarView, CALENDAR_PERSIAN};
9
10use crate::calendar::calendar_names::*;
11use crate::month_names::*;
12use crate::season_names::*;
13use crate::weekday_names::*;
14
15#[derive(Debug, Serialize, Clone, PartialEq)]
16pub struct PersianCalendar;
17
18/* fn convert_weekday(weekday: i32) -> WeekDaysUnixOffset {
19    // Weekday since Shanbe - [0, 6](<0, 6>). 0 = Shanbeh, ..., 6 = Jomeh.
20    match weekday {
21        0 => WeekDaysUnixOffset::Sat,
22        1 => WeekDaysUnixOffset::Sun,
23        2 => WeekDaysUnixOffset::Mon,
24        3 => WeekDaysUnixOffset::Tue,
25        4 => WeekDaysUnixOffset::Wed,
26        5 => WeekDaysUnixOffset::Thu,
27        6 => WeekDaysUnixOffset::Fri,
28        _ => WeekDaysUnixOffset::Sat,
29    }
30} */
31
32impl CalendarSpecificDateView for PersianCalendar {
33    fn new_date(datetime: DateTime<Local>) -> Date {
34        let (gy, gm, gd) = (datetime.year(), datetime.month(), datetime.day());
35        let (year, month, day) = Self::gregorian_to_jalali(gy, gm, gd);
36        let weekday = convert_weekday(datetime.weekday()) as u32;
37        Date {
38            calendar: Calendar::Persian(PersianCalendar),
39            day,
40            month,
41            weekday,
42            year,
43        }
44    }
45
46    fn new_date_view(datetime: DateTime<Local>, lang: &Language) -> DateView {
47        let (gy, gm, gd) = (datetime.year(), datetime.month(), datetime.day());
48        let (year, month, day) = Self::gregorian_to_jalali(gy, gm, gd);
49        let day = lang.change_numbers_language(&day.to_string());
50        let month = (month - 1) as usize;
51        let month = match lang {
52            Language::Farsi => PERSIAN_MONTH_NAME_FA[month],
53            _ => PERSIAN_MONTH_NAME_EN[month],
54        };
55        let month = month.to_string();
56        let year = lang.change_numbers_language(&year.to_string());
57        let weekday = convert_weekday(datetime.weekday()) as usize;
58        let weekend_holiday = is_weekday_weekend_holiday(weekday.into());
59        let full_format = match lang {
60            Language::Farsi => format!(
61                "{}، {} {} {}",
62                WEEKDAY_NAME_FULL_FA[weekday], day, month, year
63            ),
64            _ => format!(
65                "{}, {} {} {}",
66                WEEKDAY_NAME_FULL_EN[weekday], day, month, year
67            ),
68        }
69        .to_string();
70        let weekday = match lang {
71            Language::Farsi => WEEKDAY_NAME_FULL_FA[weekday],
72            _ => WEEKDAY_NAME_HALF_CAP_EN[weekday],
73        }
74        .to_string();
75        DateView {
76            unix_day: 0,
77            day,
78            month,
79            weekday,
80            year,
81            full_format,
82            weekend_holiday,
83        }
84    }
85
86    fn get_calendar_view(lang: &Language) -> CalendarView {
87        let months_names: Vec<String> = match lang {
88            Language::Farsi => str_to_vec(&PERSIAN_MONTH_NAME_FA),
89            _ => str_to_vec(&PERSIAN_MONTH_NAME_EN),
90        };
91        let seasons_names: Vec<String> = match lang {
92            Language::Farsi => str_to_vec(&SEASON_NAME_FA),
93            _ => str_to_vec(&SEASON_NAME_EN),
94        };
95        let calendar_name: String = match lang {
96            Language::Farsi => PERSIAN_CALENDAR_NAME_FA.into(),
97            _ => PERSIAN_CALENDAR_NAME_EN.into(),
98        };
99        CalendarView {
100            calendar: CALENDAR_PERSIAN,
101            calendar_name,
102            language: lang.clone().into(),
103            direction: lang.default_direction(),
104            months_names,
105            seasons_names,
106        }
107    }
108}
109
110impl PersianCalendar {
111    /// note: the chrono-persian calendar (provided link below) has a bug that tries to convert
112    /// from persian dates (output of gregorian_to_jalali function) to chrono::NaiveDate, which
113    /// internally checks for gregorian dates and fails on some specific persian dates. (like 31th
114    /// of 2nd month is available in Ordibehesht, but not in February.)
115    /// source: https://jdf.scr.ir
116    /// https://docs.rs/chrono-persian/0.1.2/src/chrono_persian/lib.rs.html#1-140;
117    fn gregorian_to_jalali(gy: i32, gm: u32, gd: u32) -> (i32, u32, u32) {
118        const G_D_M: [i32; 12] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
119        let gy2 = if gm > 2 { gy + 1 } else { gy };
120
121        let mut days = 355666 + (365 * gy) + ((gy2 + 3) / 4) - ((gy2 + 99) / 100)
122            + ((gy2 + 399) / 400)
123            + gd as i32
124            + G_D_M[(gm - 1) as usize];
125
126        let mut jy = -1595 + (33 * (days / 12053));
127        days %= 12053;
128        jy += 4 * (days / 1461);
129        days %= 1461;
130
131        if days > 365 {
132            jy += (days - 1) / 365;
133            days = (days - 1) % 365;
134        }
135
136        let jm = if days < 186 {
137            1 + (days / 31)
138        } else {
139            7 + ((days - 186) / 30)
140        };
141
142        let jd = if days < 186 {
143            1 + (days % 31)
144        } else {
145            1 + ((days - 186) % 30)
146        };
147
148        (jy, jm as u32, jd as u32)
149    }
150}