thisweek_core/calendar/
persian.rs

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